Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. This change

## [Unreleased]

### Added (v1.0.7-preview.2 post-release sync)
### Added (v1.0.7 sync)
- **Opaque tool-definition metadata** — port of upstream
[PR #1864](https://github.com/github/copilot-sdk/pull/1864). `define-tool` and
`define-tool-from-spec` accept an optional `:metadata` map and forward its
Expand All @@ -26,6 +26,14 @@ All notable changes to this project will be documented in this file. This change
`:promo` maps (`:ends-at`, with optional `:id`, `:discount-percent`, and
`:message`). No new public session event type was added.

### Added (post-v1.0.7 sync)
- **Per-custom-agent reasoning effort** — port of
[upstream PR #1981](https://github.com/github/copilot-sdk/pull/1981). Custom
agent maps accept optional `:agent-reasoning-effort` values `"low"`,
`"medium"`, `"high"`, or `"xhigh"`. Session create and resume send it as the
exact `reasoningEffort` wire field. When absent, the field is omitted and does
not inherit the session-level `:reasoning-effort`.

## [1.0.7-preview.2.1] - 2026-07-15
### Added
- **API-surface drift guard** ([#120](https://github.com/copilot-community-sdk/copilot-sdk-clojure/issues/120)) —
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Add to your `deps.edn`:

```clojure
;; From Maven Central
io.github.copilot-community-sdk/copilot-sdk-clojure {:mvn/version "1.0.7-preview.2.1"}
io.github.copilot-community-sdk/copilot-sdk-clojure {:mvn/version "1.0.7.0"}

;; Or git dependency
io.github.copilot-community-sdk/copilot-sdk-clojure {:git/url "https://github.com/copilot-community-sdk/copilot-sdk-clojure.git"
Expand Down
2 changes: 1 addition & 1 deletion build.clj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
(:import [java.io File]))

(def lib 'io.github.copilot-community-sdk/copilot-sdk-clojure)
(def version "1.0.7-preview.2.1")
(def version "1.0.7.0")
(def class-dir "target/classes")

(defn- try-sh
Expand Down
7 changes: 4 additions & 3 deletions doc/api/API.html
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ <h4><a href="#with-client-session" id="with-client-session"></a><code>with-clien
<tr><td> <code>:exp-assignments</code> </td><td> map </td><td> (Internal) Opaque experiment flight assignments. Keys are source-defined flight ids and are forwarded verbatim (string keys bypass kebab→camel conversion). Serialized as <code>expAssignments</code>. (upstream PR #1750) </td></tr>
<tr><td> <code>:mcp-servers</code> </td><td> map </td><td> MCP server configs keyed by server ID (see <a href="../mcp/overview.html">MCP docs</a>). Local (stdio) servers: <code>:mcp-command</code>, <code>:mcp-args</code>, <code>:mcp-tools</code>. Remote (HTTP/SSE) servers: <code>:mcp-server-type</code> (<code>:http</code>/<code>:sse</code>), <code>:mcp-url</code>, <code>:mcp-tools</code>. Spec aliases: <code>::mcp-stdio-server</code> = <code>::mcp-local-server</code>, <code>::mcp-http-server</code> = <code>::mcp-remote-server</code> </td></tr>
<tr><td> <code>:commands</code> </td><td> vector </td><td> Command definitions (slash commands). See <a href="#commands">Commands</a> </td></tr>
<tr><td> <code>:custom-agents</code> </td><td> vector </td><td> Custom agent configs. Each agent map: <code>:agent-name</code> (required), <code>:agent-prompt</code> (required), <code>:agent-display-name</code>, <code>:agent-description</code>, <code>:agent-tools</code>, <code>:agent-infer?</code>, <code>:agent-skills</code> (vector of strings), <code>:agent-model</code> (string, e.g. <code>"claude-haiku-4.5"</code>; when set the runtime tries this model for the agent, falling back to the parent session model — upstream PR #1309), <code>:mcp-servers</code> </td></tr>
<tr><td> <code>:custom-agents</code> </td><td> vector </td><td> Custom agent configs. Each agent map: <code>:agent-name</code> (required), <code>:agent-prompt</code> (required), <code>:agent-display-name</code>, <code>:agent-description</code>, <code>:agent-tools</code>, <code>:agent-infer?</code>, <code>:agent-skills</code> (vector of strings), <code>:agent-model</code> (string, e.g. <code>"claude-haiku-4.5"</code>; when set the runtime tries this model for the agent, falling back to the parent session model — upstream PR #1309), <code>:agent-reasoning-effort</code> (<code>"low"</code>, <code>"medium"</code>, <code>"high"</code>, or <code>"xhigh"</code>), <code>:mcp-servers</code>. <code>:agent-reasoning-effort</code> is serialized as <code>reasoningEffort</code> on both <code>session.create</code> and <code>session.resume</code>. When omitted, no per-agent override is sent; the backend chooses its default rather than inheriting the parent session’s effort. </td></tr>
<tr><td> <code>:default-agent</code> </td><td> map </td><td> Built-in/default agent config. Use <code>{:excluded-tools [...]}</code> to hide tools from the default agent while leaving them available to custom agents </td></tr>
<tr><td> <code>:on-permission-request</code> </td><td> fn </td><td> Permission handler function. <strong>Optional</strong> (upstream PR #1308). When omitted, permission requests are not auto-resolved; resolve them manually via <code>handle-pending-permission-request!</code>. Use <code>copilot/approve-all</code> to approve everything. </td></tr>
<tr><td> <code>:streaming?</code> </td><td> boolean </td><td> Enable streaming deltas </td></tr>
Expand Down Expand Up @@ -1813,10 +1813,11 @@ <h3><a href="#default-agent-tool-exclusions" id="default-agent-tool-exclusions">
:tools [repo-index-tool]
:custom-agents [{:agent-name "repo-auditor"
:agent-prompt "Audit repository changes."
:agent-tools ["repo_index_search"]}]
:agent-tools ["repo_index_search"]
:agent-reasoning-effort "high"}]
:default-agent {:excluded-tools ["repo_index_search"]}}))
</code></pre>
<p>The default agent cannot call <code>repo_index_search</code>. The <code>repo-auditor</code> custom agent can still call it because custom-agent tool assignment is independent of <code>:default-agent</code>.</p>
<p>The default agent cannot call <code>repo_index_search</code>. The <code>repo-auditor</code> custom agent can still call it because custom-agent tool assignment is independent of <code>:default-agent</code>. Its reasoning effort is overridden to <code>"high"</code>; omit <code>:agent-reasoning-effort</code> to let the backend choose its default rather than inherit the parent session’s effort.</p>
<h3><a href="#config-directory-and-skills" id="config-directory-and-skills"></a>Config Directory and Skills</h3>
<p><code>config-dir</code> overrides where the CLI reads its config and state (e.g., <code>~/.copilot</code>). It does not define custom agents. Custom agents are provided via <code>:custom-agents</code>.</p>
<pre><code class="language-clojure">(def session (copilot/create-session client
Expand Down
2 changes: 1 addition & 1 deletion doc/api/getting-started.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ <h2><a href="#prerequisites" id="prerequisites"></a>Prerequisites</h2>
</code></pre>
<h2><a href="#step-1-add-the-sdk-dependency" id="step-1-add-the-sdk-dependency"></a>Step 1: Add the SDK Dependency</h2>
<p>Add to your <code>deps.edn</code>:</p>
<pre><code class="language-clojure">{:deps {io.github.copilot-community-sdk/copilot-sdk-clojure {:mvn/version "1.0.7-preview.2.1"}}}
<pre><code class="language-clojure">{:deps {io.github.copilot-community-sdk/copilot-sdk-clojure {:mvn/version "1.0.7.0"}}}
</code></pre>
<p>Or use as a Git dependency:</p>
<pre><code class="language-clojure">{:deps {io.github.copilot-community-sdk/copilot-sdk-clojure
Expand Down
2 changes: 1 addition & 1 deletion doc/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ copilot --version
Add to your `deps.edn`:

```clojure
{:deps {io.github.copilot-community-sdk/copilot-sdk-clojure {:mvn/version "1.0.7-preview.2.1"}}}
{:deps {io.github.copilot-community-sdk/copilot-sdk-clojure {:mvn/version "1.0.7.0"}}}
```

Or use as a Git dependency:
Expand Down
7 changes: 4 additions & 3 deletions doc/reference/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ Create a client and session together, ensuring both are cleaned up on exit.
| `:exp-assignments` | map | (Internal) Opaque experiment flight assignments. Keys are source-defined flight ids and are forwarded verbatim (string keys bypass kebab→camel conversion). Serialized as `expAssignments`. (upstream PR #1750) |
| `:mcp-servers` | map | MCP server configs keyed by server ID (see [MCP docs](../mcp/overview.md)). Local (stdio) servers: `:mcp-command`, `:mcp-args`, `:mcp-tools`. Remote (HTTP/SSE) servers: `:mcp-server-type` (`:http`/`:sse`), `:mcp-url`, `:mcp-tools`. Spec aliases: `::mcp-stdio-server` = `::mcp-local-server`, `::mcp-http-server` = `::mcp-remote-server` |
| `:commands` | vector | Command definitions (slash commands). See [Commands](#commands) |
| `:custom-agents` | vector | Custom agent configs. Each agent map: `:agent-name` (required), `:agent-prompt` (required), `:agent-display-name`, `:agent-description`, `:agent-tools`, `:agent-infer?`, `:agent-skills` (vector of strings), `:agent-model` (string, e.g. `"claude-haiku-4.5"`; when set the runtime tries this model for the agent, falling back to the parent session model — upstream PR #1309), `:mcp-servers` |
| `:custom-agents` | vector | Custom agent configs. Each agent map: `:agent-name` (required), `:agent-prompt` (required), `:agent-display-name`, `:agent-description`, `:agent-tools`, `:agent-infer?`, `:agent-skills` (vector of strings), `:agent-model` (string, e.g. `"claude-haiku-4.5"`; when set the runtime tries this model for the agent, falling back to the parent session model — upstream PR #1309), `:agent-reasoning-effort` (`"low"`, `"medium"`, `"high"`, or `"xhigh"`), `:mcp-servers`. `:agent-reasoning-effort` is serialized as `reasoningEffort` on both `session.create` and `session.resume`. When omitted, no per-agent override is sent; the backend chooses its default rather than inheriting the parent session's effort. |
| `:default-agent` | map | Built-in/default agent config. Use `{:excluded-tools [...]}` to hide tools from the default agent while leaving them available to custom agents |
| `:on-permission-request` | fn | Permission handler function. **Optional** (upstream PR #1308). When omitted, permission requests are not auto-resolved; resolve them manually via `handle-pending-permission-request!`. Use `copilot/approve-all` to approve everything. |
| `:streaming?` | boolean | Enable streaming deltas |
Expand Down Expand Up @@ -2327,11 +2327,12 @@ Hide tools from the built-in/default agent while keeping them available to custo
:tools [repo-index-tool]
:custom-agents [{:agent-name "repo-auditor"
:agent-prompt "Audit repository changes."
:agent-tools ["repo_index_search"]}]
:agent-tools ["repo_index_search"]
:agent-reasoning-effort "high"}]
:default-agent {:excluded-tools ["repo_index_search"]}}))
```

The default agent cannot call `repo_index_search`. The `repo-auditor` custom agent can still call it because custom-agent tool assignment is independent of `:default-agent`.
The default agent cannot call `repo_index_search`. The `repo-auditor` custom agent can still call it because custom-agent tool assignment is independent of `:default-agent`. Its reasoning effort is overridden to `"high"`; omit `:agent-reasoning-effort` to let the backend choose its default rather than inherit the parent session's effort.

### Config Directory and Skills

Expand Down
1 change: 1 addition & 0 deletions resources/github/copilot_sdk/api_surface.edn
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
:github.copilot-sdk.specs/agent-model
:github.copilot-sdk.specs/agent-name
:github.copilot-sdk.specs/agent-prompt
:github.copilot-sdk.specs/agent-reasoning-effort
:github.copilot-sdk.specs/agent-skills
:github.copilot-sdk.specs/agent-tool-names
:github.copilot-sdk.specs/agent-tools
Expand Down
13 changes: 11 additions & 2 deletions src/github/copilot_sdk/client.clj
Original file line number Diff line number Diff line change
Expand Up @@ -1925,6 +1925,15 @@
(some? (:metadata t))
(assoc :metadata (:metadata t))))

(defn- custom-agent->wire
"Convert a custom agent to its wire shape for session.create / session.resume.
The idiomatic `:agent-reasoning-effort` key maps to the upstream SDK's
`reasoningEffort` field and is omitted when absent."
[agent]
(let [reasoning-effort (:agent-reasoning-effort agent)]
(cond-> (util/clj->wire (dissoc agent :agent-reasoning-effort))
(some? reasoning-effort) (assoc :reasoningEffort reasoning-effort))))
Comment thread
krukow marked this conversation as resolved.

(defn- config-defaults-for-mode
"Mode-specific session config defaults spread UNDER the caller's config
(caller's values always win). Mirrors upstream `configDefaultsForMode`
Expand Down Expand Up @@ -2067,7 +2076,7 @@
wire-mcp-servers (when-let [servers (:mcp-servers config)]
(util/mcp-servers->wire servers))
wire-custom-agents (when-let [agents (:custom-agents config)]
(mapv util/clj->wire agents))
(mapv custom-agent->wire agents))
wire-default-agent (when-let [agent (:default-agent config)]
(util/clj->wire agent))
wire-infinite-sessions (when-let [is (:infinite-sessions config)]
Expand Down Expand Up @@ -2252,7 +2261,7 @@
wire-mcp-servers (when-let [servers (:mcp-servers config)]
(util/mcp-servers->wire servers))
wire-custom-agents (when-let [agents (:custom-agents config)]
(mapv util/clj->wire agents))
(mapv custom-agent->wire agents))
wire-default-agent (when-let [agent (:default-agent config)]
(util/clj->wire agent))
wire-infinite-sessions (when-let [is (:infinite-sessions config)]
Expand Down
10 changes: 6 additions & 4 deletions src/github/copilot_sdk/specs.clj
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,7 @@
;; Custom agent configuration
;; -----------------------------------------------------------------------------

(s/def ::reasoning-effort #{"low" "medium" "high" "xhigh"})
(s/def ::agent-name ::non-blank-string)
(s/def ::agent-display-name string?)
(s/def ::agent-description string?)
Expand All @@ -419,11 +420,15 @@
;; runtime will attempt to use this model for the agent, falling back to the
;; parent session model if unavailable. Upstream PR #1309.
(s/def ::agent-model ::non-blank-string)
;; Per-agent override of the model's reasoning effort. The nested API key keeps
;; the established agent prefix; the wire serializer emits `reasoningEffort`.
(s/def ::agent-reasoning-effort ::reasoning-effort)

(s/def ::custom-agent
(s/keys :req-un [::agent-name ::agent-prompt]
:opt-un [::agent-display-name ::agent-description ::agent-tools
::mcp-servers ::agent-infer? ::agent-skills ::agent-model]))
::mcp-servers ::agent-infer? ::agent-skills ::agent-model
::agent-reasoning-effort]))

(s/def ::custom-agents (s/coll-of ::custom-agent))

Expand Down Expand Up @@ -630,9 +635,6 @@
(s/def ::memory
(s/keys :req-un [::enabled]))

;; Reasoning effort support (PR #302)
(s/def ::reasoning-effort #{"low" "medium" "high" "xhigh"})

;; Hooks and user input handlers (PR #269)
(s/def ::on-user-input-request fn?)
(s/def ::on-pre-tool-use fn?)
Expand Down
48 changes: 48 additions & 0 deletions test/github/copilot_sdk/integration_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -5232,6 +5232,54 @@
agent (first (get-in @seen ["session.create" :customAgents]))]
(is (not (contains? agent :agentModel))))))

;; --- Per-agent reasoning effort (upstream PR #1981) -------------------------

(deftest test-custom-agent-reasoning-effort-spec
(testing "::custom-agent spec validates optional :agent-reasoning-effort"
(is (s/valid? :github.copilot-sdk.specs/custom-agent
{:agent-name "test" :agent-prompt "You are helpful"
:agent-reasoning-effort "high"}))
(is (not (s/valid? :github.copilot-sdk.specs/custom-agent
{:agent-name "test" :agent-prompt "You are helpful"
:agent-reasoning-effort "max"})))))

(deftest test-custom-agent-reasoning-effort-on-wire
(testing "reasoning effort is sent on wire in session.create and session.resume"
(let [seen (atom {})
_ (mock/set-request-hook! *mock-server* (fn [method params]
(when (#{"session.create" "session.resume"} method)
(swap! seen assoc method params))))
_ (sdk/create-session *test-client*
{:on-permission-request sdk/approve-all
:custom-agents [{:agent-name "reasoning-agent"
:agent-prompt "Hello"
:agent-reasoning-effort "high"}]})
session-id (sdk/get-last-session-id *test-client*)
_ (sdk/resume-session *test-client* session-id
{:on-permission-request sdk/approve-all
:custom-agents [{:agent-name "reasoning-agent-2"
:agent-prompt "Hi"
:agent-reasoning-effort "low"}]})
create-agent (first (get-in @seen ["session.create" :customAgents]))
resume-agent (first (get-in @seen ["session.resume" :customAgents]))]
(is (= "high" (:reasoningEffort create-agent)))
(is (= "low" (:reasoningEffort resume-agent)))
(is (not (contains? create-agent :agentReasoningEffort)))
(is (not (contains? resume-agent :agentReasoningEffort))))))

(deftest test-custom-agent-reasoning-effort-omitted-when-not-set
(testing ":agent-reasoning-effort is omitted from wire when not provided"
(let [seen (atom {})
_ (mock/set-request-hook! *mock-server* (fn [method params]
(when (= "session.create" method)
(swap! seen assoc method params))))
_ (sdk/create-session *test-client*
{:on-permission-request sdk/approve-all
:custom-agents [{:agent-name "default-reasoning"
:agent-prompt "Hi"}]})
agent (first (get-in @seen ["session.create" :customAgents]))]
(is (not (contains? agent :reasoningEffort))))))

;; --- Default agent config (upstream PR #1098) --------------------------------

(deftest test-default-agent-excluded-tools-on-wire
Expand Down