Change Log
August 22, 2026 · View on GitHub
All notable changes to this project will be documented in this file. This change log follows the conventions of keepachangelog.com.
Unreleased
Changed (upstream parity)
- Recertified the stable public surface through upstream commit
eb7ba2411171f5e1fea9d38df01b436acdfb7271; no stable API, wire, generated source, or runtime schema changes were found. Aligned the experimental Agent Factory documentation with upstream's opt-in resource-limit guidance and removed guessed ceilings from introductory examples. (upstream PR #2353)
Added (post-v1.0.12-preview.0 sync)
- Added join-only
:requested-environment-variablesfor extension environment access.join-sessionreturns approved values under:granted-environment-variables, filtered to the exact requested names, because the JVM cannot portably mutate process environment variables. (upstream PR #2348) - Added stable runtime event and permission fields through upstream commit
ea41dadb199725766d5097f4592c17be3200035f, including model-change source, assistant usage metadata, per-agent shutdown metrics, interaction correlation, host routing/display metadata, extension environment access, memory scope, custom-tool permission skipping, and managed-approval event metadata. (upstream PR #2358, upstream PR #2363, upstream PR #2364)
Changed (post-v1.0.12-preview.0 sync)
- Updated the runtime schema pin from
1.0.80to1.0.81-6and recertified the complete stable Node SDK public surface through upstream commitcc0438d66e3e68c333537cb935d9425d4e4ed8d5. Experimental assisted-approval controls, ephemeral UI queries, factory lifecycle events, generated-only RPC declarations, and Node-specific package resolution remain intentionally excluded. - BREAKING (experimental): Updated the already-exposed
:copilot/session.permissions_changedevent to the current permission-mode transition data,{:mode <mode> :previous-mode <mode>}, with optional:assisted-approval-model. This replaces the removed aggregate allow-all booleans and"off"/"auto"/"on"mode fields. resume-session,<resume-session, andjoin-sessionnow apply supplied MCP server configuration directly insession.resume, without a redundantsession.mcp.reloadWithConfigfollow-up. (upstream PR #2367)
Fixed (post-v1.0.12-preview.0 sync)
- Create, resume, and join now send omitted command descriptions as
"", and elicitation results omit absent content rather than serializing JSONnull. (upstream PR #2358)
Fixed (examples)
- Scoped the manual pending-tool resume example to its declaration-only custom tool so host-configured MCP tools cannot exhaust the selected model's prompt budget before the example begins.
1.0.11.0 - 2026-08-16
Added (v1.0.11 sync)
- Added client-level
:builtin-plugin-directoriesfor absolute, trusted plugin directories bundled by a host. A non-empty vector is registered exactly once after protocol negotiation and before sessions can be created; registration failure force-stops startup. (upstream PR #2330) - Added
attributed-permission-resultandattributed-permission-result?, with closed idiomatic decision-context specs. Permission handlers can attach outcome, source, and surface context without changing the decision; plain decisions preserve legacy wire omission and attributed:no-resultstill suppresses the response RPC. (upstream PR #2294)
Changed (v1.0.11 sync)
- Synced the library version to
1.0.11.0and the runtime schema pin to1.0.80. The API and session-event schemas are unchanged at the new pin, so deterministic code generation produces no generated source delta.
Changed (performance)
- Avoided unnecessary camel/snake-case conversion work for already-normalized protocol keywords while preserving exact conversion semantics for camel, snake, namespaced, and punctuated keys.
- Buffered TCP protocol input before Content-Length framing so header-byte reads are satisfied from user space instead of repeatedly reaching the socket.
1.0.9.0 - 2026-08-13
Changed (agent guidance)
- Consolidated durable repository guidance for exact-pin stable upstream recertification, end-to-end wire/idiom contract proof, core.async and resource ownership, executable documentation, deterministic generated outputs, and matched-process performance evidence.
Added (stable 811adc sync)
- Added stable
:enable-file-change-tracking?session config parity through upstream commit811adc050a82d823cc6f6891576f30058554af8d. Create, resume, and join omit the wire key by default, preserve explicitfalseandtrueasenableFileChangeTracking, and reject explicitnilbefore RPC. The option is not mutable throughsession.options.update; experimental low-level rewind RPCs remain excluded. - Updated the generated event schema pin from
1.0.79-6to1.0.79-9and added the optional:cancelledfield tosubagent.completed. Cancellation still emits completion; the field distinguishes cancelled teardown from a subagent that ran to the end. - Added recursive JSON value/object specs and tightened
:tool-telemetryto the stable NodeToolTelemetrycontract: string bucket names map to JSON object maps whose nested values are JSON-compatible. Result helpers now enforce the contract under public API instrumentation. - Added machine-readable symbol-based parity evidence for every stable delta at the target commit, with experimental and internal exclusions classified and no unclassified stable rows.
Added (benchmarking)
- Added a matched Node/Clojure benchmark harness with fresh deterministic TCP JSON-RPC fixtures per independent process, public SDK clients, separate cold-start and warmed profiles, replicate-identified raw NDJSON observations, deferred fixture request traces, dirty-tree and loaded-dist provenance, paired process confirmatory inference with exact sign-flip tests and Holm correction, benchmark-scoped process-tree shutdown cleanup, optional explicit Copilot CLI provenance, assertion-free timed operation sampling with untimed pre/postflight validation, and a quick smoke profile. Results are rejected when fixture sequences, request counts, metadata, or corpus hashes differ.
- Revised the predeclared confirmatory benchmark methodology so the unchanged 15% warmup and 10% measured drift values are report-only diagnostics instead of process-selection gates. Added out-of-timing JVM/Node runtime checkpoints, exact diagnostic validation, and a SHA-256 evidence manifest. All 20 matched steady pairs are retained; only integrity failures abort a run.
- Recorded the complete 2026-08-13 methodology version 2 confirmatory result,
including exact provenance, pair-level effects, bootstrap intervals, exact
and Holm-adjusted p-values, retained drift-reference exceedances, and hashes
for the 480 MiB raw evidence set. The deterministic fixture supports
directional conclusions for three endpoints; ping throughput remains
no-supported-differenceand does not establish equivalence.
Added (upstream parity)
- Added the stable extension session config fields
:request-extensions?,:extension-sdk-path, and:extension-infoforsession.createandsession.resume.join-sessionaccepts:request-extensions?and:extension-infobut rejects the SDK path override, matching the official Node SDK. Explicit false is preserved for the opt-in, nil is rejected instead of serialized as JSON null, and extension identity is a closed two-string map that remains distinct from the richersession.extensions_loadedevent item. Ports upstream PR #1401 and PR #1494; the experimental extension launch-provider RPC remains excluded.
Changed (architecture)
- Accepted the
ADR: Defer a host-owned inference boundary,
intentionally excluding the upstream experimental
CopilotClientOptions.requestHandlerand five-methodllmInference.*lifecycle until concrete Clojure consumer demand or upstream stabilization/material redesign. Reconfirmed against upstream commit811adc050a82d823cc6f6891576f30058554af8d.
Fixed (optional session wire contracts)
session.createandsession.resumenow match the official Node SDK's optional-field contract: explicitfalseis preserved for:streaming?and:disable-resume?; create sendsrequestPermission: falsewhen:on-permission-requestis omitted; empty hooks sendhooks: false; and an empty:system-messageremains an empty wire map instead of gainingmode: "append"andcontent: null. An explicitly nil nested:system-message :contentis also omitted, while an empty string is preserved.- Custom-agent maps now serialize their disambiguating Clojure keys to the
official unprefixed wire fields (
:agent-nametoname,:agent-prompttoprompt,:agent-modeltomodel, and the corresponding display, description, tools, inference, and skills fields). :context-tiernow accepts only:defaultor:long-context; explicitnilis rejected by create/resume because the officialSessionConfigBase.contextTiercontract supports omission but not JSONnull. The existingswitch-model!/set-model!option remains nilable under instrumentation and omitscontextTierfrom the model-switch RPC.- BREAKING: model info now exposes the canonical
:model-capabilities {:supports ... :limits ...}shape instead of the stale:model-supports/:model-limitsresponse aliases. Outbound session and model-switch configs accept both shapes for compatibility, normalize both tosupports/limits, preservereasoningEffort, and emit the runtime's snake_caseadaptive_thinkingand token/vision limit leaves. Supplying both names for the same branch is rejected as ambiguous. :mcp-defer-toolsis now explicitly documented and classified as an experimental CLI-only Clojure escape hatch; it remains supported but is not presented as part of the official Node SDK's publicMCPServerConfig.- Added a machine-readable, table-driven create/resume contract matrix pinned
to upstream commit
3108e8ce26286043afa52f12781331460628baa0. It covers every accepted Clojure session config key through the public JSON-RPC path, including aliases, empty values, false values, local-only handlers, post-create option updates, unknown-key rejection, and explicit exclusions. AddressesPAR-016. - Updated the
manual_tool_resumeexample for the corrected create-timerequestPermissioncontract: it now registers a deferring:on-permission-requesthandler (returns{:kind :no-result}) so the permission prompt stays pending, and suspends withforce-stop!(matching the upstream sample'sforceStop()) so the runtime does not auto-resolve pending work on resume. Because the runtime emits each pending event before durably persisting it (with no observable persistence signal), the example waits one second after capturing each pending event so the write completes before the SIGKILL, then a secondpause!("Simulating time passing...") mirrors the upstream lifecycle. The flow runs once and fails loudly — no retry. Previously the example relied on the pre-fix behavior wherecreatealways sentrequestPermission: true.
Added (testing)
- The API-surface drift guard now covers the explicitly supported public
namespaces, their public vars and compatibility metadata, registered public
fdef forms, and curated idiom spec keys in one deterministic versioned
snapshot. Compiler-generated record constructors and private proxy class
interns are explicitly excluded, and snapshot generation preserves the
caller's spec-instrumentation state. The canonical
bb api-surface:updatetask regenerates the complete contract. - Added focused behavioral coverage for async Factory facade routing and error handling, lifecycle macro ownership order, transformed and sliding-buffered event subscriptions, pending interaction envelopes, UI elicitation, and workspace-path propagation across session producers.
- The monolithic integration test namespace is now decomposed into 17 focused namespaces with shared lifecycle and synchronization support, preserving the exact behavioral inventory while removing obsolete Factory symbol-existence assertions now covered by behavior tests.
- Replaced fixed timing sleeps in concurrency and event-routing tests with observable request, event, handler, and lifecycle synchronization. The two remaining bounded sleep backoffs poll real protocol state with explicit deadlines.
Fixed (helpers)
with-query-seqandquery-seq!now use an explicitly suppliedCopilotClientinstead of treating it as client options and silently starting a second helpers-managed client. The temporary session still disconnects on scope exit while the caller retains client lifecycle ownership.
Fixed (specs)
- Helper fdefs now validate each function's actual
:clientand:sessionoption contract:queryaccepts owned instances or config maps, seq helpers accept either client form with session config, andquery-chanremains config-map-only.
Fixed (documentation)
- Closed example tracking gaps: the portable runner now covers every
credential-free standalone entry point, including both streaming helper paths
and the closed-stdin user-input path. Streaming examples are bounded, propagate
session errors, and cancel their helper sources on timeout; manual exclusions
and preconditions are explicit. The upstream documentation matrix now covers
all pages at its pinned commit, records the intentional canvas-authoring
exclusion, classifies experimental citations and session limits, and links the
citation payload contract. Session docstrings and examples use the canonical
:config-directoryspelling. - Codox topic generation now assigns deterministic output identities from source
paths, preserves flat URLs for unambiguous basenames that do not collide with
Codox-owned pages, and rewrites relative topic links through the resulting
manifest. The Codox project index is no longer overwritten by
doc/index.md; the documentation and authentication index topics now render separately. Generated HTML validation covers missing topic targets, unresolved source-topic paths, and broken local topic anchors. A manifest-derived reserved-target check independently rejects any rendered topic-content link that resolves onto a Codox-owned output (the project index or a namespace page), so a producer that fails to rewrite a documentation link cannot leave it pointing at Codox chrome. Fixes issue 174.
Fixed (client lifecycle)
- Internally managed clients now store
:external-server? falseinstead ofnil, preserving process ownership semantics while satisfying the public client spec under instrumentation. External URI and child-process clients remaintrue. AddressesCOR-004.
Added (helper lifecycle)
- Scope-bound helper event sequences --
github.copilot-sdk.helpers/with-query-seqbinds the same bounded event sequence shape asquery-seq!for the dynamic extent of a body and disconnects infinally, so partial realization, early body return, body exceptions, and positive:max-eventsbody exits clean up the hidden session and event tap.query-seq!remains supported and is not deprecated in this change.
Fixed (helper lifecycle)
query-seq!now disconnects the created session when setup fails duringsend!, and:max-events 0is accepted by the public helper specs under instrumentation.query-channow treats closing its returned channel as explicit cancellation: a prioritized cancellation alt releases producers parked on a full bounded output, while one-shot asynchronous disconnect releases producers waiting for source events. Setup failures, normal terminal completion, repeated or concurrent close, and close/terminal races disconnect exactly once. Buffered values accepted before cancellation remain readable; an in-flight parked event may be dropped.
Changed (v1.0.79 sync)
send-and-wait!default idle-wait timeout is now 60000ms (was 300000ms), matching the upstream Node.js SDK (nodejs/src/session.ts,effectiveTimeout = timeout ?? 60_000). The same 60-second default now applies to the channel-based variantssend-async,send-async-with-id,<send!, and<send-and-wait!, and tohelpers/query(previously documented as 180000ms). All three previously disagreeing values (implementation 300000ms, facade docstring 180000ms,helpers/query180000ms) are reconciled to upstream's 60000ms. Passing an explicit:timeout-ms(or the 3-aritytimeout-msargument) is unaffected; the timeout controls how long to wait forsession.idleand does not abort in-flight agent work. AddressesPAR-003.send-and-wait!now honors:timeout-msin its 2-arityopts— the 2-arity form previously ignored the documented:timeout-mskey and always used the default. It now selects(:timeout-ms opts)when present, strips the key before the underlyingsession.send(so it is never forwarded on the wire), and — consistently with the async variants — treats aniltimeout (inoptsor as the positional argument) as "no deadline": the wait set contains only the event channel rather than calling(async/timeout nil). The positional argument's spec is correspondingly relaxed from a strict positive integer to the shared nilable::timeout-ms.
Added (v1.0.79 sync)
- Experimental
:enable-mcp-appssession opt-in -- port of upstream PR #1335.create-session,resume-session, andjoin-sessionaccept a boolean:enable-mcp-apps; only explicittrueemitsrequestMcpApps: true.falseand omission leave the wire key absent, and explicitnilis rejected by the closed public config specs. - Deterministic regression coverage for the
send-and-wait!outcome race (test/github/copilot_sdk/send_and_wait_test.clj), porting the upstreamnodejs/test/session-send-and-wait.test.tssuite: an earlysession.errorobserved whilesession.sendis in flight is retained and surfaced once send completes; an earlysession.idleis preserved but does not return before send completes; asession.sendRPC rejection wins over an earliersession.error; the first terminal outcome (idle or error) observed wins; and the zero-timeout default is asserted to be 60000ms. Tests gate the real piped-stream JSON-RPCsession.sendon a latch (no fixed sleeps) and were proven to fail against a tap-after-send mutant. AddressesPAR-006. test-send-and-wait-serializesis now deterministic -- the existing serialization test relied onThread/sleepand on the two send futures acquiring the send-lock in creation order (unspecified), so it could complete the wrong future and flake (~1/6 runs). It now gates the second caller's start on the first holding the lock via latches, proving the same serialization contract without sleeps or timing assumptions.
Fixed (cleanup diagnostics and failed-connect teardown)
- A rejected handshake no longer retains transport resources --
connect-with-streams!set:status :errorand rethrew without releasing anything it had built, so a rejected protocol version left the JSON-RPC connection, its reader/writer/dispatcher threads, and the reverse-request executor live. A retry then overwrote:connection-io, orphaning them with no remaining handle. It now runs the same teardown as a failedstart!, so a caller can retry without cleanup of its own. - One cleanup path --
start!,stop!,force-stop!, andconnect-with-streams!each hand-rolled the same router/connection/socket/ process sequence (or, in one case, none of it). They now sharerelease-transport!, parameterized by how the child process should be released.stop!'s RPC ordering (runtime.shutdownbefore transport teardown, graceful wait before signalling) is unchanged. - Expected and unexpected teardown failures are no longer conflated --
closing a socket or channel and joining a finished thread are idempotent by
contract, so a step that throws is either an interruption of the calling
thread or a genuine failure that left the resource live. Blanket
(catch Exception _)branches inprotocol/disconnect,process/destroy!, and the client teardown paths made the second case invisible. Unexpected failures now carry the operation and resource identity and reach the caller through the existing contract:stop!returns them in its error vector,force-stop!and a failedstart!log them. Expected outcomes stay quiet, interrupts are re-flagged rather than swallowed, and a failing step never short-circuits the steps that follow it. stderr-readerdistinguishes a closed stream from a real read failure -- anIOExceptionis now classified by process liveness: once the child has exited its stderr is expected to fail and is logged at debug, but a stderr failure while the child is still running hides its diagnostics and is warned with the Throwable and resource identity instead of discarded.- A child that outlives a forced kill is no longer reported as clean
teardown --
destroy!ignored the boolean from the wait that followsdestroyForcibly, anddestroy-forcibly!(theforce-stop!path) sent SIGKILL without waiting at all. In both cases a surviving process produced no failures and the client cleared:process, discarding the only handle to a live process. Both paths now share one confirm step: the child is signalled, then waited on for a bounded window, and a survivor is reported as an unexpected failure carrying:operation :wait-for-exit,:resource :process, and the:stage/:timeout-msthat elapsed. The client keeps the process handle whenever process teardown reported a failure.force-stop!still returnsnil, and the wait ends as soon as the child dies, so the bound is a worst case rather than a fixed delay.
Fixed (logging)
- The logging facade preserves Throwable-first semantics -- every macro
expanded to
(str ...)over all arguments, so a Throwable was flattened into the message text (class and message only, no stack trace) and never reached the backend as a throwable.debug/info/warn/errornow dispatch on the first argument and pass a leading Throwable throughclojure.tools.logging's portable(log level throwable message)arity, so backends render stack traces again. Every argument is evaluated exactly once and only after the level is known to be enabled, so a disabled level costs nothing however expensive its arguments are. Message rendering for calls without a leading Throwable is unchanged. - Removed the full
json/write-strdump of outgoing permission responses from the protocol writer's debug logging; the adjacent message-id line already identifies the message without serializing its payload.
Changed (reverse-RPC execution policy)
- Reverse request handlers run on a bounded worker pool -- server-to-client RPC
handlers (hooks,
sessionFs.*, factories, user input, provider tokens,systemMessage.transform, etc.) are arbitrary caller code that may block, and were previously invoked from inside a core.asyncgoblock. In core.async 1.8godispatch is backed by the same process-wide unbounded cached:ioexecutor asthread-call, so blocking handlers grew that shared pool without limit: a 32-request reproducer entered all 32 handlers concurrently onasync-io-*threads. Handlers now execute on a boundedThreadPoolExecutorowned by the connection, on threads namedjsonrpc-request-worker-*, and the same reproducer now caps at the configured bound. The reader thread only submits work, so it keeps routing responses and notifications while every worker is busy. - BREAKING (behavior): when more than
:request-handler-threads+:request-handler-queue-sizereverse requests are outstanding, the runtime now receives an explicit JSON-RPC-32000error withdata.coderequest_handler_saturated(includingmethod,maxConcurrency, andqueueSize) instead of the request being queued indefinitely. Nothing is silently dropped, and no timeout was added around handler execution -- a wedged handler occupies exactly one worker and is surfaced through the overload error and the new counters. disconnectnow shuts the handler pool down explicitly, interrupting blocked handlers, and warns if workers fail to terminate rather than leaking them.
Added (reverse-RPC execution policy)
:request-handler-threads(default16) and:request-handler-queue-size(default256) client options bound reverse-handler concurrency. Defaults are well above realistic reverse-RPC concurrency, so existing hosts are unaffected. These are the only new public keys; the worker/queue counters that back them (:dropped-notifications,:rejected-requests,:active-request-workers,:queued-requests,:request-workers-terminated?) are protocol-internal diagnostics, not public API.
Fixed (observability)
- Notification-queue overflow is no longer silent --
dispatch-message!dropped notifications with adebuglog and no counter. Drops are now logged atwarnwith the method and a running total, and counted inprotocol/connection-stats. A deterministic saturation test proves the branch is reachable and observable; it does not demonstrate that a terminal session event can be lost, so no event prioritization or transport change was made.
Fixed (lifecycle)
- Force-stop session teardown --
force-stop!now marks active sessions terminal, cancels local factory executions, closes event subscriptions and send locks, then drops client ownership without issuingsession.destroyorruntime.shutdownRPCs.
Fixed
- Restore the green formatting gate by correcting the formatter-prescribed indentation in
src/github/copilot_sdk/util.clj.
Changed (agent workflow)
- Worktree-safe upstream sync — the repo-local
update-upstreamskill now uses a tracked helper to resolve the siblinggithub/copilot-sdkcheckout through Git's common directory, and no longer switches tomain, creates a second branch, or depends on an ignored root-levelupdate.sh.
Added (v1.0.7 sync)
- Opaque tool-definition metadata — port of upstream
PR #1864.
define-toolanddefine-tool-from-specaccept an optional:metadatamap and forward its host-defined contents onsession.createandsession.resume. - Tool-search configuration and metadata — port of upstream
PR #1933. Session create,
resume, and join configs accept
:tool-searchwith optional:enabledand:defer-threshold. Atool_search_tooloverride receives the current tool metadata snapshot in:available-tools: each entry has:nameand:description, plus optional:namespaced-name,:mcp-server-name,:mcp-tool-name,:input-schema, and:defer-loading. Tool result maps may return:tool-references. - Schema regen to 1.0.71-2 — port of upstream schema updates
edbe6c66(1.0.71-0) and9744fd52(1.0.71-2). Bumped.copilot-schema-versionfrom1.0.70to1.0.71-2and regenerated wire specs and coercions. The curated public idiom adds optional canvas:iconpaths and model-billing:promomaps (:ends-at, with optional:id,:discount-percent, and:message). No new public session event type was added.
Added (v1.0.8 sync)
- Per-custom-agent reasoning effort — port of
upstream PR #1981. Custom
agent maps accept optional
:agent-reasoning-effortvalues"low","medium","high", or"xhigh". Session create and resume send it as the exactreasoningEffortwire field. When absent, the field is omitted and the runtime resolves the effective effort. - Strongly typed PascalCase
:exp-assignmentscontract — port of upstream PR #2033. Session create and resume configs validate the completeCopilotExpAssignmentResponseshape and forward its string-keyed PascalCase fields unchanged. - Schema regen from 1.0.71-2 through 1.0.73 — port of upstream package bumps
PR #2035 and
PR #2055. Regenerated wire
specs and coercions. The curated public event sets now include
:copilot/assistant.server_tool_progress,:copilot/session.managed_settings_enforced,:copilot/session.managed_settings_resolved, and:copilot/tool_search.activated;assistant.turn_retryandmodel.call_startremain generated internal-only.
Added (post-v1.0.8 sync)
:on-agent-stopsession hook — port of upstream PR #2054. Session:hooksaccept:on-agent-stopfor runtimeagentStopcallbacks, using the existing allow/block hook decision contract.
Added (v1.0.9 sync)
- Agent Factories authoring surface — port of
upstream PR #2114. New
github.copilot-sdk.factorynamespace:define-factorydeclares a:name/:description/:phasesfactory (optional:limitswith:max-concurrent-subagents,:max-total-subagents,:max-ai-credits,:timeout-seconds);join-sessionaccepts:factories; andrun!/resume!/get-run/wait-for-run!/list-runs/get-run-detail/get-run-progress/cancel!drive factory execution, with reverse-RPC handlers forcontext.agent/step/phase/log,parallel/pipeline, and journaling/replay/cancellation semantics. Permission requests gain a:factory:permission-kind, and the new:copilot/factory.run_updatedevent reports:run-id/:revisionprogress. Marked@experimentalupstream and marked experimental. - Disabled MCP servers — port of
upstream PR #2260. Session
create and resume configs accept
:disabled-mcp-servers, a vector of MCP server names to keep stopped for the session without mutating global settings — covers both plugin and built-in GitHub MCP servers. :github-mcp-tool-config— port of upstream PR #2112. Session create and resume configs accept:github-mcp-tool-configwith:enable-all-tools?,:additional-toolsets,:additional-tools,:enable-insiders-mode?, and:disable-form-deferral?, forwarded to the runtime's built-in GitHub MCP server asgithubMcpToolConfig.:disable-form-deferral?lets autonomous workflows opt out of MCP App form deferral so form-backed GitHub write tools execute directly.:additional-directoriesin session config — port of upstream PR #2180. Session create and resume configs accept:additional-directories, a vector of extra directories forwarded to the runtime alongside:working-directory.:enable-experimental-mode?session config — port of upstream PR #1600. Session create and resume configs accept:enable-experimental-mode?, sent as theisExperimentalModewire field. In"empty"mode it defaults tofalseunless explicitly set; in"copilot-cli"mode it is omitted from the wire when unset, letting the runtime decide."max"reasoning effort — port of upstream PR #2228.:reasoning-effortand per-custom-agent:agent-reasoning-effortnow accept"max"alongside"low","medium","high", and"xhigh".:on-user-prompt-transformedhook — port of upstream PR #2254. Session:hooksaccept:on-user-prompt-transformed, invoked after a submitted prompt has been transformed (e.g. by slash-command or skill expansion).- SQLite transaction support in the session filesystem provider — port of
upstream schema update
PR #2140 (
1.0.76-5). A session filesystem provider's:sqlitesub-map may include an optional 1-arg:transactionfunction;::sqlite-transaction-error-classcovers:busy-or-locked,:post-commit-ambiguous, and:fataloutcomes. :custom-agents-local-onlyforwarded on create/resume — port of upstream PR #1899.:custom-agents-local-onlyis now sent directly onsession.createandsession.resume(previously only reached the runtime via a latersession.options.updatecall, after agent discovery had already run), and defaults totruein"empty"mode.- Schema regen through 1.0.78 — port of upstream package bumps
PR #2140 (
1.0.76-5), PR #2183 (1.0.77), PR #2193 (1.0.78-2), and PR #2239 (1.0.78). Regenerated wire specs and coercions underlying the additions above.
Changed (v1.0.9 sync)
- BREAKING:
approve-allnow respects managed policy — when:enable-managed-settings?or:managed-settingsactivates enterprise policy,approve-allthrows instead of bypassing the policy. Requests with:managed-approval-required truereturn{:kind :no-result}so the host can collect an explicit user decision. This is the fail-closed behavior from upstream PR #2080. - Managed approval requirement exposed on permission requests — port of
upstream PR #2080.
Permission-request data now surfaces whether a request requires managed
(host-side) approval, so an
on-permission-requesthandler can distinguish managed-approval requests from ordinary caller-approved ones. - Open-ended model promotions —
:promomaps no longer require:ends-at, matching schema1.0.79-6, where a promotion without an expiry omits the field. - Version bump to
1.0.9.0— synced with upstreamcopilot-sdkrelease1.0.9viaclj -T:build sync-version, tracked by the v1.0.9 sync entries above.
Added (post-v1.0.9 sync)
history.clearContextand terminal tools — port of upstream PR #2129.define-toolanddefine-tool-from-specaccept an optional:is-terminal?flag: a successful call from a terminal tool ends the agent turn without feeding the result back to the model. Clearing session history now emits the:copilot/session.context_clearedevent, with required:messages-clearedand optional:initial-message.- Structured
:managed-settingssession config — port of upstream PR #2139. Session create, resume, and join configs accept:managed-settingswith a nested:permissionsmap (:disable-bypass-permissions-mode,:deny,:ask,:allow), forwarded to the runtime asmanagedSettings. This is distinct from the existing boolean:enable-managed-settings?flag and from the managed-approval-request exposure above. - Schema regen to 1.0.79-6 — port of upstream package bumps
PR #2282 (
1.0.79-5) and PR #2287 (1.0.79-6, matching.copilot-schema-version). Regenerated wire specs and coercions.session.task_completedata gains:outcome("completed","continue", or"blocked"),:objective-id,:reason, and:success; newsession.compaction_start(:model,:current-tokens,:token-limit,:trigger) andsession.compaction_complete(:success,:error,:status-code,:token-limit,:trigger) event data, with:triggervalues"threshold","context_limit_retry","manual","memory_pressure", and"model_switch"; and streaming/chunking metadata (:chunk-index,:chunk-count,:rte,:interaction-type) onassistant.message,assistant.reasoning, andassistant.usagedata.
Fixed (v1.0.8 sync)
- Completing
assistant.usageidiom metadata — the schema 1.0.73cacheExpiresAtfield is coerced tojava.time.Instant, andserviceRequestIdis validated and exposed. Field descriptions align with the later documentation in upstream PR #2074.
Fixed (post-v1.0.8 documentation)
- Runtime configuration semantics — corrected custom-agent reasoning-effort
inheritance and Azure BYOK API-version omission behavior. The runtime inherits
the parent effort only for the same model, and an omitted Azure API version
uses the GA versionless
v1route. Matches upstream PR #2064. - Subagent event fields — documented optional
:modelmetadata onsubagent.started, matching upstream PR #2072.
Fixed
- Hook invocation response envelopes —
hooks.invokenow returns the canonicalHookInvokeResponsewire shape, wrapping non-nil handler values underoutput, omittingoutputfor nil values, and preserving opaque MCP metadata within the nested hook output. Unknown session IDs now return an RPC error instead of a successful nil result. - Custom-agent MCP server IDs —
issue #158.
Nested
:mcp-serversnow use the same wire serializer as session-level MCP servers on both session create and resume, preserving keyword and string server IDs while converting each server config to the runtime wire shape. - Variant-local generated validation — the Clojure schema generator now
scopes same-named properties with different schemas to each data variant,
preserving
abort's closedreasonenum whileassistant.turn_retryaccepts open strings.
1.0.7-preview.2.1 - 2026-07-15
Added
- API-surface drift guard (#120) —
a new test (
github.copilot-sdk.api-surface-test) locks the public contract: every public var (with kind, plus:arglistswhen the var carries it — plaindefre-exports have none) in thegithub.copilot-sdkfacade namespace plus every curatedgithub.copilot-sdk.specsspec key are snapshotted toresources/github/copilot_sdk/api_surface.edn. The test fails on any undeclared drift (added/removed/changed vars or spec keys), so accidental breaking changes to the frozen GA surface are caught in CI. Intentional changes regenerate the snapshot with the newbb api-surface:updatetask, making the contract change reviewable as an EDN diff.
Added (documentation)
- Naming and shape differences vs the official SDK — new reference
section in
doc/reference/API.mddocumenting the handful of public Clojure names/return shapes that do not map 1:1 to the Node.js SDK::disable-resume?↔suppressResumeEvent,:max-input-tokens↔maxPromptTokens, andjoin-session's{:client :session}return vs the upstreamjoinSession()→CopilotSession. Resolves #124.
Changed
- Lifecycle handlers dispatched on a dedicated serial worker — lifecycle
handlers registered via
on-lifecycle-event(and the type-filtered variants) are now invoked on a per-client worker thread fed by a bounded dispatch channel, instead of inline inside the notification router'sgoloop. A slow or blocking lifecycle handler no longer stalls the router (which also delivers session events, request/response completions, and MCP callbacks). Events dispatched through the worker stay strictly in-order — one lifecycle event is fully dispatched to all matching handlers before the next begins — and handlers still see the handler map as of the moment each event is processed, so late registration keeps working. (Before the worker starts or after its channel closes during teardown, dispatch falls back to inline on the router loop.) The worker uses a sliding buffer (drops the oldest event under sustained overload) and is torn down cleanly on the per-client stop paths (stop!/force-stop!). Internal dispatch change only; the public API is unchanged. Resolves #126.
Fixed
default-join-session-permission-handler in the top-level github.copilot-sdk
namespace were bare def aliases, so editor tooltips and generated API docs showed
no call signature. They now carry an explicit :arglists '([request ctx]) so
editor tooltips and generated API docs surface the two-arg [request ctx] contract.
(The source vars name these params _request/_ctx since they ignore them; the
re-exports use the descriptive names for public documentation.)
Resolves #119.
(The result-* and convert-mcp-call-tool-result re-exports mentioned in that issue
are already defn wrappers and were unaffected.)
Fixed (documentation)
query-seq!leak foot-gun documented — thequery-seq!docstring and the API reference previously claimed "guaranteed cleanup ... even if the consumer stops early," which is false: the session and its event tap are released only when the lazy seq is realized to end of stream — a:copilot/session.idle/:copilot/session.errorevent, or the events channel closing (an end-of-stream condition detected when the next read yieldsnil, not an emitted event). Abandoning the seq early —(first ...)/(take 1 ...)when that first realized element is not itself a terminal event, or hitting a positive:max-eventsbound before a terminal event (:max-events 0disconnects immediately) — leaks the session. Rewrote the docstring and API docs to warn about this and steer callers towardquery-chan/queryfor early-stop use. (#127)
1.0.7-preview.2.0 - 2026-07-15
Added (v1.0.7-preview.2 sync)
Ported from upstream github/copilot-sdk v1.0.6-preview.1 → v1.0.7-preview.2
(@github/copilot 1.0.68 → 1.0.70). Schema bumped to 1.0.70. Preview sync: the
CLI is a prerelease. Of the new events below, only session.auto_mode_resolved is
marked @experimental upstream.
- Schema regen to 1.0.70 — port of upstream schema bumps
PR #1908 (1.0.69-1),
PR #1940 (1.0.69-3),
PR #1941 (1.0.69),
PR #1954 (1.0.70-0), and
PR #1962 (1.0.70). Bumped
.copilot-schema-version1.0.68→1.0.70and regenerated wire specs /.copilot-schema-version1.0.68→1.0.70and regenerated wire specs / coercions viabb codegen. Schema 1.0.70 also widenedtimeToFirstTokenMsfromintegertonumber; the idiom::time-to-first-token-ms/::ttft-msspecs now accept any non-negative number. - Five new session event types — surfaced in the curated public event sets
(
event-typesplus the relevant category set) and the idiom::event-typeenum::copilot/assistant.tool_call_delta(schema 1.0.69-3) — streaming tool-call argument input chunk; data{:tool-call-id "..." :input-delta "..."}with optional:tool-name,:tool-type.:copilot/mcp.tools.list_changed,:copilot/mcp.resources.list_changed,:copilot/mcp.prompts.list_changed(schema 1.0.70) — remote MCP server list-changed signals; data{:server-name "..."}.:copilot/session.auto_mode_resolved(schema 1.0.70-0,@experimental) — auto model-selection resolution; data{:chosen-model "..."}with optional:candidate-models,:category-scores,:confidence,:predicted-label,:reasoning-bucket.
session.permissions_changedallow-all mode fields — schema 1.0.70 added the optional experimental:allow-all-permission-mode/:previous-allow-all-permission-modefields (tri-state#{"off" "auto" "on"}) to the existing event; surfaced in the idiom::session.permissions_changed-dataspec and API reference.:enable-managed-settings?session config — port of upstream PR #1925. Boolean forwarded onsession.create/session.resume/join as wireenableManagedSettings, gated onsome?so an explicitfalseis sent verbatim and an absent key is omitted.:canvas-providersession config — port of upstream PR #1847. Map{:id "..." :name "..."}(:nameoptional) forwarded onsession.create/session.resume/join as wirecanvasProvider.- Telemetry forwarding on the
connecthandshake — port of upstream PR #1909. When:on-github-telemetryis registered, the SDK now also sendsenableGitHubTelemetryForwarding: trueon the connection-levelconnecthandshake, so the first session's un-replayablesession.starttelemetry is forwarded. The existing per-sessionsession.create/session.resumeflag is retained for older CLIs.
Added (v1.0.6-preview.1 sync)
Ported from upstream github/copilot-sdk (post-v1.0.5-preview.0). Schema bumped to
1.0.68. Preview sync: the CLI is a prerelease and these additions are
@experimental upstream.
- GitHub telemetry forwarding — port of upstream
PR #1835 (
@experimental/ Internal). New client option:on-github-telemetry, a one-arg callback. Registering it is the opt-in: the SDK addsenableGitHubTelemetryForwarding: trueto the wire params of bothsession.createandsession.resume(gated onsome?, so the flag is omitted entirely when no callback is set —falseis never sent). The runtime then emits connection-globalgitHubTelemetry.eventnotifications, each dispatched to the callback on the client's notification loop. A throwing callback is caught and logged (WARN) so it cannot corrupt JSON-RPC dispatch; no reply is sent. The notification is an idiom-shaped map{:session-id :restricted :event}; the event's:properties,:metrics, and:featuressub-maps are opaque source-defined data and pass through verbatim (keys not kebab-cased) via a protocol escape hatch. Added::github-telemetry-notification,::github-telemetry-event,::github-telemetry-client-info,::on-github-telemetry, and supporting specs;:on-github-telemetryadded toclient-options-keys. - Schema regen to 1.0.68 — port of upstream
PR #1886. Bumped
.copilot-schema-version1.0.67→1.0.68and regenerated wire specs / coercions viabb codegen. The new metadata RPC methods (getContextAttribution,getContextHeaviestMessages) and their types exist only in the generated protocol layer and are not surfaced on the public Node SDK, so per the API-parity rules no public Clojure API is added for them. No session-event changes.
Added (v1.0.5-preview.0 sync)
Ported from upstream github/copilot-sdk v1.0.4 → v1.0.5-preview.0 (@github/copilot
1.0.65 → 1.0.67). Schema bumped to 1.0.67. Preview sync: the CLI is a
prerelease and these additions are @experimental upstream.
- New
SessionConfigBasesession options — port of upstream PR #1865.create-sessionandresume-sessionaccept three new optional keys, all wire-forwarded on bothsession.createandsession.resume::excluded-builtin-agents— a vector of built-in agent names to hide from the session. Wire-encoded asexcludedBuiltinAgents. Added::excluded-builtin-agentsspec.:enable-citations(@experimental) — a boolean opting into native model citations. Gated onsome?, so an explicitfalseis forwarded and an absent key is omitted. Wire-encoded asenableCitations. Added::enable-citationsspec.:session-limits(@experimental) — a map{:max-ai-credits <number>}capping session AI-credit spend. Wire-encoded assessionLimits.maxAiCredits. Added::session-limitsand::max-ai-creditsspecs.
:on-mcp-auth-requesthandler — port of upstream PR #1669 (@experimental).create-sessionandresume-sessionaccept an optional:on-mcp-auth-requesthandler for interactive MCP OAuth. When provided, the SDK registers interest in themcp.oauth_requiredevent (before thesession.createmode-options patch on create, and before thesession.resumeRPC on resume — matching upstreamclient.ts), so the runtime delegates browser-based OAuth to the handler instead of silently using a cached token. The 2-arg handler(fn [request ctx])receives anMcpAuthRequestmap ({:request-id :server-name :server-url :reason :www-authenticate-params :resource-metadata :static-client-config}) and{:session-id ...}, and may return acore.asyncchannel. A result with:access-token(plus optional:token-type,:expires-in) answers with a token;nil,{:kind :cancelled}, or a thrown exception cancels. A failedregisterInterestrejects session creation/resume rather than being silently swallowed. Added::on-mcp-auth-requestspec.- BYOK
sessionIdin:bearer-token-providercallback — parity with upstream PR #1796 confirmed; no code change required. TheproviderToken.getTokencallback already receives{:provider-name ... :session-id ...}(since the v1.0.4 sync), matching upstream's rename ofgetBearerToken→bearerTokenProviderand itssessionIdargument. - New session-event types (schema 1.0.66 → 1.0.67) — schema-driven event types
added to the generated wire specs:
assistant.idle,session.session_limits_changed,session.usage_checkpoint,session_limits_exhausted.requested,session_limits_exhausted.completed,mcp.headers_refresh_required, andmcp.headers_refresh_completed. Publicevent-types/session-events/assistant-events/interaction-eventsentries added where upstream exposes a public SDK event:session_limits_changedandusage_checkpointjoinsession-events;session_limits_exhausted.requested/.completedjoininteraction-events(mirroringsampling.requested/sampling.completed).
Added (v1.0.4 sync)
Ported from upstream github/copilot-sdk v1.0.1 → v1.0.4 (@github/copilot
1.0.63 → 1.0.65). Schema bumped to 1.0.65.
:preamblesystem-message section +:preserveaction — port of upstream PR #1713. The customize-mode section catalog gains:preamble(the agent identity preamble, split out from the:identitygroup, which is now a section group). The static section actions gain:preserve, a no-op marker that opts an individually-addressable section out of a group-level:remove. Bothsystem-prompt-sectionsand itssystem-message-sectionsalias expose:preamble.:capisession option — port of upstream PR #1711.create-sessionandresume-sessionaccept an optional:capimap ({:enable-web-socket-responses boolean}), wire-encoded ascapi.enableWebSocketResponses. Added::capiand::enable-web-socket-responsesspecs.- Provider
:transport— port of upstream PR #1711. The singular BYOK:provideraccepts an optional:transport(:httpor:websockets), emitted as wiretransport. Registry named providers (in:providers) do not accept:transport, matching upstream'sNamedProviderConfig. Added::transportspec. - Multi-provider BYOK registry (
:providers/:models) — port of upstream PR #1718 (@experimental).create-sessionandresume-sessionaccept:providers(a vector of named providers) and:models(a model catalog referencing them by:name). A model selection id is"providerName/id". Combining the singular:providerwith either:providersor:modelsis rejected. Added::named-provider,::provider-model,::providers, and::modelsspecs. :bearer-token-providercallback — port of upstream PR #1748 (@experimental). Providers accept a:bearer-token-providerfunction for dynamic, per-request bearer tokens. The fn is stripped before serialization (sendinghasBearerTokenProvider true); the runtime requests a token via a new inboundproviderToken.getTokenRPC, dispatched to the registered callback. Non-string callback results are rejected and never logged. Added::bearer-token-providerspec.:exp-assignmentssession option — port of upstream PR #1750 (@internal). An opaque experiment-flight assignment map forwarded verbatim (string keys bypass kebab→camel conversion) asexpAssignments. Added::exp-assignmentsspec.- New session-event types (schema 1.0.65) — schema-driven event types added to
the generated wire specs: citations, binary assets (PersistedBinary /
OmittedBinary / BinaryAssetReference), additional canvas events
(CanvasUnavailable / Recorded / Removed), ScheduleRearmed, and MCP OAuth events.
Public
event-types/session-eventsentries added where upstream exposes a public SDK event. open-canvasesvalidation relaxed — parity with upstreamsession.ts: an open-canvas instance now requires only the three id fields:instance-id+:extension-id+:canvas-id(dropped the:reopen/:availabilityrequirements).
Changed (v1.0.4 sync)
redact-secretsmasks the:providersregistry — the validation-error redactor now masks:api-key,:bearer-token, and:headerson every entry in the multi-provider:providersregistry, matching the existing singular:providermasking.
Added (post-v1.0.1 sync)
:memorysession configuration — port of upstream PR #1617.create-sessionandresume-sessionnow accept an optional:memorymap (shape{:enabled boolean}) that configures the agent's persistent memory. It is forwarded on bothsession.createandsession.resume, omitted entirely when the key is absent (never wirenull), and wire-encoded asmemory. In:mode :emptyit is defaulted to{:enabled false}(caller can override). Added a::memoryspec (reusing the existing::enabled).:otlp-protocoltelemetry option — port of upstream PR #1648. The client:telemetrymap accepts an optional:otlp-protocol("http/json"or"http/protobuf"), mapped to theOTEL_EXPORTER_OTLP_PROTOCOLenvironment variable on the spawned CLI. Added::otlp-protocolto the::telemetryspec.- Graceful
runtime.shutdowninstop!— port of upstream PR #1667 (restores the behavior of the reverted PR #1539). For SDK-spawned (non-external) processes,stop!now sends aruntime.shutdownRPC bounded by a 10-second timeout before closing the connection, falling back to process termination (SIGTERM → SIGKILL) on timeout or error.force-stop!is unchanged. :mcp-defer-toolsMCP option — new in upstream CLI schema 1.0.63. Stdio and HTTP/SSE MCP server configs accept an optional:mcp-defer-toolskeyword (:autoor:never) controlling tool-deferral. Wire-encoded asdeferToolswith the keyword value stringified. Added::mcp-defer-toolsspec to both MCP server specs.:copilot/session.todos_changedevent — new signal-only event in upstream CLI schema 1.0.63. Carries no payload; fires when the agent's todos / todo-deps table is written. Added to the publicevent-typesandsession-eventssets.- New optional event-data fields (upstream CLI schema 1.0.63):
:copilot/assistant.usage::content-filter-triggered(boolean),:finish-reason(string).:copilot/tool.execution_complete::structured-content(arbitrary structured tool result).:copilot/assistant.message::server-tools(replaces the removedanthropicAdvisorBlocks/anthropicAdvisorModelfields).:copilot/tool.execution_start::tool-description.
::model-billingtoken-prices spec — port of upstream PR #1633. The::model-billingspec gains an optional:token-pricesmap (:input-price,:output-price,:cache-price,:batch-size,:context-max,:long-context);list-modelsalready passes the whole billing map through, so this is documentation/validation only.:defertool-definition option — port of upstream PR #1632.define-toolanddefine-tool-from-specnow accept an optional:deferkeyword (:autoor:never) that controls whether a tool may be deferred (loaded lazily via tool search) rather than always pre-loaded. The keyword is converted to the wire string ("auto"/"never") and sent on the tool definition in bothsession.createandsession.resume; when omitted the field is not sent and the runtime applies its default ("auto"). Added::defervalue spec (#{:auto :never}) to the::toolspec.
Changed (post-v1.0.1 sync)
- Bumped pinned
@github/copilotschema 1.0.61 → 1.0.63 (PR #1686 and intermediate 1.0.62), regeneratinggenerated/event_specs.cljandgenerated/coerce.clj. Pulls in the newsession.todos_changedevent, optional usage / tool-execution / assistant-message fields, the MCPdeferToolsconfig, and theExtensionSourceenum extension (plugin/session).
Added (v1.0.1 sync)
open-canvasessnapshot — port of upstream PR #1604. A newgithub.copilot-sdk/open-canvases(alsogithub.copilot-sdk.session/open-canvases) returns the per-session vector of currently-open canvases. The snapshot is initialized from thesession.resumeresponse (session.createdoes NOT populate it, matching upstream Node.js client) and updated by the:copilot/session.canvas.openedand:copilot/session.canvas.closedevents. Missing/blank:instance-idpayloads log a warning and no-op.:copilot/session.canvas.closedevent type — newly added in upstream PR #1604. Fires when a canvas is closed; the SDK removes the matching entry from the open-canvases snapshot before publishing the event so observers see consistent state.- New optional event-data fields (upstream schema 1.0.57 → 1.0.61):
:copilot/session.resumeand:copilot/session.shutdown::events-file-size-bytes(nat-int?).:copilot/assistant.message::api-call-id.:copilot/hook.progress::temporary(boolean?).:copilot/session.schedule_created::at,:cron,:tz(with:interval-msrelaxed to optional — schedules can now use cron / fixed-time variants instead of intervals).
Changed (v1.0.1 sync)
- Bumped pinned
@github/copilotschema 1.0.57 → 1.0.61, regeneratinggenerated/event_specs.cljandgenerated/coerce.clj.
Added (v1.0.1 sync follow-up)
:open-canvasesaccepted inresume-session/join-sessionconfig (upstreamResumeSessionConfig.openCanvases). Lets callers seed the open-canvases snapshot when reconnecting to a session.
Changed (v1.0.1 sync follow-up)
- Strict validation on
session.canvas.openedupserts — payloads missing any of:instance-id,:extension-id,:canvas-id,:reopen(boolean), or:availability("ready"/"stale") are now no-ops with a warn log, matching upstreamisOpenCanvasInstance. :inputmap keys preserved verbatim onsession.canvas.openedevents, onopenCanvasesreturned bysession.resume, and when sent outbound via the:open-canvasesresume config. Caller-defined opaque keys (e.g.:user_id, nested or non-camelCase) are NOT re-cased by wire conversion.
Fixed (v1.0.1 sync follow-up)
:github.copilot-sdk.specs/atnow requirespos-int?(was laxnumber?).atrepresents an epoch-ms timestamp, so non-integer or non-positive values are invalid by construction.
1.0.0.0 - 2026-06-04
Highlights
First generally available (GA) release, at full API/wire/schema parity with
upstream github/copilot-sdk v1.0.0.
The public API is stable going forward. Notable changes since the last published
release (v1.0.0-beta.3.0):
- Stable, idiomatic public API — immutable data throughout,
clojure.specvalidation at the boundary, andcore.asyncevent streams; the public surface has been audited and frozen for GA. - Correctness & safety hardening — async lifecycle, thread-safety, resource cleanup, and input-handling fixes (see Security and Fixed below).
- Documentation — API reference, guides, and auth/MCP docs brought to parity with upstream.
- Examples — coverage meets or exceeds upstream;
./run-all-examples.shruns green end-to-end.
See the sections below for the complete change list since v1.0.0-beta.3.0.
Added (examples)
- New
manual_tool_resumeexample (examples/manual_tool_resume.clj), the SDK-driven analogue of the upstreammanual_tool_resumesample. It demonstrates a declaration-only tool (defined without a:handler, upstream PR #1308) whose pending permission request and pending tool call are resolved by hand across three separate client lifecycles viaresume-sessionwith:continue-pending-work? true— resolving the original request ids withhandle-pending-permission-request!andhandle-pending-tool-call!, subscribing to events before each trigger with a bounded wait. Each lifecycle suspends gracefully withdisconnect!(which persists the in-flight pending requests) rather than force-killing the client. run-all-examples.shnow runsask_user_failureandmanual_tool_resume(18 CLI-only example files; 19 runs total, sincehelpers-queryruns twice) and documents whybyok_provider,empty_mode, andmcp_local_serverare excluded (they require a provider API key ornpx/network setup).examples/README.mdupdated to match.
Changed (v1.0.0 GA sync)
- Synced version to upstream GA
v1.0.0(1.0.0-beta.12.0->1.0.0.0). Upstream'sv1.0.0release is functionally identical tov1.0.0-beta.12at the SDK level: no changes tonodejs/src/, the pinned@github/copilotCLI dependency stays^1.0.57(matching our schema1.0.57), and there are noschemas/or generated-code diffs. The remaining upstream commits in the range are Go/Java/CI/release plumbing and E2E test de-flaking — nothing to port. This SDK is therefore at full API/wire/schema parity with upstream GA.
Documentation
- Documented observability/telemetry. Added an Observability section to the
API reference covering the client
:telemetrymap (OpenTelemetry export::otlp-endpoint,:file-path,:exporter-type,:source-name,:capture-content?), the client:on-get-trace-contextdistributed-trace hook, and the session:enable-session-telemetry?flag. These options were already implemented but undocumented in the API reference. - Documented
:on-exit-plan-modeand:on-auto-mode-switchsession config handlers (upstream PR #1228) in the API reference, plus added option-table rows for:telemetryand:on-get-trace-contextto the client constructor. - Corrected stale docstrings. The top-level
create-session,resume-session, andjoin-sessiondocstrings claimed:on-permission-requestwas required; it has been optional since upstream PR #1308. Docstrings now match the implementation. - Added a Features navigation map to the documentation hub and a durable
doc/upstream-doc-gap-matrix.mdrecording per-topic coverage versus the upstream SDK docs. - Doc validation now covers
doc/-root pages.bb validate-docspreviously skipped markdown files directly underdoc/(e.g.index.md,getting-started.md); it now validates them, and link extraction ignores illustrative links inside inline-code spans.
Changed (release tooling)
- Install-doc coordinates stay in sync at release. The release SHA/version
refresh (
update-install-doc-shas,bb install-docs:sha, formerlyupdate-readme-sha/bb readme:sha) now updates every doc that embeds install coordinates (README.mdanddoc/getting-started.md) from a shared list, and the Release workflow stages both. Previously onlyREADME.mdwas refreshed, sodoc/getting-started.mddrifted (stale:mvn/versionand:git/sha).
Security
- Validation exceptions no longer leak secrets. Configuration validation
failures (
client,create-session,resume-session, and MCP-server checks) previously embedded the raw caller-supplied options map in the thrown exception'sex-data(and viaclojure.spec's::s/value), so a default uncaught-exception report could print:github-token,:tcp-connection-token, BYOK:providercredentials (:api-key,:bearer-token, custom:headers), and MCP:mcp-headers/:envvalues in cleartext. These values are now masked ("***") in both the exception message andex-databefore the exception is thrown.
Fixed (correctness)
start!is now safe under concurrent calls. The status guard previously did a non-atomic check-then-act (read:status, then a separateswap!to:connecting), so two threads callingstart!on the same client could both pass the guard and spawn two CLI processes. The transition is now an atomicswap-vals!compare-and-set: only the caller that observes a non-:connecting/:connectedstatus proceeds to spawn; the others no-op. The same atomic guard is applied to the test-onlyconnect-with-streams!.disconnect!is now idempotent under concurrent calls. It used a non-atomic check-then-act on the session's:destroyed?flag, so two threads disconnecting the same session could both send asession.destroyRPC. The teardown is now claimed with an atomicswap-vals!on:destroyed?; only the winning caller notifies the server and closes the event channel.remove-session!no longer closes the event channel before removing the session. It closed the channel first and dissoc'd the session afterward, leaving a window where the notification router could still resolve the session andoffer!an event to the just-closed channel (a spurious "buffer full" warning). The session is now removed from the registry first, then its channel is closed.query-channo longer blocks a go dispatch thread or leaks on send failure. It called the blockingdisconnect!directly inside its event go-loop (parking a shared core.async dispatch thread for the duration of connection teardown); teardown now runs onasync/threadand the loop parks on its result. If the initialsend!throws before the loop starts, the freshly created session is now disconnected (instead of leaking) and the output channel is closed before the error propagates.subscribe-events/events->channow actually isolate slow subscribers. Both used a fixed (blocking) channel buffer, but their docstrings promised that a full subscriber buffer drops events "for this subscriber only." With a fixed buffer,multblocks when any tap's buffer fills, stalling delivery to all subscribers until the slow one drains. Both wrappers now use asliding-buffer, so a slow subscriber drops its own oldest events without ever blocking the mult or other subscribers — matching the documented behavior. Docstrings and the API reference were corrected accordingly.- A failed
start!no longer leaks resources. If startup failed after the CLI process was spawned (e.g. the process died before announcing its port, or protocol verification failed), the error path only set the client status to:errorand left the spawned process, its stderr/exit-watcher threads, the socket, and the JSON-RPC connection running.start!now tears these down before re-throwing. - In-flight requests no longer hang on disconnect. A graceful
disconnect(e.g.stop!) previously left any in-flight JSON-RPC request's response channel unresolved, so a caller blocked on it would wait forever.disconnectnow drains all pending requests and delivers a{:error {:code -32000 :message "Connection closed"}}to each. The read loop's EOF/IO-error draining anddisconnectnow share a single atomicdrain-pending!, so a pending request is resolved exactly once even when both run concurrently. - Requests sent during/after disconnect fail fast.
send-requestnow registers its pending entry only while the connection is running, in one atomic step, and resolves the response channel with a connection-closed error if the connection is gone or the outgoing channel is already closed — previously such a request was silently dropped and the caller hung.
Removed
- Dropped the Babashka test-compatibility tasks (
test:bb,test:all) frombb.edn. Thetest:bbtask ran the full JVM test suite under the Babashka interpreter as a compatibility gate, but the suite depends on libraries Babashka does not bundle (e.g.clojure.data.json), so the task could not succeed and was not wired into CI. Removed the tasks and the related "Babashka compatibility" claims inJAVA_SDK_COMPARISON.md.bb test(the JVM test runner) and the Babashka-based build tooling underscript/are unaffected. - Dropped the unused
:force-stopping?client-state flag.force-stop!set it (andinitial-stateinitialized it), but nothing ever read it, so it was dead state.:stopping?(which the process-exit watcher does read) is unchanged.
Changed
- BREAKING: renamed
unsubscribe-eventstounsubscribe-events!. The function mutates — it untaps the channel from the session's event mult and closes it — so it now carries the!side-effect suffix per the SDK's naming convention. Update callers to the new name (no behavior change).
Added
<send-and-wait!— channel-based equivalent ofsend-and-wait!for use insidegoblocks. Returns a channel that delivers the final assistant message event (same shape assend-and-wait!'s successful return; content under[:data :content]), or closes empty if none was received. Like<send!, it does not surface:copilot/session.error/timeout as exceptions. Complements<send!(which yields just the content string).
Fixed (GA parity)
- BYOK
ProviderConfigwire keys —:provider {:provider-type ... :azure-options {:azure-api-version ...}}now serializes to the upstream wire shape (type/azure/apiVersion) instead of the camelCased SDK names (providerType/azureOptions/azureApiVersion). The runtime reads the provider config verbatim, so the previous encoding meant non-OpenAI BYOK (:azure,:anthropic) and the AzureapiVersionwere silently dropped — only:openaiworked, because it is the runtime default. Matches theProviderConfigshape innodejs/src/types.ts.
Added (GA parity)
:session-idle-timeout-secondsclient option — server-wide session idle timeout. When> 0, the SDK appends--session-idle-timeout <n>to the spawned CLI, matching the official SDK'ssessionIdleTimeoutSeconds. Default disabled (0).
Changed (GA parity)
list-tools,get-quota, andget-current-modelare now marked^:experimental. None of these correspond to a method on the official Copilot SDK'sCopilotClient/CopilotSession; they expose convenience wire RPCs (tools.list,account.getQuota,session.model.getCurrent). Marking them experimental keeps the stable GA surface aligned with the upstream SDK while leaving the helpers available. Non-breaking.- Public
event-typesset now matches the pinned schema exactly. The curated set previously omittedassistant.message_start,model.call_failure,session.extensions.attachments_pushed, and the two canvas events (session.canvas.opened,session.canvas.registry_changed). These are all delivered by the runtime and parsed by the wire layer, so consumers must be able to discover them; they are now included (and added to the idiom::event-typespec), withassistant.message_startalso categorized underassistant-events. The canvas authoring API remains out of scope for 1.0.0 — only the events are observable. A new codegen test guards against future drift between the publicevent-typesset and the generated schema set.
Added (v1.0.0-beta.12 sync)
:context-tierand:reasoning-summaryonswitch-model!/set-model!(upstream PR #1522).:context-tieraccepts:defaultor:long-context(wire-encoded ascontextTier→"default"/"long_context");:reasoning-summaryaccepts"none"/"concise"/"detailed"(wire-encoded asreasoningSummary). Mirrors the existing create/resume session-config options.:modelfield on:copilot/tool.execution_startevent data (upstream npm@github/copilot1.0.57). Added to::tool.execution_start-dataas an optional key, mirroring::tool.execution_complete-data.session.extensions.attachments_pushedevent +extension_contextattachment branch — regenerated wire specs from the bumped schema (upstream PR #1517).
Fixed (v1.0.0-beta.12 sync)
- Preserve opaque
extension_contextattachment payloads —extension_contextattachments (reachable onuser.messageevents viasession.getMessagesand onsession.extensions.attachments_pushedevents) carry an opaque:payloadwhose keys must not be kebab-cased bywire->clj. The protocol layer now restores the raw payload for these attachments on both the live notification and historical response paths.
Changed (v1.0.0-beta.12 sync)
- Pinned schema bumped
1.0.56-1→1.0.57; version synced to upstream releasev1.0.0-beta.12(1.0.0-beta.12.0).
Added (Client Mode Empty — upstream PR #1428)
:modeclient option —#{:copilot-cli :empty}, default:copilot-cli. Selects between historical CLI behavior and a hardened multitenancy posture for SaaS hosts that must isolate sessions from the local machine. Validated oncopilot/client.:emptymode constructor enforcement — In:emptymode the client requires at least one tenant-scoped storage root (:copilot-home,:session-fs,:cli-url, or:is-child-process?) so the CLI never falls back to the user's home directory, and forcesCOPILOT_DISABLE_KEYTAR=1on the spawned CLI so the headless server never touches the host keychain.- Required
:available-toolsin:emptymode —create-session/resume-session(sync and async) now reject empty-mode sessions that don't supply a tool allow-list. An empty vector[]is legitimate (it means "no tools") — the key just has to be present so silently-empty filters can't happen. - 9 mode-default session config fields — In
:emptymode the SDK spreads safe defaults UNDER the caller's session config (caller always wins)::enable-session-telemetry? false,:mcp-oauth-token-storage :in-memory,:skip-embedding-retrieval true,:embedding-cache-storage :in-memory,:enable-on-demand-instruction-discovery false,:enable-file-hooks false,:enable-host-git-operations false,:enable-session-store false,:enable-skills false. session.options.updateplumbing — After a successfulsession.create/session.resume, the SDK now issues a follow-upsession.options.updateRPC carrying the four overridable feature flags (and, in:emptymode,installedPlugins: [])::skip-custom-instructions(defaulttruein:empty):custom-agents-local-only(defaulttruein:empty):coauthor-enabled(defaultfalsein:empty):manage-schedule-enabled(defaultfalsein:empty) In:copilot-climode only flags the caller explicitly set are forwarded; if the patch ends up empty the RPC is skipped. On failure the SDK disconnects and removes the half-configured session before rethrowing. Wired into all four entry points (create-session,resume-session,<create-session,<resume-session).
- System message normalization in
:emptymode — Mirrors upstreamgetSystemMessageConfigForMode: if the caller did not provide a:system-message, the SDK emits{:mode "customize" :sections {:environment_context {:action "remove"}}}. If the caller provided:append, the SDK promotes it to:customize(preserving the content) and adds the env-context removal. If the caller used:customizeand supplied their own:environment-contextsection, the SDK leaves it untouched.:replacemode is passed through unchanged.:copilot-climode keeps the legacy behavior — no normalization. - Always-emit
:tool-filter-precedence "excluded"— Both modes now always sendtoolFilterPrecedence: "excluded"onsession.createandsession.resume. Makes the ordering between:available-toolsand:excluded-toolsdeterministic regardless of CLI version. github.copilot-sdk.tool-setnamespace — Source-qualified tool filter constructors (builtin,mcp,custom,builtins) plusisolated-builtins/isolated— the parity equivalents of upstreamBuiltInTools.Isolated. Bare"*"(no source) is rejected at the SDK boundary and at construction time.
Added (post-v1.0.0-beta.4 sync, round 6)
-
:agent-modeand:display-promptsend options —session/send!(and async/streaming variants) now accept::agent-mode— keyword in#{:interactive :plan :autopilot :shell}, wire-encoded asagentMode. Lets the model run with different agent behaviours per message. (upstream PR #1438):display-prompt— string shown in the timeline UI instead of the model-facing:prompt. Useful when the model prompt contains machinery or context that should not be surfaced to the end user. Wire-encoded asdisplayPrompt. (upstream PR #1470)
-
:mcp-oauth-token-storageconfig option — Controls where MCP OAuth tokens are persisted. Enum#{:persistent :in-memory}, defaulting to the server's default (persistent disk-backed). Set to:in-memoryin multi-tenant hosts that must not leak tokens to disk. Wire-encoded asmcpOAuthTokenStorage(the wire key is set directly to bypass the default kebab-camel converter which would mangleOAuth). Accepted on both create and resume. (upstream PR #1326) -
Multitenancy per-session granular flags — All optional, accepted on both
create-sessionandresume-session::embedding-cache-storage(#{:persistent :in-memory}, wireembeddingCacheStorage):skip-embedding-retrieval(boolean):organization-custom-instructions(string):enable-on-demand-instruction-discovery(boolean):enable-file-hooks(boolean):enable-host-git-operations(boolean):enable-session-store(boolean):enable-skills(boolean)
Lets multi-tenant hosts opt individual sessions out of disk-backed caches, host git, hooks, sessions store, and skills discovery without switching to a separate client. (upstream PR #1474)
-
:plugin-directoriesconfig option —[string]of extra plugin directories. Wire-encoded aspluginDirectories. Loaded even when:enable-config-discoveryisfalse, so multi-tenant hosts can inject a curated plugin set without enabling general discovery. Accepted on both create and resume. (upstream PR #1482) -
Cloud sessions can defer
sessionIdto the server — When:cloudis set and:session-idis omitted fromcreate-session/<create-session, the SDK now omitssessionIdfrom thesession.createrequest and captures the server-assigned id from the response. A new inline-response callback (registered withprotocol/send-request's{:on-response-inline}option) runs synchronously in the JSON-RPC reader thread before the next inbound message is processed, so any session-scoped notification arriving immediately after the response is correctly routed to the newly-registered session. Callers may still supply:session-idexplicitly; if both caller and server provide an id, they must agree. (upstream PR #1479) -
Config parity additions — Existed in upstream
SessionConfigBaseprior to this window; added to close pre-existing parity gaps. All optional, accepted on both create and resume::reasoning-summary(#{"none" "concise" "detailed"}, wirereasoningSummary) — controls inclusion/granularity of reasoning summaries in assistant turns. String-valued for consistency with the existing:reasoning-effortoption.:context-tier(#{:default :long-context}, wirecontextTieras"default"/"long_context") — selects long-context model variants.:large-outputonresume-session— already accepted on create; now also forwarded on resume (wirelargeOutput).
-
:config-directoryand:output-directoryoption aliases — Non-breaking aliases for:config-dirand:output-dir(:output-directoryis inside the:large-outputmap). Wire keys stayconfigDir/outputDir. When both old and new keys are supplied, the new key wins. (upstream PR #1482 source-side rename) -
New event types — Added to the public
event-typesset and picked up automatically by the generated wire spec::copilot/hook.progress— ephemeral progress updates from long-running hooks. Curated::hook.progress-dataspec exposes:message(non-blank string);:session-id/:timestamplive on the envelope.:copilot/session.autopilot_objective_changed— autopilot objective lifecycle events. Generated wire spec carries:operation(required, one of"create"/"update"/"delete"), with optional:id(integer) and:status. The:statusenum is widened to include"active","paused","cap_reached","completed".:copilot/session.permissions_changed— emitted when per-session permission flags change. Curated::session.permissions_changed-dataspec requires:allow-all-permissionsand:previous-allow-all-permissions(both booleans).
-
Schema bump —
.copilot-schema-versionadvanced from1.0.55-1to1.0.56-1, covering upstream tagsv1.0.0-beta.9andv1.0.0-beta.10. Schema regen picks up several new optional event fields (working-directoryonexternal_tool.requested-data,context-tieronsession.resume-data, autopilot status values) and the three new event types above.
Deferred (round 6)
- Removal of the legacy
:config-dir/:output-diroption keys (upstream PR #1482 follow-up) — The new:config-directory/:output-directoryaliases ship in this release (see Added). The breaking removal of the older spellings is tracked alongside the other rename PRs (#1357 etc.) for a coordinated rename release. - Canvas runtime, MCP Apps
enableMcpApps— Continue to defer as experimental coupled surfaces.
Added (post-v1.0.0-beta.4 sync, round 5)
:on-post-tool-use-failurehook — New lifecycle hook in the:hooksmap. Fires after a tool execution whose result was"failure";:on-post-tool-useonly fires for successful results, so register this handler to observe or react to failed tool outcomes. Note:"rejected","denied", and"timeout"results do not currently trigger this hook — only"failure"does. Handler input has:tool-name,:tool-args,:error(string), plus the base hook fields (:session-id,:timestamp,:cwd). Optional return value:{:additional-context "..."}is appended as hidden guidance to the model alongside the failed tool result. (upstream PR #1421):runtime-instructionssystem message section — New section recognized by the SDK's:system-message:customizemode. Wire-encoded as"runtime_instructions"and accepted by::specs/system-prompt-section. Upstream PR #1377 also renamedSystemPromptSection→SystemMessageSectionin TypeScript; for source compatibility the Clojure side keepsspecs/system-prompt-sectionsas the canonical name and exposesspecs/system-message-sections(and::specs/system-message-section) as aliases pointing at the same data. (upstream PR #1377):copilot/mcp_app.tool_call_completeevent — New session event emitted when a tool call from an MCP App completes (upstream schema 1.0.52-4, SEP-1865). Added to the publicevent-typesset. The:argumentsand:resultfields are preserved opaquely byprotocol/preserve-event-opaque-fields(they survivenormalize-incomingwithout kebab-case rewriting so source-defined keys round-trip verbatim).- Additional event-data fields (passive, via schema regen) — All optional;
generated
:opt-unspecs pick them up automatically::service-request-idon:error,:assistant.message,:assistant.usage,:model.call_failure,:session.compaction_completeevent data (Copilot CAPI service-request-id for correlation with CAPI logs).:context-tier("long_context" | "default" | nil) on:session.model_changedata.:transport,:plugin-name,:plugin-versionon the loaded MCP server spec inside:session.mcp_servers_loadeddata.:erroron:session.mcp_server_status_changeddata.:sourceand:trigger("user-invoked" | "agent-invoked" | "context-load") on:skill.invokeddata.:tool-descriptionand:ui-resourceon:tool.execution_completedata.
- Schema bump —
.copilot-schema-versionadvanced from1.0.52-1to1.0.55-1. Picked up the 1.0.52-4 pre-release (upstream PR #1393), the 1.0.52 stable release (upstream PR #1405), the 1.0.53-2 pre-release (upstream PR #1408), and the 1.0.53 / 1.0.54 / 1.0.55-0 / 1.0.55-1 schema bumps (upstream PRs #1410, #1411, #1412, #1432). Schema regen surfaces new wire-only canvas event types (session.canvas.opened,session.canvas.registry_changed) and their field set in the generatedevent-specsnamespace. The canvas runtime (extension manifests,requestCanvasRenderer,openCanvases, etc. — upstream PRs #1401, #1413) is not yet exposed on the public Clojure API, including the curatedevent-typesset. Canvas runtime support (including opaque-field preservation fordata.inputand nestedinputSchemapayloads on canvas events) will land in a dedicated future sync round.
Changed (post-v1.0.0-beta.4 sync, round 5)
- BREAKING: Minimum supported protocol version raised from 2 to 3. The
SDK will now reject CLI servers that report protocol version 2. The
back-compat shims that adapted v2
tool.call/permission.requestJSON-RPC requests into v3 broadcast-event flows have been removed fromset-request-handler!and fromprotocol/normalize-incoming. Clients must use a Copilot CLI that supports protocol v3 (CLI 1.0.46 or later). (upstream PR #1378)
Removed (post-v1.0.0-beta.4 sync, round 5)
- v2 protocol RPC dispatcher cases —
tool.callandpermission.requestrequest handlers (and their associated teststest-tool-call-response-shape,test-tool-handler-runs-on-blocking-thread,test-permission-denied-with-deny-handler,test-permission-approved-with-handler,test-permission-unknown-session-response-shape,test-permission-custom-handler,test-permission-no-result-v2). v3 broadcast handlershandle-v3-tool-requested!/handle-v3-permission-requested!cover the same behaviour. (upstream PR #1378)
Added (post-v1.0.0-beta.4 sync, round 4)
-
:on-pre-mcp-tool-callhook — New lifecycle hook in the:hooksmap that fires before an MCP tool call is dispatched to its server (upstream PR #1366, wirehookType: "preMcpToolCall"). The handler receives an input map with kebab-cased base fields (:server-name,:tool-name,:tool-call-id,:session-id,:timestamp) plus two opaque, source-defined fields that are preserved verbatim through wire normalization::arguments(the MCP tool arguments) and:_meta(the MCP request metadata; the leading underscore is preserved — not collapsed by kebab-case conversion). The handler return value supports a tri-state:meta-to-usefield controlling the outgoing MCP request_meta:- absent (
nil/{}): preserve the existing_meta {:meta-to-use {...}}: replace_metawith the given map (inner keys are preserved opaquely — not camelCased){:meta-to-use nil}: serialize as JSONnull, removing_meta.
Note: PR #1366 also renamed hook-input
cwdtoworkingDirectoryin the Node.js public API. Existing Clojure hook handlers (:on-pre-tool-use,:on-post-tool-use, etc.) currently receive the field as:cwd. For internal consistency,:on-pre-mcp-tool-callalso exposes:cwd; the coordinated:cwd→:working-directoryrename across all hooks is tracked with the deferred PR #1357 work below.(upstream PR #1366)
- absent (
-
Schema bump —
.copilot-schema-versionadvanced from1.0.51to1.0.52-1. Additive changes only:session.compaction_complete-datagains optional:custom-instructions.tool.execution_complete-datagains optional:sandboxed.session.shutdown-datarelaxes:total-premium-requestsfrom required to optional. The hand-curated idiom spec::specs/session.shutdown-datamatches.
Most of the remaining schema diff is
x-opaque-json/ description annotations that do not affect the generated specs.
Tracked-but-not-ported (post-v1.0.0-beta.4 sync, round 4)
- PR #1357 (TypeScript SDK API review fixes) — Pure naming/API-shape
changes in the JS public API:
onExitPlanMode → onExitPlanModeRequest,onAutoModeSwitch → onAutoModeSwitchRequest,ResumeSessionConfig.disableResume → suppressResumeEvent,cwd → workingDirectoryacross config types,getMessages → getEvents,InputOptions → UiInputOptions,maxInputTokens → maxPromptTokens(drops wire shim), and removal ofautoStart/autoRestartfromCopilotClientOptions. The Clojure SDK already uses idiomatic kebab-case names that are independent of upstream's JS naming, and the:max-input-tokens→maxPromptTokenswire shim was explicit in Clojure from the start (mirrors upstream's pre-#1357 behavior), so dropping the JS shim has no effect on Clojure. The remaining renames (:cwd→:working-directoryon hook inputs,:disable-resume?→:suppress-resume-event?, etc.) are breaking and are deferred to a separate PR that can introduce deprecation aliases. - PRs #1370 / #1371 (1.0.52-x schema bumps) — Picked up by the schema regen in this PR.
Changed (post-v1.0.0-beta.4 sync, round 3)
ping:timestampfield type changed in CLI 1.0.51 — Upstream PR #1340 changed thepingRPC resulttimestampfield from epoch-millis number to an ISO 8601 date-time string (e.g."2026-05-21T08:00:00.000Z"). The SDK forwards the server value verbatim, so callers ofsdk/pingwill see a string:timestampagainst CLI ≥ 1.0.51 and a numeric epoch-millis value against older CLIs. The::specs/timestampspec accepts both shapes ((s/or :iso-string string? :epoch-ms nat-int?)) so spec instrumentation passes against either CLI version. The mock test server was updated to emit the ISO string form, and the ping docstring documents both shapes. (upstream PR #1340)
Added (post-v1.0.0-beta.4 sync, round 3)
:mcp-argsis now optional on MCP stdio server configs — Following upstream PR #1347 (MCPStdioServerConfig.argsmade optional across all SDKs), the::mcp-local-server/::mcp-stdio-serverspec moves::mcp-argsfrom:req-unto:opt-un. Stdio MCP servers declared with just{:mcp-command "..." :mcp-tools [...]}(no:mcp-args) now validate and forward correctly. (upstream PR #1347):time-to-first-token-msonassistant.usageevent data — The CLI 1.0.51 wire schema renamed the assistant-usage TTFT property fromttftMstotimeToFirstTokenMs, which surfaces as the kebab-case key:time-to-first-token-msafter wire normalization. The::assistant.usage-dataspec lists both keys in:opt-unso events from older and newer CLIs both validate; the new key is the canonical name going forward. The generated wire spec (generated/event_specs.clj) only declares the new field, matching the current schema. (upstream CLI 1.0.51 schema)- Schema bump —
.copilot-schema-versionadvanced from1.0.49to1.0.51. Generated wire specs and coercions regenerated. Additive changes only beyond the rename above: bounded-integer fields previously typed asnumber?now generate asinteger?(upstream PR #1329, "Use 32-bit types for bounded schema integers").
Tracked-but-not-ported (post-v1.0.0-beta.4 sync, round 3)
- PR #1316 (re-export generated session-event types from
index.ts) — Node.js packaging concern only; the Clojure SDK already exposes generated event specs via thegithub.copilot-sdk.generated.event-specsnamespace. - PR #1327
ToolBinaryResult.typetightened to"image" | "resource"— Our::binary-results-for-llmspec is intentionally permissive ((coll-of map?)). Tightening would require adding a dedicated::tool-binary-resultspec with string-valued:type; deferred since the runtime helpers already emit valid values. - Upstream test stabilization (#1346, #1317, #1314), other-language / documentation / codegen-only PRs (#1336, #1291, #1331, #1338, #1339, #1304, #1289, #1313) — no Clojure SDK action required.
Added (post-v1.0.0-beta.4 sync, round 2)
- SessionFs SQLite support —
sessionFs.sqliteQueryandsessionFs.sqliteExistsRPCs are now dispatched to a user-supplied provider. The provider-style handler accepts an optional nested:sqlite {:query (fn [query-type sql params]) :exists (fn [])}map, alongside the existing filesystem keys. The low-level handler shape uses flat:sqlite-query/:sqlite-existskeys (the adapter translates between them). Clients advertise support via:capabilities {:sqlite true}under:session-fs; the value is forwarded onsessionFs.setProviderand validated at session creation (declaringcapabilities.sqlitewithout providing a:sqlitehandler now throws).query-typeis automatically coerced from the wire string to a keyword (#{:exec :query :run}). SQL bind-parameter keys (e.g.$userId) are preserved verbatim through wire normalization, and result row column-name keys (e.g.:user_id,:created_at) round-trip verbatim on the outgoing wire path — they are no longer mangled by recursive kebab→camelCase conversion. SQLite errors propagate as JSON-RPC errors (not wrapped as SessionFsError). (upstream PR #1299) - Schema bump —
.copilot-schema-versionadvanced from1.0.49-1to1.0.49. Additive changes only: new named enum types (AutoModeSwitchResponse,ExitPlanModeAction,McpServerSource,McpServerStatus,SessionMode,SkillSource, renamedPermissionRequestMemoryAction/Direction),format: "duration"/"uri"annotations,"max"value in reasoning-effort description, plus the newsessionFs.sqliteQuery/sessionFs.sqliteExistsRPC methods. (upstream PRs #1305, #1307, #1327, #1333)
Fixed (post-v1.0.0-beta.4 sync, round 2)
examples/permission_bash.clj— Updated permission decision kind from the deprecated:approvedto the current:approve-once. (carried from upstream PR #1315)
Added (post-v1.0.0-beta.4 sync)
-
:session-idon hook input maps —:on-hook-invokehandlers now receive a:session-idkey on the input map. When the upstream wire payload includes asessionId(sub-agent hooks), the wire-provided value is preserved; otherwise the SDK fills in the parent session id as a convenience. (upstream PR #1290) -
:cloudsession config option (create only) —create-sessionaccepts an optional:cloudmap for creating a remote cloud session. Shape:{:repository {:owner "octocat" :name "hello-world" :branch "main"}}—:ownerand:nameare required non-blank strings;:branchis optional. Forwarded on the wire ascloud.repository.*. Matches upstream'sCloudSessionOptions/CloudSessionRepository. Not accepted onresume-session/join-session, matching upstreamResumeSessionConfig(Pick excludescloud). (upstream PR #1306) -
Optional permission and tool callbacks (manual pending RPCs) — Following upstream PR #1308,
:on-permission-requestis now optional oncreate-sessionandresume-session, and:handleris optional on tools built viatools/define-tool. When omitted, the runtime no longer auto-responds to permission requests or tool calls. Applications can resolve these requests asynchronously via the new public functions:sdk/handle-pending-tool-call!/sdk/<handle-pending-tool-call!sdk/handle-pending-permission-request!/sdk/<handle-pending-permission-request!
Useful for human-in-the-loop UIs that surface pending tool/permission requests through
sdk/get-messagesand resolve them later. Note: This is a behavioural change — previously the SDK threw if:on-permission-requestwas missing; now it's accepted and the request is treated as pending until the application resolves it. (upstream PR #1308) -
:agent-modelon custom-agent configs — Custom agent maps in:custom-agentsnow accept an optional:agent-modelstring (e.g."claude-haiku-4.5"). When set, the runtime attempts to use that model for the agent, falling back to the parent session model if unavailable. Forwarded on the wire asagentModelon each entry incustomAgentsfor bothsession.createandsession.resume. (upstream PR #1309) -
Schema bump —
.copilot-schema-versionadvanced from1.0.48to1.0.49-1. Generated wire specs and coercions regenerated; new pass-through event fields include:display-prompt,:reasoning-summary,:previous-reasoning-summary. (upstream PRs #1305, #1307)
Fixed (post-v1.0.0-beta.4 sync, review iteration)
::cloud-repositoryspec now enforces non-blank:name(was juststring?via the shared::namespec, allowing blanks despite docs).handle-pending-tool-call!and<handle-pending-tool-call!now throw when neither:resultnor:erroris supplied (previously fell through to a default "tool returned no result" payload).handle-pending-tool-call!/<handle-pending-tool-call!validate that:error, when supplied, is a string.- All four pending-RPC resolvers (
handle-pending-tool-call!,<handle-pending-tool-call!,handle-pending-permission-request!,<handle-pending-permission-request!) now require:request-idto be a non-blank string. handle-pending-permission-request!and async variant validate that:result :kindis a keyword in the documented decision set — matches the upstreamPermissionDecisionschema::approve-once,:approve-for-session,:approve-for-location,:approve-permanently,:reject,:user-not-available. Previously unsupported values (e.g.{:kind 42}) would be sent on the wire and surface as opaque server-side errors.tools/define-tool-from-specmirrorstools/define-tool: when:handleris omitted, no:tool-handlerwrapper is installed (declaration-only tool).
Notes (v1.0.0-beta.4 sync)
Upstream v1.0.0-beta.4 shipped no new Node.js SDK API surface relative to
v1.0.0-beta.3 — every SDK-visible change in the upstream diff
(ModelBilling.multiplier optional, extension permission kinds,
detachedFromSpawningParentSessionId, advisor block fields on
assistant.message, model on assistant.message, model-picker categories,
session.commands.respondToQueuedCommand) was already brought in by the
earlier CLI 1.0.48 schema sync (PR #103), whose entries appear below.
session.tasks.sendMessage and the C#/Go-only changes from beta.4 are
deliberately out of scope per the API-parity rule (see Tracked-but-not-ported).
This release bumps the upstream marker from 1.0.0-beta.3 to 1.0.0-beta.4.
Added (v1.0.0-beta.4 sync)
- Schema bump —
.copilot-schema-versionadvanced from1.0.46to1.0.48(the latest GA on npm). Generated wire specs and coercions regenerated. :remote-sessionsession config option —create-sessionandresume-sessionaccept an optional:remote-sessionkey set to:off,:export, or:on, enabling per-session Mission Control remote mode at session-creation time without a separateremote-enablecall. Forwarded to the wire asremoteSession. Reuses the::remote-session-modespec. (upstream PR #1295, CLI 1.0.48):copilot/session.custom_notificationevent — Skills (via theNotifyblock) can emit arbitrary application-level events to the SDK. The event exposes:source,:name,:payload(any JSON value), optional:subject(map of keyword→string), and optional:version(positive integer). Added tosdk/event-typesandsdk/session-events; new idiom spec::session.custom_notification-data. The:subjectand:payloadfields contain source-defined identifiers and opaque JSON, so their keys are preserved verbatim by the protocol normalizer (no kebab-casing) — matching the existing escape hatch forexternal_tool.requestedarguments. (upstream PR #1292, CLI 1.0.48)- Extension permission kinds —
::permission-kindaccepts the new upstream values:extension-managementand:extension-permission-access, emitted by the CLI for extension lifecycle and capability-access prompts. (upstream PR #1239, CLI 1.0.44-3) :detached-from-spawning-parent-session-idonsession.startevents — when a session continues another session's context (e.g., a detached headless rem-agent run launched on the parent's interactive shutdown),session.startnow exposes the spawning parent's session id. Telemetry from such sessions is reported under the parent'ssession_id. Accepted by both the regenerated wire spec and the hand-curated::specs/session.start-data. (upstream PR #1239, CLI 1.0.44-3)- Anthropic advisor block fields on
assistant.message—::assistant.message-datanow accepts the optional:anthropic-advisor-blocks(raw Anthropic content array with advisor blocks, for verbatim replay),:anthropic-advisor-model, and:model(model that produced the response, when known). The regenerated wire spec already exposed these; the hand-curated idiom spec now mirrors them. (upstream PR #1263, CLI 1.0.45) :model-picker-category/:model-picker-price-categoryonlist-models— the regeneratedModelshape carries upstream's new model-picker categorization fields ("lightweight" | "versatile" | "powerful"and"low" | "medium" | "high" | "very_high").parse-model-infonow surfaces both as idiomatic strings (open enum) on each entry returned bycopilot/list-models. (upstream PR #1270, CLI 1.0.46)session/respond-to-queued-command!(experimental) — wraps the newsession.commands.respondToQueuedCommandRPC for acknowledging:copilot/command.queuedevents. Accepts{:request-id ... :handled? true/false :stop-processing-queue? bool?}and forwards the wire shape{:requestId ..., :result {:handled bool, :stopProcessingQueue bool?}}. Marked experimental, mirroring upstream's exposure of this only via the generated low-level RPC. (upstream PR #1263, CLI 1.0.45):is-autopilot-continuationonuser.messageevents —::user.message-datanow accepts the optional boolean flag emitted by autopilot's continuation loop.trueindicates the message was auto-injected rather than typed by the user; used to distinguish autopilot-driven turns in telemetry. Wire key:isAutopilotContinuation→ kebab-case key:is-autopilot-continuation(no?suffix — camel-snake-kebab does not append?for booleans). (upstream PR #1286, CLI 1.0.47):api-endpointonassistant.usageevents —::assistant.usage-datanow accepts the optional API endpoint string identifying which CAPI endpoint produced the model call. Known values:"/chat/completions","/v1/messages","/responses","ws:/responses". Modeled as an open string spec for forward-compatibility; the regenerated wire spec enforces the closed enum. (upstream PR #1286, CLI 1.0.47):modeonsession/remote-enable(experimental) — thesession.remote.enableRPC now accepts an optionalRemoteSessionModeparameter.remote-enablegained a 2-arity overload(remote-enable session opts)whereoptsmay contain:modeset to:off,:export, or:on.:offdisables remote,:exportexports session events to Mission Control without enabling remote steering,:onenables both. Zero-arg call is unchanged. New idiom specs:::remote-session-modeand::remote-enable-opts. (upstream PR #1288, CLI 1.0.48-1):recurringonsession.schedule_createdevents —::session.schedule_created-datanow accepts the optional boolean flag indicating whether the schedule re-arms after each tick (/every) or fires once (/after). Wire key:recurring→ kebab-case key:recurring(no?suffix — csk does not append?for booleans). (upstream PR #1288, CLI 1.0.48-1)
Tracked-but-not-ported (v1.0.0-beta.4 sync)
session.tasks.sendMessage(experimental) — the upstream Tasks API (session.tasks.*) is intentionally not surfaced in the Clojure SDK yet; the newsendMessageRPC is tracked here for a future port. (upstream PR #1239, CLI 1.0.44-3)UserToolSessionApprovalextension kinds — upstream addsextension-managementandextension-permission-accessto theUserToolSessionApprovaldiscriminated union. The Clojure idiom spec for::approvalis intentionally broad (map?), so these payloads pass through unchanged; only the wire-side discriminator changed. (upstream PR #1263, CLI 1.0.45)WorkspacesGetWorkspaceResult.session_sync_levelremoval — upstream dropped this field. The Clojure SDK never surfaced it; no change needed. (upstream PR #1239, CLI 1.0.44-3)session.commands.list/session.commands.invokeRPCs — upstream added these slash-command discovery and invocation methods to the generated RPC layer in CLI 1.0.47. The Node.js SDK's publicCopilotSessiondoes NOT expose them as high-level methods (only via the low-level generated RPC), so per the API-parity rule the Clojure SDK does not surface them either. (upstream PR #1286, CLI 1.0.47)ModelBilling.tokenPrices— upstream added per-token pricing fields (inputPrice,outputPrice,cachePrice,batchSize) toModel.billing. The Clojure idiom spec for::billingis intentionally broad (open map), so these payloads pass throughlist-modelsunchanged. No dedicated idiom spec added yet. (upstream PR #1270, CLI 1.0.46)
1.0.0-beta.3.0 - 2026-05-12
Changed (release tooling)
- Version scheme — Maven qualifier support. The release workflow,
script/release.sh, andbuild.clj(sync-version,bump-version) now accept upstream versions carrying Maven pre-release qualifiers (e.g.,1.0.0-beta.3in addition to0.1.23) and the corresponding 4th-segment forms (e.g.,1.0.0-beta.3.0,1.0.0-beta.3.0-SNAPSHOT). The full grammar isX.Y.Z[-(alpha|beta|rc).M].N[-SNAPSHOT]. Maven and tools.deps already sort these correctly; only our own validation regex was too strict.
Added (v1.0.0-beta.3 sync)
:enable-session-telemetry?session config — boolean. When omitted (default) ortrue, the CLI's internal session telemetry is enabled for GitHub-authenticated sessions. Set tofalseto disable. With a custom:provider(BYOK), session telemetry is always disabled regardless of this setting. Independent of the OpenTelemetry config in:telemetry. Accepted in bothcreate-sessionandresume-session. Wire key:enableSessionTelemetry. (upstream PR #1224):on-exit-plan-modesession handler — restores the Exit Plan Mode request RPC. When the SDK is configured with this handler, the CLI sends anexitPlanMode.requestRPC asking the client to approve leaving plan mode. The handler receives(request, {:session-id ...})whererequesthas:summary, optional:plan-content,:actions(vec of string), and:recommended-action. Returns an idiomatic map with:approved?(required boolean), optional:selected-action,:feedback. When omitted, the SDK auto-replies with the wire-shaped equivalent of{:approved? true}(i.e.,{"approved": true}on the wire) and sets therequestExitPlanModecapability flag tofalse. Accepted in bothcreate-sessionandresume-session. (upstream PR #1228):on-auto-mode-switchsession handler — restores the Auto Mode Switch request RPC. When the SDK is configured with this handler, the CLI sends anautoModeSwitch.requestRPC asking the client whether to switch the agent to auto mode after a rate-limit event. The handler receives(request, {:session-id ...})whererequestmay include:error-codeand:retry-after-seconds. Returns:yes,:yes-always, or:no(keyword or matching string), or a map{:response ...}with the same. When omitted, the SDK auto-replies with the wire-shaped equivalent of:no(i.e.,{"response": "no"}on the wire) and sets therequestAutoModeSwitchcapability flag tofalse. Accepted in bothcreate-sessionandresume-session. (upstream PR #1228)AbortReasonwire enum —abortevents now carry a:reasonfield that is a closed enum of"user_initiated","remote_command","user_abort". Validated by the regenerated wire spec. (upstream schema 1.0.44-2)subagent.started.modelfield —subagent.startedevents now expose an optional:modelfield identifying the model the sub-agent will run against. Both the regenerated wire spec and the hand-curated idiom spec (::specs/subagent.started-data) accept it. (upstream schema 1.0.44-2)session.remote.enable/session.remote.disable(schema-only) — the regeneratedschemas/api.schema.jsonintroduces two new experimental RPC methods for enabling/disabling remote session access, along with aRemoteEnableResultdefinition. Not yet surfaced in the Clojure public API — the schemas are tracked here so future ports can lift them without another schema bump. (upstream schema 1.0.44-2)
Changed (v1.0.0-beta.3 sync)
- Schema bump —
.copilot-schema-versionadvanced from1.0.42to1.0.44-2; generated wire specs and coercions regenerated. - MCP binary tool result mime-type fallback — when an MCP tool returns a
blob resource whose
:mime-typeis missing, the empty string, or any non-string value, the SDK now falls back to"application/octet-stream"(previously onlyniltriggered the fallback). Matches upstream Node.js behavior. (upstream PR #1222)
Added (v1.0.0-beta.2 sync)
:remote?client option — whentrue, the SDK appends--remoteto the spawned CLI args so the headless CLI exposes its session over a GitHub-hosted remote endpoint. Ignored when:cli-urlis set (i.e., when connecting to an externally-managed CLI). (upstream PR #1192)session/remote-enable,session/remote-disable(experimental) — enable/disable remote steerability for an active session via the newsession.remote.enable/session.remote.disableJSON-RPC methods.remote-enablereturns{:url ... :remote-steerable boolean}. (upstream PR #1192)- ProviderConfig overrides —
:providerconfig now accepts:model-id,:wire-model,:max-input-tokens, and:max-output-tokensalongside the existing:base-url/:api-keyfields.:max-input-tokensis renamed to wiremaxPromptTokensto match upstream'stoWireProviderConfig. (upstream PR #966) session.schedule_created/session.schedule_cancelledevents — the CLI now emits these events when the/everyslash-command registers a scheduled prompt or it is cancelled from the schedule manager dialog; both are added toevent-typesandsession-events. (upstream schema 1.0.42)mcpToolNamefield on tool requests — already-existing:mcp-tool-nameonAssistantMessageToolRequestis now spec-validated by the regenerated wire layer. (upstream schema 1.0.42)
Changed (v1.0.0-beta.2 sync)
- Schema bump —
.copilot-schema-versionadvanced from1.0.41-0to1.0.42; generated wire specs and coercions regenerated. CustomAgentsUpdatedAgent.toolsis now nilable (tools: string[] | null) on the wire; the regenerated wire spec acceptsnil. (upstream schema 1.0.41-1)
Added (v1.0.0-beta.1 sync)
:copilot-homeclient option — base directory for Copilot data files; forwarded to the spawned CLI as theCOPILOT_HOMEenvironment variable. (upstream PR #1191):instruction-directoriessession config — additional directories to search for custom instruction files. Accepted increate-session,resume-session, andjoin-sessionconfigs and forwarded asinstructionDirectorieson the wire. (upstream PR #1190):tcp-connection-tokenclient option — connection token for the headless CLI server when running in TCP mode. When the SDK spawns its own CLI in TCP mode and the caller did not supply a token, a UUID is auto-generated so the loopback listener is safe by default. The token is sent to the CLI via theCOPILOT_CONNECTION_TOKENenvironment variable. Rejected when combined with:use-stdio? true(stdio is pre-authenticated by transport). (upstream PR #1176)connecthandshake — the SDK now performs the protocol-version handshake via the newconnectJSON-RPC method (carrying the optional connection token). Falls back topingagainst legacy servers that respond with JSON-RPCMethodNotFound(-32601), or with a non-MethodNotFound code but the message"Unhandled method connect"(matching upstream Node parity,client.ts:1132-1135). (upstream PR #1176):continue-pending-work?resume/join session config — when truthy, the CLI re-emits any in-flightpermission.requestedand external tool requests on resume so the consumer can respond, instead of treating them as interrupted. Forwarded ascontinuePendingWorkonsession.resume.
Changed (v1.0.0-beta.1 sync)
- Schema bump —
.copilot-schema-versionadvanced from0.0.403to1.0.41-0; generated wire specs (src/github/copilot_sdk/generated/) and field-level coercions regenerated. The upstream session-events schema now references each event variant via$ref; the codegen emitter (script/) was updated to dereference these refs when collecting leaf properties and emitting per-event data specs. (upstream PR #1184)
Added (v0.3.0 sync)
- Per-session GitHub authentication —
:github-tokenis now accepted increate-session,<create-session,resume-session,<resume-session, andjoin-sessionconfigs and is forwarded asgitHubTokenon session create/resume RPCs. This enables one client to manage sessions authenticated as different GitHub users. (upstream PR #1124) - Stable v0.3.0 permission decision kinds —
::permission-kindnow includes:hook, and::permission-result-kindaccepts upstream decision kinds:approve-once,:approve-for-session,:approve-for-location,:reject, and:user-not-availablein addition to:no-result. Legacy Clojure denial aliases remain accepted and are normalized before the SDK sends decisions to the CLI. (upstream PR #1124)
Changed (v0.3.0 sync)
approve-allparity —copilot/approve-allnow returns{:kind :approve-once}, matching the upstream Node.js SDKapproveAllhelper. Existing legacy:approvedpermission results are still accepted from custom handlers and normalized to:approve-once. (upstream PR #1124)
Added (codegen)
- Schema-driven codegen pipeline — new
bb codegentask generatessrc/github/copilot_sdk/generated/event_specs.cljfrom the upstream@github/copilot/schemas/session-events.schema.json. Produces ~190 spec forms (one leaf spec per unique property, one*-dataspec per event variant, one envelope spec per variant, and an aggregate::eventspec). - Schema fetch task — new
bb schemas:fetchdownloads the upstream npm package at the version pinned in.copilot-schema-versionand extracts schema JSON files intoschemas/(committed for reproducibility). - CI codegen-check workflow —
.github/workflows/codegen-check.ymlregenerates on every PR touching schemas, generator, generated files, or the pinned version, and fails on drift between committed and regenerated output. - Developer documentation — new
doc/codegen.mdexplains the pipeline, workflows for local development, and the JSON Schema →clojure.spectranslation rules. - Phase 3.5: three-tier wire/coerce/idiom architecture — new
script/codegen/coercions.edn(hand-curated event-scoped coercion table) and generatedsrc/github/copilot_sdk/generated/coerce.clj(event-wire->idiomandevent-idiom->wire). The runtime event dispatcher inclient.cljnow applies idiomatic coercion (e.g. ISO-8601 strings →java.time.Instant) before delivering events to user handlers. Coercion is fail-open: parse failures log a warning and deliver the uncoerced-but-normalized event so a malformed payload cannot kill the notification go-loop. Thehand-written-specs-agree-with-generateddrift audit now runs against coerced data with an emptyknown-driftsset, and three new invariants are enforced by tests (every coercion is exercised, converters are idempotent, round-trip is semantically lossless). - Historical event coercion —
session/get-messagesnow applies the same wire→idiom coercion pipeline as the live notification path, so:start-timeonsession.startevents fetched from history is also delivered as ajava.time.Instant. New integration testtest-get-messages-applies-coercionenforces this.
Changed (instrumentation)
- Phase 6: instrument deduplication —
src/github/copilot_sdk/instrument.cljno longer maintains three parallel symbol lists (ones/fdefper public API fn, one symbol list passed toinstrument-all!, one tounstrument-all!). A new privateregister-fdef!macro both delegates tos/fdefand records the fully-qualified symbol in a singleregistered-fdefsregistry; bothinstrument-all!andunstrument-all!now derive their target list from that registry. The macro fail-fast rejects unqualified symbols at macroexpansion time, so a stale or alias-qualified entry is caught immediately rather than silently leaving an instrumentation gap. Net effect: ~162 lines removed frominstrument.clj, no behavior change, and adding a new public API fn now requires a single edit instead of three.
Fixed (codegen)
session.startevent delivery —:selected-modelwas being read with the wrong (camelCase) key:selectedModelin the runtime dispatcher; fixed to:selected-modelto match the kebab-case keys produced byutil/wire->clj.
Changed (testing)
- Mock server validates injected event types —
mock/send-session-event!now rejects unknown event types instead of silently emitting an unrecognised notification. Validation uses the SDK's canonical publicevent-typesregistry as the source of truth, so any event the SDK recognises can still be injected. A dedicatedmock/send-v3-broadcast-event!helper restricts injection to the five protocol v3 broadcast events (kept in sync withclient/handle-v3-broadcast-event!); the four v3 integration tests have migrated to it. Surfaces typos like"session.startt"immediately rather than as a confusing missing-event test failure.
0.3.0.0-SNAPSHOT - 2026-04-23
Added (v0.3.0-preview.0 sync)
defaultAgent.excludedToolssession option — new:default-agent {:excluded-tools [...]}config for create, resume, and join session paths. This hides selected tools from the built-in/default agent while preserving tool availability for custom agents. (upstream commitb1b0df5c)- Session FS provider adapter — new
create-session-fs-adapterhelper adapts provider-style filesystem functions into structuredsessionFs.*RPC results. Session factories now also auto-adapt provider-style maps while preserving the existing low-level one-arg handler contract. (upstream commita3e273c9) - Generated event data specs — added explicit specs for assistant reasoning/message/usage fields, MCP server status/load events, skills loaded events, and extension loaded events from the upstream generated schema.
- Integration tests for
defaultAgentwire params, session FS adapter behavior, session FS factory auto-adaptation, extension status values, and event data specs.
Fixed (v0.3.0-preview.0 sync)
- Extension status enum parity —
session.extensions_loadednow accepts upstream statuses"running","starting","disabled", and"failed"and rejects obsolete"enabled". - Message content specs — assistant/user message and reasoning event content remains string-only while elicitation result content accepts maps, matching upstream generated schemas.
0.2.2.0 - 2026-04-20
Added (post-v0.2.2 sync, batch 2)
includeSubAgentStreamingEventssession option — new boolean:include-sub-agent-streaming-events?on::session-config,::resume-session-config, and::join-session-config. Whentrue(default), sub-agent streaming events are forwarded to the parent session's event stream. (upstream PR #1108)- Per-request HTTP headers on
send!— new:request-headersoption (map of string→string) on::send-options. Forwarded as wirerequestHeadersand merged with provider-level headers by the CLI. (upstream PR #1094) - Provider-level HTTP headers — new
:headersfield (map of string→string) on::providerconfig. Sent with each model request to BYOK endpoints. (upstream PR #1094) ::can-offer-session-approvalspec — boolean field present onpermission.requestedevents of kindwriteFile, indicating the CLI can offer a "trust this session" choice. (CLI 1.0.28, upstream PR #1089)::reasoning-tokensspec — non-negative integer field onassistant.usageandsession.usage_infoevents tracking tokens used for reasoning content. (CLI 1.0.32, upstream PR #1105)::agent-idspec — optional string field on::base-event, identifying which (sub-)agent emitted the event. (upstream PR #1108)- Integration tests for all new wire fields and specs (6 new
deftests covering sync/async wire forwarding and the 3 new spec additions).
Added (post-v0.2.2 sync)
convert-mcp-call-tool-result— new public function intoolsnamespace that converts MCPCallToolResultformat into the SDK'sToolResultObject. Handles text, image, and resource content types. (upstream PR #1049)default-join-session-permission-handler— new permission handler forresume-sessionthat returns{:kind :no-result}, signaling the CLI to handle permissions itself. SendsrequestPermission: falseon the wire. (upstream PR #1056)- MCP config spec aliases —
::mcp-stdio-serverand::mcp-http-serveras aliases for::mcp-local-serverand::mcp-remote-serverrespectively, matching upstream rename from Local→Stdio, Remote→HTTP. Old names kept for backward compatibility. (upstream PR #1051) - Per-agent skills field —
::agent-skills(vector of strings) on::custom-agentspec, allowing skill injection per custom agent. (upstream PR #995) - Memory permission event specs —
::memory-action,::memory-direction,::memory-reasonspecs for enriched memory permission request events. (CLI 1.0.22, upstream PR #1055) - New RPC wrappers in
sessionnamespace (all experimental):session-name-get,session-name-set!— get/set session display name (CLI 1.0.26, upstream PR #1076)workspace-get-workspace— get current workspace metadata (CLI 1.0.26, upstream PR #1076)mcp-discover— discover MCP servers in a working directory (CLI 1.0.22, upstream PR #1055)usage-get-metrics— get session usage metrics (CLI 1.0.22, upstream PR #1055)
- Integration tests for all new features (18 tests covering convert-mcp-call-tool-result, spec renames, agent skills, requestPermission behavior, new RPCs, and memory specs)
Changed (post-v0.2.2 sync)
requestPermissionon resume —resume-sessionnow sendsrequestPermission: falsewhen usingdefault-join-session-permission-handler, andtruewhen using any other handler (e.g.,approve-all). Previously always senttrue. (upstream PR #1056)
Added (v0.2.2 sync)
enableConfigDiscoverysession option — new boolean:enable-config-discoveryon session and resume configs. Auto-discovers.mcp.json,.vscode/mcp.json, skills, etc. Instruction files are always loaded regardless. (upstream PR #1044)modelCapabilitiesoverride — new:model-capabilitiesoption on session config, resume config, andswitch-model!/set-model!. Pass a partial capabilities map (e.g.{:model-supports {:supports-vision true}}) to override model capabilities for the session. (upstream PR #1029)history-truncate!— new experimental function to trigger manual truncation of session context (upstream PR #1039)sessions-fork!— new experimental function to fork the current session (upstream PR #1039)- Integration tests for all new features (wire param verification, RPC routing)
Changed (v0.2.2 sync)
compaction-compact!RPC renamed — underlying JSON-RPC method changed fromsession.compaction.compacttosession.history.compact(upstream PR #1039). The Clojure function name is unchanged for backward compatibility.
0.2.1.1 - 2026-04-04
Added
- Session RPC wrappers — new experimental functions for session-level RPCs previously only accessible via
proto/send-request!:mode-get,mode-set!— get/set agent mode (interactive/plan/autopilot)plan-read,plan-update!,plan-delete!— read/update/delete session plan fileworkspace-list-files,workspace-read-file,workspace-create-file!— session workspace file operationsagent-list,agent-get-current,agent-select!,agent-deselect!,agent-reload!— custom agent managementfleet-start!— start parallel sub-sessions
- MCP config wrappers — new experimental server-level functions in
client:mcp-config-list,mcp-config-add!,mcp-config-update!,mcp-config-remove!— MCP server configuration management
- Hooks integration tests — 6 tests covering all hook types (preToolUse, postToolUse, sessionStart, unknownType, handler exceptions, no-hooks)
- User input handler tests — 2 tests for
userInput.requestserver→client RPC - System message transform tests — 3 tests for
systemMessage.transformcallback invocation, error fallback, and passthrough - Tool result normalization tests — 3 tests for string, nil, and structured ToolResultObject results via v3 broadcast
- Session RPC wrapper tests — 18 integration tests for all new RPC wrapper functions
- Mock server enhancements —
send-rpc-request!for testing server→client RPCs, response routing in server loop, 30+ new method stubs - Full
s/fdefinstrumentation for all 19 new public functions
Changed (v0.2.1 sync)
session.errorevent data spec enriched — optional:status-code(int),:provider-call-id(string), and:url(string) fields added to::session.error-dataspec. These fields carry HTTP status codes, GitHub request tracing IDs, and actionable URLs from upstream error events (upstream PR #999, runtime 1.0.17).
0.2.1.0 - 2026-04-04
Added (v0.2.1 sync)
resolvedByHookguard onpermission.requested— when the runtime resolves a permission request via apermissionRequesthook, the broadcast event includesresolvedByHook: true. The SDK now skips the client's:on-permission-requesthandler and does not send thehandlePendingPermissionRequestRPC, preventing duplicate responses. Event subscribers still observe the event (upstream PR #999, runtime 1.0.17).- New permission result kinds —
:denied-by-content-exclusion-policyand:denied-by-permission-request-hookadded to::permission-result-kindspec (upstream PR #999). - MCP fields on
tool.execution_startevents — optional:mcp-server-nameand:mcp-tool-namefields added to::tool.execution_start-dataspec indicating the MCP server and original tool name for MCP-originated tool calls (upstream runtime 1.0.17). ::resolved-by-hookspec — boolean spec for theresolvedByHookfield onpermission.requestedevent data.- Commands example — new
examples/commands.cljdemonstrating slash command registration and handling. - Integration tests for
resolvedByHookguard (both true and false cases), new permission result kind specs, and MCP tool event fields.
Changed
- Public preview branding — README updated from "technical preview" to "public preview" with link to the announcement.
Changed (v0.2.1 sync)
- BREAKING: Elicitation handler signature changed from 2-arg
(fn [request ctx])to single-arg(fn [context]). TheElicitationContextmap now includes:session-idalongside request fields (:message,:requested-schema,:mode,:elicitation-source,:url). Matches upstream cross-SDK consistency change (upstream PR #960).::elicitation-requestspec renamed to::elicitation-context.
Added (v0.2.1 sync)
remote-steerable?field onsession.startandsession.resumeevents — event data now includes optional:remote-steerable?boolean field indicating whether the session supports remote steering via Mission Control. Replaces previous:steerable?(upstream PRs #927, #908).get-session-metadata— new function on client for efficient O(1) session lookup by ID. Returns session metadata map if found, ornilif not found. Sendssession.getMetadataJSON-RPC call. Sharedwire->session-metadatahelper extracted fromlist-sessionsto eliminate duplication (upstream PR #899).- Elicitation provider support — new
:on-elicitation-requesthandler onSessionConfigandResumeSessionConfig. When provided, sendsrequestElicitation: truein the session create/resume RPC. The runtime routeselicitation.requestedbroadcast events to the handler, and results are sent back viasession.ui.handlePendingElicitationRPC. Handler errors automatically send a cancel response. New::elicitation-requestand::on-elicitation-requestspecs (upstream PR #908). capabilities.changedevent handling — session capabilities are dynamically updated whencapabilities.changedbroadcast events are received, e.g. when another client joins with elicitation support (upstream PR #908).- New event types —
sampling.requested,sampling.completed,session.remote_steerable_changed,capabilities.changedadded to event type enum and event sets (upstream PRs #908, #916). - Subagent event data fields —
subagent.started,subagent.completed,subagent.failedevents now include optional:model,:total-tool-calls,:total-tokens,:duration-msfields. New::subagent.started-data,::subagent.completed-data,::subagent.failed-dataspecs (upstream PR #916). skill.invokedevent:descriptionfield — optional:descriptionfrom SKILL.md frontmatter (upstream PR #916).session.custom_agents_updatedpayload spec — full::session.custom_agents_updated-dataspec with:agents(array of agent metadata),:warnings,:errors. New::custom-agent-infospec (upstream PR #916).- SessionFs virtual filesystem — new
:session-fsclient option with:initial-cwd,:session-state-path,:conventions. Client callssessionFs.setProviderRPC on connect. New:create-session-fs-handleron session config provides a per-session FS handler factory. The SDK dispatches incomingsessionFs.*RPC requests (10 operations:readFile,writeFile,appendFile,exists,stat,mkdir,readdir,readdirWithTypes,rm,rename) to the session's handler. Enables custom session storage backends (upstream PR #917). aborted?onsession.task_complete— optional boolean indicating the preceding agentic loop was cancelled via abort signal. New::aborted?spec (upstream PR #917).timeouttool result type —::result-typenow accepts:timeout/"timeout"for tool calls that timed out (upstream PR #970).- Integration tests for elicitation provider routing, handler error→cancel fallback, capabilities.changed updates, and requestElicitation wire flag.
Changed (v0.2.1 sync)
- BREAKING:
::steerable?renamed to::remote-steerable?onsession.startandsession.resumeevent data, matching upstream wire field rename fromsteerabletoremoteSteerable(upstream PR #908). session.idleis now ephemeral — the runtime no longer persistssession.idleevents in session history.get-messageswill no longer returnsession.idleevents. Live event listeners (used bysend-and-wait!andsend!) are unaffected and still receive it (upstream PR #927).
0.2.1.1-SNAPSHOT - 2026-03-26
Added (v0.2.1 sync)
- Commands support — register slash commands per-session via
:commandsoption in session config. Each command definition has:name, optional:description, and a:command-handlerfunction. Commands are sent on the wire (name + description) and executed viacommand.executebroadcast events withsession.commands.handlePendingCommandRPC callback (upstream PR #906). - UI Elicitation convenience API — new public functions
confirm!,select!,input!wrap the existingui-elicitation!with typed schemas.capabilitiesaccessor returns host capabilities from session create/resume response.elicitation-supported?predicate checks if the host supports elicitation dialogs. All convenience methods throw with a clear error when elicitation is unsupported (upstream PR #906). COPILOT_CLI_PATHenv var fallback — client constructor now checksCOPILOT_CLI_PATHenvironment variable before defaulting to"copilot"when no explicit:cli-pathor:cli-urlis provided (upstream PR #906).- New event type
session.custom_agents_updatedadded to event type enum. :hostfield onsession.handoffevents — event data now includes optional:hostfield with the GitHub host URL. New::session.handoff-dataspec documents the shape (upstream PR #900).- New specs:
::command-definition,::commands,::session-capabilities,::elicitation-params,::elicitation-result,::input-options. - Function specs and instrumentation for
capabilities,elicitation-supported?,confirm!,select!,input!. - Integration tests for command wire format, command.execute routing, unknown command errors, handler errors, capabilities storage, and elicitation guards.
Changed (v0.2.1 sync)
ui-elicitation!no longer marked^:experimental— now asserts elicitation support before calling. Updated fdef to use::elicitation-paramsspec.- Mock server
handle-requestnow supportssession.commands.handlePendingCommandRPC and allows request hooks to merge additional data into responses.
0.2.0.0 - 2026-03-23
Added (v0.2.0 sync)
- System message customize mode — new
:customizemode for:system-messageenables section-level overrides of the Copilot system prompt. Ten configurable sections::identity,:tone,:tool-efficiency,:environment-context,:code-change-rules,:guidelines,:safety,:tool-instructions,:custom-instructions,:last-instructions. Each section supports static actions (:replace,:remove,:append,:prepend) and transform callbacks (1-arity functions receiving current content, returning modified text). Newsystem-prompt-sectionsconstant exported from main namespace (upstream PR #816). - New experimental RPC methods — thin wrapper functions in
sessionnamespace for emerging CLI APIs (upstream PR #900):- Skills:
skills-list,skills-enable!,skills-disable!,skills-reload! - MCP servers:
mcp-list,mcp-enable!,mcp-disable!,mcp-reload! - Extensions:
extensions-list,extensions-enable!,extensions-disable!,extensions-reload! - Plugins:
plugins-list - Compaction:
compaction-compact! - Shell:
shell-exec!,shell-kill! - UI:
ui-elicitation!
- Skills:
- 15 new event types added to the event type enum:
command.completed,command.execute,command.queued,commands.changed,exit_plan_mode.requested,exit_plan_mode.completed,external_tool.completed,mcp.oauth_required,mcp.oauth_completed,session.tools_updated,session.background_tasks_changed,session.skills_loaded,session.mcp_servers_loaded,session.mcp_server_status_changed,session.extensions_loaded. - Experimental API annotations (
^:experimentalmetadata) on all new RPC method wrappers. - Function specs (
s/fdef) and instrumentation for all new RPC methods.
Changed (v0.2.0 sync)
- Version bump to
0.2.0.0-SNAPSHOTtracking upstream copilot-sdk v0.2.0. - Updated
interaction-eventsset to include new event types (commands, MCP OAuth, exit plan mode).
0.1.33.0-SNAPSHOT - 2026-03-19
Added
:no-resultpermission outcome — extensions can attach to sessions without actively answering permission requests by returning{:kind :no-result}from their:on-permission-requesthandler. On v3 protocol, thehandlePendingPermissionRequestRPC is skipped; on v2, an error is propagated to the CLI (upstream PR #802).:blobattachment type for outbound messages — send inline base64-encoded data (e.g. images) via{:type :blob :data "..." :mime-type "image/png"}in:attachments. Previously blob attachments were only supported in inbound events (upstream PR #731).
Added (v0.1.33 sync)
:skip-permission?option on tool definitions — whentrue, the tool executes without triggering a permission prompt. Sent asskipPermission: truein the wire protocol (upstream PR #808).- OpenTelemetry support: new
:telemetryclient option (map with:otlp-endpoint,:file-path,:exporter-type,:source-name,:capture-content?) configures OTel environment variables on the spawned CLI process. New:on-get-trace-contextclient option (0-arity fn returning{:traceparent ... :tracestate ...}) enables W3C Trace Context propagation intosession.create,session.resume, andsession.sendRPCs (upstream PR #785). - Tool invocations now receive
:traceparentand:tracestatefields in the invocation context map when the CLI provides them (upstream PR #785). - Optional
:reasoning-effortparameter inswitch-model!andset-model!— pass{:reasoning-effort "high"}as a third argument to set reasoning effort when switching models (upstream PR #712). - New event data fields from upstream codegen update (upstream PR #796):
session.startevent::reasoning-effort,:already-in-use?,:host-type,:head-commit,:base-commitoptional fieldssession.resumeevent: new::session.resume-dataspec with:event-count,:selected-model,:reasoning-effort,:already-in-use?,:host-type,:head-commit,:base-commitsession.model_changeevent: new::session.model_change-dataspec with:new-model,:previous-model,:reasoning-effort,:previous-reasoning-effortuser.messageevent: new:blobattachment type with:data(base64),:mime-type, optional:display-name
Changed (v0.1.33 sync)
join-sessionnow makes:on-permission-requestoptional. When omitted, a default handler returns{:kind :no-result}, leaving any pending permission request unanswered. This matches the upstreamJoinSessionConfigwhereonPermissionRequestis optional (upstream PR #802).:auto-restart?client option is deprecated and has no effect. The auto-restart/reconnect behavior has been removed across all official SDKs. The option is retained for backward compatibility but will be removed in a future release (upstream PR #803).
Added (documentation)
- "Permission Handling" section in README.md — covers deny-by-default model,
approve-all, custom handlers, and links to API reference (upstream PR #879).
0.1.32.0 - 2026-03-12
Added (upstream sync)
- Session pre-registration: sessions are now created and registered in client state before the RPC call, preventing early events (e.g.
session.start) from being dropped. Session IDs are generated client-side viajava.util.UUID/randomUUIDwhen not explicitly provided. On RPC failure, sessions are automatically cleaned up (upstream PR #664). :on-eventoptional handler increate-sessionandresume-sessionconfigs — a 1-arity function receiving event maps, registered before the RPC call so no events are missed. Equivalent to callingsubscribe-eventsimmediately after creation, but executes earlier in the lifecycle (upstream PR #664).join-sessionfunction — convenience for extensions running as child processes of the Copilot CLI. ReadsSESSION_IDfrom environment, creates a child-process client, and resumes the session with:disable-resume? true. Returns{:client ... :session ...}(upstream PR #737).:copilot/system.notificationevent type — structured notification events with:kinddiscriminator (agent_completed,shell_completed,shell_detached_completed) (upstream PR #737).
Changed
CopilotSessionrecord no longer includesworkspace-pathas a field. Use(workspace-path session)accessor which reads from mutable session state. This enables the pre-registration flow where workspace-path is set after the RPC response.
0.1.32.0 - 2026-03-10
Added (v0.1.32 sync)
:agentoptional string parameter increate-sessionandresume-sessionconfigs — pre-selects a custom agent by name when the session starts. Must match a name in:custom-agents. Equivalent to callingagent.selectafter creation (upstream PR #722).:on-list-modelsoptional handler in client options — zero-arg function returning model info maps. Bypasses themodels.listRPC call and does not requirestart!. Results use the same promise-based cache (upstream PR #730).log!session method — logs a message to the session timeline via"session.log"RPC. Accepts optional:level("info","warning","error") and:ephemeral?(transient, not persisted) options. Returns the event ID string (upstream PR #737).:is-child-process?client option — whentrue, the SDK connects via its own stdio to a parent Copilot CLI process instead of spawning a new one. Mutually exclusive with:cli-url; requires:use-stdio?to betrue(or unset) (upstream PR #737).
0.1.30.1 - 2026-03-07
Added
disconnect!function as the preferred API for closing sessions, matching upstream SDK'sdisconnect()(upstream PR #599).destroy!is deprecated but still works as an alias.- 6 new broadcast event types from CLI protocol 0.0.421 (upstream PR #684):
:copilot/permission.requested,:copilot/permission.completed,:copilot/user_input.requested,:copilot/user_input.completed,:copilot/elicitation.requested,:copilot/elicitation.completed - New
interaction-eventscategory set for permission, user input, and elicitation flow events :memorypermission kind added to::permission-kindspec (upstream PR #684)- Protocol v3 support with backwards compatibility (supports v2 and v3). The SDK negotiates the protocol version with the CLI server at startup using a supported range
[2, 3]. Version 3 replacestool.callandpermission.requestRPC callbacks with broadcast events (external_tool.requested,permission.requested) and new RPC response methods (session.tools.handlePendingToolCall,session.permissions.handlePendingPermissionRequest). - Custom agents & sub-agent orchestration guide (
doc/guides/custom-agents.md)
Changed
stop!now usesdisconnect!internally instead ofdestroy!delete-session!docstring clarified to contrast withdisconnect!- Version negotiation now validates the CLI-reported protocol version is within the supported range
[2, 3]instead of requiring exact match on version 2
Deprecated
destroy!— usedisconnect!instead.destroy!delegates todisconnect!and will be removed in a future release.
0.1.30.0 - 2026-03-04
Added (v0.1.30 sync)
- Support overriding built-in tools via
:overrides-built-in-tooloption indefine-tool(upstream PR #636). Tools with this flag set totruecan override built-in tools likegreporedit_file. Without the flag, name clashes cause an error. set-model!function as alias forswitch-model!, matching the upstream SDK'ssetModel()API (upstream PR #621).
Changed (v0.1.30 sync)
- Updated
switch-model!andget-current-modeldocstrings — removed stale "not yet implemented as of CLI 0.0.412" notes. These RPC methods are now supported.
0.1.29.0 - 2026-03-03
Added (upstream PR #605 sync)
:copilot/subagent.deselectedevent type added to::event-typespec,event-typesvar, and API reference table (upstream PR #605 / CLI 0.0.420).:github-referenceattachment type: represents a GitHub issue, PR, or discussion attached to a user message. Added to::attachment-typespec and::attachmentspec. Data fields:number,title,reference-type("issue"/"pr"/"discussion"),state,url(upstream PR #605).::assistant.turn_start-dataspec withturn-id(required) andinteraction-id(optional).:interaction-idoptional field added to::user.message-data,::assistant.turn_start-data,::assistant.message_delta-data, and::tool.execution_complete-dataspecs (upstream PR #605).:modeloptional field added to::tool.execution_complete-dataspec — model used for the tool execution (upstream PR #605).:plugin-nameand:plugin-versionoptional fields added to::skill.invoked-dataspec (upstream PR #605).
Added (documentation)
- Azure Managed Identity BYOK guide (
doc/auth/azure-managed-identity.md): shows how to useDefaultAzureCredentialwith short-lived bearer tokens for Azure AI Foundry, with Clojure examples for basic usage and token refresh (upstream PR #498). - Updated BYOK limitations to link to the Managed Identity workaround instead of listing it as fully unsupported.
- Added Azure Managed Identity guide to
doc/auth/index.mdanddoc/index.md.
Added (upstream PR #512 sync)
examples/file_attachments.clj— Demonstrates sending file attachments with prompts using:attachmentsin message options.examples/session_resume.clj— Demonstrates session resume: create session, send secret word, resume by ID, verify context preserved.examples/infinite_sessions.clj— Demonstrates infinite sessions with context compaction thresholds for long conversations.examples/lifecycle_hooks.clj— Demonstrates all 6 lifecycle hooks: session start/end, pre/post tool use, user prompt submitted, error occurred.examples/reasoning_effort.clj— Demonstrates the:reasoning-effortsession config option.
0.1.28.0 - 2026-02-27
Changed (upstream PR #554 sync)
- BREAKING:
:on-permission-requestis now required when callingcreate-session,resume-session,<create-session, and<resume-session. Calls without a handler throwExceptionInfowith a descriptive message. This matches upstream Node.js SDK whereonPermissionRequestis required inSessionConfigandResumeSessionConfig(upstream PR #554). create-sessionand<create-sessionno longer accept a 0-arity (no config) form — a config map with:on-permission-requestmust always be provided.resume-sessionand<resume-sessionno longer accept a 2-arity (no config) form — a config map with:on-permission-requestmust always be provided.- All examples, tests, and documentation updated to always pass
:on-permission-request.
Added (upstream PR #555 sync)
:custom-toolpermission kind —::permission-kindspec now includes:custom-tool, matching the upstreamPermissionRequest.kindunion type. Permission handlers will receive{:permission-kind :custom-tool ...}for SDK-registered custom tool invocations (upstream PR #555).
Added (upstream PR #544 sync)
:copilot/session.task_completeevent type added to::event-typespec andevent-typesvar. Previously it was in the spec but missing from the publicevent-typesset (upstream PR #544).:copilot/assistant.streaming_deltanew event type: emitted when the total response size changes during streaming. Data:{:total-response-size-bytes N}. Added to::event-typespec,event-typesvar, andassistant-eventsvar (upstream PR #544).:copilot/session.mode_changedevent type: emitted when the session agent mode changes. Data:{:previous-mode "...", :new-mode "..."}. Added to::event-typespec,event-typesvar, andsession-eventsvar.:copilot/session.plan_changedevent type: emitted when the session plan changes. Data:{:operation "create"|"update"|"delete"}. Added to::event-typespec,event-typesvar, andsession-eventsvar.:copilot/session.workspace_file_changedevent type: emitted when a workspace file is created or updated. Data:{:path "...", :operation "create"|"update"}. Added to::event-typespec,event-typesvar, andsession-eventsvar.- Data specs for new events:
::session.mode_changed-data,::session.plan_changed-data,::session.workspace_file_changed-data,::session.task_complete-data,::assistant.streaming_delta-data.
Changed (upstream PR #544 sync)
::assistant.message_delta-dataspec: removed::total-response-size-bytesfrom optional keys. The response size is now delivered via the separateassistant.streaming_deltaevent (upstream PR #544).
Added (documentation)
- Microsoft Foundry Local BYOK provider guide in
doc/auth/byok.md: quick start example, installation instructions, and connection troubleshooting (upstream PR #461). doc/reference/API.md: added:copilot/session.task_completeto the Event Reference table (from upstream PR #544 sync).doc/reference/API.md: added permission kind reference table (:shell,:write,:mcp,:read,:url,:custom-tool) in the Permission Handling section.
Added (upstream PR #329 sync)
- Windows console window hiding: CLI process is spawned with explicit PIPE redirects ensuring the JVM sets
CREATE_NO_WINDOWon Windows — no console window appears in GUI applications. Equivalent to upstreamwindowsHide: true(upstream PR #329).
Changed
- Recommended default model for non-streaming examples is
claude-haiku-4.5instead ofgpt-5.2for faster response times
0.1.26.0-SNAPSHOT - 2026-02-20
Added (upstream PR #510 sync)
:client-nameoption forcreate-sessionandresume-session— identifies the application using the SDK, included in the User-Agent header for API requests. Forwarded asclientNameon the wire (upstream PR #510).
Changed (upstream PR #509 sync)
- BREAKING: Deny all permissions by default —
requestPermissionis now alwaystrueon the wire, and permission requests are denied when no:on-permission-requesthandler is configured. Previously, omitting the handler meant the CLI never asked for permission. To restore the old behavior, pass:on-permission-request copilot/approve-allin your session config.
Added (upstream PR #509 sync)
approve-all— convenience permission handler that approves all requests (copilot/approve-all). Equivalent to the upstream Node.js SDKapproveAllexport. Use as:on-permission-request copilot/approve-allin session config.- Integration tests for deny-by-default permission model: wire format assertions,
approve-allbehavior, handler dispatch with/without handler, custom selective handler
Changed
- MCP local server example now passes
:on-permission-request copilot/approve-all(required for MCP tool execution under deny-by-default)
Fixed
- Permission denial result
:kindnow consistently uses keywords (not strings) in default handler responses, matching specs andapprove-allbehavior
0.1.25.1 - 2026-02-18
Fixed
- Release pipeline: GPG signing now fails fast with a clear error when no key is available, instead of silently producing unsigned artifacts that Maven Central rejects
- Release pipeline:
stamp-changelogno longer throws when[Unreleased]is empty — prints a warning and exits cleanly
Changed
- Metadata API example: suppressed SDK INFO log noise, improved session display (short IDs, summaries, timestamps), clearer messaging for unsupported CLI methods
0.1.25.0 - 2026-02-18
Added
- Core.async native async architecture — eliminates all blocking operations from the async API path:
<create-session/<resume-session— async session lifecycle functions that return channels deliveringCopilotSession, safe for use insidegoblocks- Protocol layer now uses core.async channels instead of Java promises for RPC responses
- Session send-lock replaced
java.util.concurrent.Semaphorewith a channel-based lock <send-async*— fully non-blocking send pipeline using parking channel operations- The idiomatic pattern is now
(go (let [s (<! (<create-session client opts))] (<! (<send! s {:prompt "..."}))))— no thread pool starvation
Fixed
- Wire parity: always send
requestPermission,requestUserInput, andhooksfields (asfalsewhen not configured) to match upstream Node.js SDK — fixes 400 errors when creating sessions without specifying a model - MCP server environment variables now passed correctly as literal values to subprocesses — sends
envValueMode: "direct"on session create/resume wire payloads (upstream PR #484) - Fix potential semaphore deadlock in
send-and-wait!andsend-async*—tapon a closed mult could leave the send-lock permanently held; movedtapinside thetry/finallyblock that releases the lock - Multi-agent example parallelism: sessions and sends now run concurrently in
goblocks instead of sequentially blocking
Added (v0.1.24 sync)
- CLI stderr is now captured and forwarded to debug logging; included in error messages on startup failure for better diagnostics (inspired by upstream PR #492)
verify-protocol-version!now races the initial ping against process exit to detect early CLI failures instead of blocking for 60 seconds on timeoutlist-sessionsnow accepts optional filter map{:cwd :git-root :repository :branch}to narrow results by session context (upstream PR #427)- Session metadata from
list-sessionsnow includes:contextmap with working directory info ({:cwd :git-root :repository :branch}) when available (upstream PR #427) list-tools— list available tools with metadata; accepts optional model param for model-specific overrides (upstream PR #464)get-quota— get account quota information (entitlements, usage, overage) (upstream PR #464)get-current-model— get the current model for a session (session-scoped) (upstream PR #464)switch-model!— switch the model for a session (session-scoped) (upstream PR #464)- New event types:
session.context_changed,session.title_changed,session.warning(upstream PRs #396, #427) line-rangeoptional field on file/directory attachment specs (upstream session-events schema update)agent-modeoptional field onuser.messageevent data — one of:interactive,:plan,:autopilot,:shell(upstream session-events schema update)
Changed
- BREAKING: Namespace prefix renamed from
krukow.copilot-sdktogithub.copilot-sdk. All requires must be updated (e.g.,github.copilot-sdk.client,github.copilot-sdk.helpers). - Repository moved to
copilot-community-sdk/copilot-sdk-clojureon GitHub. Maven artifact unchanged:io.github.copilot-community-sdk/copilot-sdk-clojure. - Git dependency URL in README fixed to point to new org
Added (v0.1.23 sync)
- Selection attachment type support (
:selectionwith:file-path,:display-name,:selection-range,:text) - Session lifecycle event subscription via
on-lifecycle-event(:session.created,:session.deleted,:session.updated,:session.foreground,:session.background) - Enhanced model info with full capabilities (
:model-capabilities), billing (:model-billing), and policy (:model-policy) structures session.shutdownandskill.invokedevent types"xhigh"reasoning effort level
Added (CI/CD)
- GitHub Actions CI workflow: runs
bb ci(unit/integration tests, doc validation, jar build) on PRs andmainpushes - Daily documentation updater agentic workflow: automatically scans for merged PRs and updates docs
.github/instructions/documentation.instructions.md: guidelines for AI agents updating documentation- GitHub Actions Release workflow: manual dispatch with version management inputs (
sync-upstream,bump-clj-patch,set-version), GPG signing, Maven Central deploy, and SLSA build provenance attestation bb citask: runs tests, doc validation, and jar build (no copilot CLI required)bb ci:fulltask: full pipeline including E2E tests and examples (requires copilot CLI)- Cross-platform
build.clj:md5-hashandsha1-hashhelpers with macOS/Linux fallback - Idempotent
update-readme-sha: succeeds when README already has current SHA stamp-changelogbuild task: automatically moves[Unreleased]entries to a versioned section with today's date and updates comparison links; integrated into the release workflow
Changed (CI/CD)
- Release workflow now creates a PR with auto-merge instead of pushing directly to
main, compatible with branch protection rules requiring PRs and status checks - Release workflow creates a
vX.Y.Z.Ntag after successful deploy
Added (documentation)
doc/index.md— Documentation hub / table of contentsdoc/style.md— Documentation authoring style guidedoc/reference/API.md— API reference (moved fromdoc/API.md)PUBLISHING.md— Maven Central publishing guidescript/validate_docs.clj— Documentation validation script (bb validate-docs).github/skills/update-docs/SKILL.md— Update-docs skill for regenerating docs after source changes
Changed
- BREAKING: Version scheme changed to 4-segment format
UPSTREAM.CLJ_PATCH(e.g.,0.1.22.0) to track upstream copilot-sdk releases. See PUBLISHING.md for details. - New build tasks:
sync-version(align to upstream),bump-version(increment clj-patch) - Replaced
cheshire/cheshire(Clojars) withorg.clojure/data.json(Maven Central) for JSON processing — eliminates Clojars and Jackson transitive dependencies - Deprecated Clojars publishing (
net.clojars.krukow/copilot-sdk). Use Maven Central (io.github.copilot-community-sdk/copilot-sdk-clojure) going forward.
Removed
- Java API (
java_api.clj), Java examples, and AOT compilation. For Java/JVM usage, see copilot-sdk-java. doc/intro.mdanddoc/java-async-api.md(replaced by reorganized documentation)
Added
- Resume session config parity with create-session (upstream PR #376):
resume-sessionnow accepts:model,:system-message,:available-tools,:excluded-tools,:config-dir, and:infinite-sessionsoptions
- API parity with official Node.js SDK (
@github/copilot-sdk)::working-directoryoption forcreate-sessionandresume-session:disable-resume?option forresume-sessionget-foreground-session-idandset-foreground-session-id!client methods (TUI+server mode):large-outputmarked as experimental (CLI protocol feature, not in official SDK)
- New metadata APIs (upstream PR #77):
get-status- Get CLI version and protocol informationget-auth-status- Get current authentication statuslist-models- List available models with metadata
- New event type
:copilot/tool.execution_progressfor progress updates during long-running tool executions - Infinite sessions support (upstream PR #76):
:infinite-sessionsconfig option forcreate-session- Automatic context compaction when approaching context window limits
- New event types:
:copilot/session.compaction_start,:copilot/session.compaction_complete
- Session workspace path accessors for Clojure and Java APIs
- New event type
:copilot/session.snapshot_rewindfor session state rollback (upstream PR #208) - Exported event type constants:
event-types- All valid event typessession-events- Session lifecycle and state eventsassistant-events- Assistant response eventstool-events- Tool execution events
- New example:
session_events.clj- demonstrates monitoring session state events - Authentication options for client (upstream PR #237):
:github-token- GitHub token for authentication (setsCOPILOT_SDK_AUTH_TOKENenv var):use-logged-in-user?- Whether to use logged-in user auth (default: true, false when token provided)
- Hooks and user input handlers (upstream PR #269):
:on-user-input-request- Handler forask_usertool invocations:hooks- Lifecycle hooks map with callbacks::on-pre-tool-use- Called before tool execution:on-post-tool-use- Called after tool execution:on-user-prompt-submitted- Called when user sends a prompt:on-session-start- Called when session starts:on-session-end- Called when session ends:on-error-occurred- Called on errors
- Reasoning effort support (upstream PR #302):
:reasoning-effortsession config option ("low", "medium", "high", "xhigh")- Model info now includes
:supports-reasoning-effort,:supported-reasoning-efforts,:default-reasoning-effort
- Documentation:
doc/getting-started.md— Comprehensive tutorialdoc/auth/index.md— Authentication guide (all methods, priority order)doc/auth/byok.md— BYOK (Bring Your Own Key) guide with examples for OpenAI, Azure, Anthropic, Ollamadoc/mcp/overview.md— MCP server configuration guidedoc/mcp/debugging.md— MCP debugging and troubleshooting guide
- New examples:
examples/byok_provider.clj— BYOK provider configurationexamples/mcp_local_server.clj— MCP local server integration
- BYOK validation:
create-sessionandresume-sessionnow throw when:provideris specified without:model
Changed
- BREAKING: Event types are now namespaced keywords (e.g.,
:copilot/session.idleinstead of:session.idle)- Migration: Add
copilot/prefix to all event type keywords in your code
- Migration: Add
- FIX: MCP server config wire format now correctly strips
:mcp-prefix before sending to CLI. Previously:mcp-command,:mcp-args,:mcp-tools, etc. were sent asmcpCommand,mcpArgs,mcpToolson the wire; they are now correctly sent ascommand,args,toolsto match the upstream Node.js SDK. The Clojure API keys (:mcp-command,:mcp-args, etc.) are unchanged. - Protocol version bumped from 1 to 2 (requires CLI 0.0.389+)
- Removed
helpers/query-seqin favor ofhelpers/query-seq!andhelpers/query-chan list-modelsnow caches results per client connection to prevent 429 rate limiting under concurrency (upstream PR #300)- Cache is cleared on
stop!andforce-stop!
- Cache is cleared on
0.1.0 - 2026-01-18
Added
- Initial release of copilot-sdk-clojure
- Full port of JavaScript Copilot SDK to idiomatic Clojure
- JSON-RPC protocol layer with Content-Length framing
- CopilotClient for managing CLI server lifecycle
- stdio and TCP transport support
- Auto-start and auto-restart capabilities
- CopilotSession for conversation management
send!,send-and-wait!,send-asyncmessage methods- Event handling via core.async mult channels
- Tool registration and invocation
- Permission request handling
- Tool definition helpers with result builders
- System message configuration (append/replace modes)
- MCP server configuration support
- Custom agent configuration support
- Provider configuration (BYOK - Bring Your Own Key)
- Streaming support for assistant messages
- Comprehensive clojure.spec definitions
- Example applications:
- Basic Q&A conversation
- Custom tool integration
- Multi-agent orchestration with core.async
Dependencies
- org.clojure/clojure 1.12.4
- org.clojure/core.async 1.6.681
- org.clojure/spec.alpha 0.5.238
- cheshire/cheshire 5.13.0