diff --git a/.copilot-schema-version b/.copilot-schema-version index 3851c8c..ca77259 100644 --- a/.copilot-schema-version +++ b/.copilot-schema-version @@ -1 +1 @@ -1.0.84-5 +1.0.84-8 diff --git a/CHANGELOG.md b/CHANGELOG.md index 130bbaa..6dc90df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,29 @@ All notable changes to this project will be documented in this file. This change ## [Unreleased] +### Added (v1.0.84-8 sync) +- Added stable event provenance for assistant originating messages, configured + MCP server identity/source, subagent model selection, and managed MCP server + display names, and added the `"error"` outcome to the public MCP + headers-refresh completion contract. + ([upstream PR #2658](https://github.com/github/copilot-sdk/pull/2658)) + +### Changed (v1.0.84-8 sync) +- Updated the runtime and schema pin to `1.0.84-8` and recertified the complete + stable Node SDK public surface through upstream commit + [`e9df3938b0f2bb028b203f4095d48b75f155c008`](https://github.com/github/copilot-sdk/commit/e9df3938b0f2bb028b203f4095d48b75f155c008). + Factory pause/checkpoint APIs, shell completion facts, managed catalog and + workspace RPC additions, and internal skill-reference events remain outside + the stable Clojure API. + ([upstream PR #2658](https://github.com/github/copilot-sdk/pull/2658), + [upstream PR #2662](https://github.com/github/copilot-sdk/pull/2662)) + +### Fixed (v1.0.84-8 sync) +- Factory abort requests now cancel only the matching execution token, so a + stale attempt cannot cancel another attempt sharing the durable run ID. + The new factory pause/checkpoint API remains intentionally excluded. + ([upstream PR #2537](https://github.com/github/copilot-sdk/pull/2537)) + ### Added (v1.0.84-5 sync) - Added stable `:mcp-transport` metadata to `:copilot/tool.execution_start` events. The optional field accepts `"stdio"`, diff --git a/doc/api/API.html b/doc/api/API.html index f9e3363..4743f77 100644 --- a/doc/api/API.html +++ b/doc/api/API.html @@ -1300,7 +1300,7 @@

Exported Constants ;; :copilot/exit_plan_mode.requested :copilot/exit_plan_mode.completed}

For schema 1.0.83-1, :copilot/assistant.server_tool_progress also belongs to copilot/assistant-events. :copilot/session.managed_settings_enforced and :copilot/session.managed_settings_resolved belong to copilot/session-events. :copilot/tool_search.activated intentionally belongs only to the master copilot/event-types set, not copilot/interaction-events or copilot/tool-events.

-

The generated wire schemas also contain the internal assistant.turn_retry (additional model inference metadata within an existing turn) and model.call_start (model API dispatch metadata) events. They are wire-only and intentionally excluded from every curated public event set. Experimental HydraFusion routing events likewise remain generated wire evidence and are not curated as public idiom events. The experimental reasoningBlocks field on assistant.message also remains generated wire evidence rather than a stable curated idiom field. Runtime schema 1.0.84-5 also adds experimental permission carry-forward and message-authorization events plus experimental catalog trust types; these remain outside the stable Clojure API.

+

The generated wire schemas also contain internal events such as assistant.turn_retry, model.call_start, and the durable skill-context reference events. They are wire-only and intentionally excluded from every curated public event set. Experimental HydraFusion routing events likewise remain generated wire evidence and are not curated as public idiom events. The experimental reasoningBlocks field on assistant.message and :shell-execution field on tool.execution_complete also remain generated wire evidence rather than stable curated idiom fields. Runtime schema 1.0.84-8 additionally carries experimental factory pause/checkpoint, permission, workspace, and managed-catalog protocol declarations that are not part of the stable Clojure API.

evt — Event Keyword Helper

(copilot/evt :session.info)      ;; => :copilot/session.info
 (copilot/evt :assistant.message) ;; => :copilot/assistant.message
@@ -1356,7 +1356,7 @@ 

Event Reference

:copilot/assistant.reasoning Model reasoning (if supported); optional data: :rte (opaque round-trip encrypted reasoning token, for providers that require it to be replayed back) (upstream schema 1.0.79-5/6) :copilot/assistant.reasoning_delta Streaming reasoning chunk :copilot/assistant.message_start Streaming assistant message start metadata - :copilot/assistant.message Complete assistant response; optional data: :chunk-index, :chunk-count (position/count when the response was split across multiple messages), :citations (see Citations), and :rte (upstream schema 1.0.79-5/6). Each :tool-requests entry may include :type ("function" or "custom") and hosted-program attribution as :caller {:caller-id "..." :type "program"}. Its :arguments is validated only as recursive JSON (nil, strings, booleans, finite non-ratio numbers, vectors, and maps with string or keyword keys); source-defined keys are preserved verbatim rather than kebab-cased. + :copilot/assistant.message Complete assistant response; optional data includes :originating-message-id, the logical primary user message that initiated the run, plus :chunk-index, :chunk-count (position/count when the response was split across multiple messages), :citations (see Citations), and :rte (upstream schema 1.0.79-5/6). Each :tool-requests entry may include :type ("function" or "custom") and hosted-program attribution as :caller {:caller-id "..." :type "program"}. Its :arguments is validated only as recursive JSON (nil, strings, booleans, finite non-ratio numbers, vectors, and maps with string or keyword keys); source-defined keys are preserved verbatim rather than kebab-cased. :copilot/assistant.message_delta Streaming response chunk :copilot/assistant.streaming_delta Response size update during streaming; data: {:total-response-size-bytes N} :copilot/assistant.turn_end Assistant turn completed @@ -1368,20 +1368,20 @@

Event Reference

:copilot/model.call_finished Completed model dispatch metadata; data requires :turn-id, non-negative :dispatch-duration-ms, :outcome ("success", "error", "cancelled", or "rejected"), and positive :edit-classifier-version. Optional fields: :interaction-id and :contains-built-in-file-edit-request. The payload remains open for additive runtime fields. :copilot/abort Current message aborted :copilot/tool.user_requested Tool execution requested by user - :copilot/tool.execution_start Tool execution started; data includes :tool-call-id, :tool-name, optional :arguments (an opaque JSON object with source-defined, non-kebab-cased keys), :parent-tool-call-id, :mcp-server-name, :mcp-tool-name, optional :mcp-transport ("stdio", "http", "sse", or "memory"), and :model (runtime schema 1.0.84-5; upstream PR #2634) + :copilot/tool.execution_start Tool execution started; data includes :tool-call-id, :tool-name, optional :arguments (an opaque JSON object with source-defined, non-kebab-cased keys), :parent-tool-call-id, :mcp-server-name, :mcp-tool-name, :mcp-config-server-name, :mcp-config-source ("user", "workspace", "plugin", "builtin", or "managed"), :mcp-transport ("stdio", "http", "sse", or "memory"), and :model. Transport metadata was added in runtime schema 1.0.84-5; configured-server provenance was added by upstream PR #2658 and is pinned through runtime schema 1.0.84-8. :copilot/tool.execution_progress Tool execution progress update :copilot/tool.execution_partial_result Tool execution partial result - :copilot/tool.execution_complete Tool execution completed; data may include optional :structured-content (arbitrary structured tool result) (upstream schema 1.0.63) and :result (recursive opaque JSON). An error may include :message, :code, and the same :remediation values as session.error. Generated wire validation still enforces known result variants, including the shell-exit variant’s :exit-code/:shell-id/:type "shell_exit" and optional :cwd/:output-file-path/:output-preview/:output-truncated; :output-file-path was added in upstream schema 1.0.83-1. + :copilot/tool.execution_complete Tool execution completed; data may include optional :structured-content (arbitrary structured tool result) (upstream schema 1.0.63) and :result (recursive opaque JSON). An error may include :message, :code, and the same :remediation values as session.error. Generated wire validation still enforces known result variants, including the shell-exit variant’s :exit-code/:shell-id/:type "shell_exit" and optional :cwd/:output-file-path/:output-preview/:output-truncated; :output-file-path was added in upstream schema 1.0.83-1. Successful skill invocations may keep concise model-facing :detailed-content; the authoritative skill body remains on the corresponding :copilot/skill.invoked event. :copilot/tool_search.activated Persisted generic client-side tool activations restored when a session resumes. Data: {:strategy <string> :tool-names [<string> ...]}. - :copilot/subagent.started Subagent started; data includes :tool-call-id, :agent-name, :agent-display-name, and :agent-description, with optional :factory-run-id, :model, :resumable (boolean), :agent-type (string), :execution-mode (string), :parent-id (string — task-registry id of the spawning subagent; unrelated to the envelope-level :parent-id), and :task-model-source ("task_argument", "subagent_configuration", "custom_agent_definition", or "unset"). (upstream PR #2072; runtime schema 1.0.84-4) + :copilot/subagent.started Subagent started; data includes :tool-call-id, :agent-name, :agent-display-name, and :agent-description, with optional :factory-run-id, :model, :resumable (boolean), :agent-type (string), :execution-mode (string), :parent-id (string — task-registry id of the spawning subagent; unrelated to the envelope-level :parent-id), :task-model-source ("task_argument", "subagent_configuration", "custom_agent_definition", or "unset"), and :model-selection-source using the same values as subagent.completed. (upstream PR #2658; runtime schema 1.0.84-8) :copilot/subagent.configured Effective subagent execution configuration; data requires string :model and boolean :multi-turn, with optional string :reasoning-effort and :context-tier. The payload remains open for additive runtime fields. :copilot/subagent.completed Subagent completed; data includes :tool-call-id, :agent-name, :agent-display-name, and optional :cancelled, :model, :total-tool-calls, :total-tokens, :duration-ms, :first-dispatched-model, :configured-model-preference, :explicit-model-override, :model-override-reason (strings), :explicit-model-matches-preference, :configured-model-matches-actual (booleans), and :model-selection-source ("explicit_override", "configured_required", "configured_preference", "complementary_default", "session_inheritance", "agent_definition_default", or "runtime_policy"). :cancelled true means cancellation tore down the subagent; cancellation still reports completion rather than failure. :copilot/subagent.failed Subagent failed; data includes :tool-call-id, :agent-name, :agent-display-name, :error, optional :model, :total-tool-calls, :total-tokens, :duration-ms, :first-dispatched-model, :configured-model-preference, :explicit-model-override, :model-override-reason (strings), :explicit-model-matches-preference, :configured-model-matches-actual (booleans), and the same :model-selection-source values as subagent.completed. :copilot/subagent.selected Subagent selected :copilot/subagent.deselected Subagent deselected - :copilot/hook.start Hook invocation started; data requires :hook-invocation-id, :hook-type, with optional :parent-tool-call-id (upstream schema 1.0.83-1) + :copilot/hook.start Hook invocation started; data requires :hook-invocation-id, :hook-type, with optional :parent-tool-call-id (upstream schema 1.0.83-1). Durable or resumed postToolUse copies may elide duplicated tool and successful skill payloads; canonical output remains on the adjacent tool and skill events. :copilot/hook.progress Ephemeral progress update from a long-running hook; data: {:message "..."} (upstream schema 1.0.56). - :copilot/hook.end Hook invocation finished; data requires :hook-invocation-id, :hook-type, and :success, with optional :parent-tool-call-id and closed :error map. The error requires string :message, permits optional string :stack and :source, and rejects other keys (upstream schema 1.0.83-1). + :copilot/hook.end Hook invocation finished; data requires :hook-invocation-id, :hook-type, and :success, with optional :parent-tool-call-id and closed :error map. The error requires string :message, permits optional string :stack and :source, and rejects other keys (upstream schema 1.0.83-1). Durable or resumed postToolUse receipts may elide unchanged successful skill output while preserving hook-modified values. :copilot/system.message System message emitted :copilot/system.notification System notification with a structured :kind discriminator: agent_completed, agent_idle, new_inbox_message, shell_completed, shell_detached_completed, instruction_discovered, factory_completed, or unclassified. Each known kind validates its required and optional fields; agent kinds may include :display-name. :copilot/permission.requested Permission request initiated; optional :agent-mode identifies the requesting mode ("interactive", "plan", or "autopilot"), and :resolved-by-hook indicates a hook already handled it. For the MCP tool-permission variant (:server-name/:tool-name/:tool-title present), optional :can-offer-server-wide-approval indicates the host may offer a server-wide approval option. Shell requests may include :request-sandbox-bypass, :request-sandbox-bypass-reason, and :request-sandbox-permissive; the permissive form remains sandboxed while recording otherwise-blocked file and process access. @@ -1394,8 +1394,8 @@

Event Reference

:copilot/external_tool.completed External tool call completed (v3) :copilot/mcp.oauth_required MCP server requires OAuth authentication :copilot/mcp.oauth_completed MCP OAuth authentication completed - :copilot/mcp.headers_refresh_required Dynamic headers refresh request for a remote MCP server (upstream schema 1.0.66) - :copilot/mcp.headers_refresh_completed MCP headers refresh request completed (upstream schema 1.0.66) + :copilot/mcp.headers_refresh_required Dynamic headers refresh request for a remote MCP server. Data requires :request-id, :server-name, :server-url, and :reason ("auth-failed", "startup", or "ttl-expired"). + :copilot/mcp.headers_refresh_completed MCP headers refresh request completed. Data requires :request-id and :outcome ("headers", "none", "error", or "timeout"); "error" was added in runtime schema 1.0.84-6. :copilot/mcp.tools.list_changed Remote MCP server signalled its tool list changed; data includes :server-name (upstream schema 1.0.70) :copilot/mcp.resources.list_changed Remote MCP server signalled its resource list changed; data includes :server-name (upstream schema 1.0.70) :copilot/mcp.prompts.list_changed Remote MCP server signalled its prompt list changed; data includes :server-name (upstream schema 1.0.70) @@ -1410,7 +1410,7 @@

Event Reference

:copilot/session.tools_updated Session tools list updated (e.g., after model change) :copilot/session.background_tasks_changed Background tasks status changed :copilot/session.skills_loaded Skills loaded for the session - :copilot/session.mcp_servers_loaded MCP servers loaded for the session. Each server may include :source, plugin identity, :error, and :server-metadata {:instructions <string-or-nil>}. + :copilot/session.mcp_servers_loaded MCP servers loaded for the session. Each server may include a managed-catalog :display-name, :source ("user", "workspace", "plugin", "builtin", or "managed"), plugin identity, :error, and :server-metadata {:instructions <string-or-nil>}. :copilot/session.mcp_server_status_changed MCP server status changed :copilot/session.mcp_server_removed MCP server was removed; data: {:server-name "..."} :copilot/session.mcp_server_needs_reconnect MCP server requires reconnection; data: {:server-name "..."} @@ -2163,7 +2163,7 @@

list-runs list-factory-runs session.factory.listRuns List all durable runs for the session, in creation order. [session]. get-run-detail get-factory-run-detail session.factory.getRunDetail Read durable phases, agent turns, and recent progress for a run. [session run-id]. get-run-progress get-factory-run-progress session.factory.getRunProgress Page durable progress lines. 2-arity or 3-arity with an options map merged into the wire params (e.g. pagination cursors). - cancel! cancel-factory-run! session.factory.cancel Request cancellation from the CLI. The runtime’s reverse factory.abort request then marks active local executions cancelled and closes their :cancel-chan. Returns the resulting terminal envelope. [session run-id]. + cancel! cancel-factory-run! session.factory.cancel Request cancellation from the CLI. The runtime’s reverse factory.abort request targets the active execution token, marks only that attempt cancelled, and closes its :cancel-chan. Returns the resulting terminal envelope. [session run-id].

All of the above return a run envelope map with at least :run-id and a keywordized :status (one of :running, :completed, :halted, :cancelled, :error, or other non-terminal statuses reported by the CLI).

diff --git a/doc/api/agent-factories.html b/doc/api/agent-factories.html index 5b44abc..190caa2 100644 --- a/doc/api/agent-factories.html +++ b/doc/api/agent-factories.html @@ -154,7 +154,7 @@

Cancellation

There are three distinct cancellation mechanisms, and they don’t overlap:

  1. Cooperative, inside :run — react to :cancel-chan closing or poll :cancelled? to stop early. Cancellation does not interrupt :run automatically; the factory must check for it.
  2. -
  3. From outside, server-side - cancel-factory-run! asks the CLI to cancel a run. The runtime then sends a reverse factory.abort request, which closes the run’s :cancel-chan and eventually delivers a :cancelled terminal status.
  4. +
  5. From outside, server-side - cancel-factory-run! asks the CLI to cancel a run. The runtime then sends a reverse factory.abort request for the active execution token, which closes only that attempt’s :cancel-chan and eventually delivers a :cancelled terminal status. An older attempt cannot cancel a replacement that shares the durable run ID.
  6. Aborting a local wait, not the run — wait-for-factory-run! accepts an optional :cancel-chan; closing it makes the wait throw ex-info with {:type :factory-wait-cancelled} without cancelling the run itself. Use this to stop blocking on a run you still want to keep executing.

Limits

diff --git a/doc/api/github.copilot-sdk.session.html b/doc/api/github.copilot-sdk.session.html index d413510..e28e9f1 100644 --- a/doc/api/github.copilot-sdk.session.html +++ b/doc/api/github.copilot-sdk.session.html @@ -58,7 +58,8 @@

handle-elicitation-request!

(handle-elicitation-request! client session-id context)

Handle an incoming elicitation.requested broadcast event. Calls the session’s elicitation handler with a single ElicitationContext arg (includes :session-id alongside request fields). Returns a channel with the result. If the handler fails, returns {:action “cancel”} to avoid hanging requests.

handle-exit-plan-mode-request!

(handle-exit-plan-mode-request! client session-id request)

Handle an incoming exitPlanMode.request RPC (upstream PR #1228). The server asks the client whether to exit plan mode. Calls the session’s :exit-plan-mode-handler with (request, {:session-id ...}). If the handler returns a channel, awaits its value. Returns a channel with a wire-shaped response wrapped in {:result …} or {:error …}.

Default behavior (no handler): {:result {:approved true}}. Handler must return an idiomatic map containing :approved? (required boolean), optional :selected-action (string), :feedback (string). If the handler result is malformed (non-map, missing :approved?, or :approved? is not a boolean), a warning is logged and the response falls back to {:result {:approved true}} (matching the no-handler default). Exceptions thrown by the handler are converted to {:error {:code -32603 :message …}}.

-

handle-factory-abort!

(handle-factory-abort! client session-id run-id)

Cooperatively cancel active executions for a durable factory run.

+

handle-factory-abort!

(handle-factory-abort! client session-id run-id)(handle-factory-abort! client session-id run-id execution-token)

Cooperatively cancel factory executions.

+

The four-arity form targets one execution attempt. The three-arity form retains run-wide cancellation for direct callers of this low-level handler.

handle-factory-execute!

(handle-factory-execute! client session-id {:keys [name run-id execution-token], :as params})

Execute an extension-authored factory for a runtime reverse RPC.

handle-hooks-invoke!

(handle-hooks-invoke! client session-id hook-type input)

Handle an incoming hooks invocation. Returns a channel with the result. PR #269 feature.

handle-mcp-auth-request!

(handle-mcp-auth-request! client session-id request)

Handle an mcp.oauth_required MCP OAuth request (upstream PR #1669). Returns a channel yielding the wire result map for session.mcp.oauth.handlePendingRequest.

diff --git a/doc/guides/agent-factories.md b/doc/guides/agent-factories.md index 8bd12ba..1ef22a9 100644 --- a/doc/guides/agent-factories.md +++ b/doc/guides/agent-factories.md @@ -196,7 +196,7 @@ A thunk or stage that throws is either **fatal** or **ordinary**: There are three distinct cancellation mechanisms, and they don't overlap: 1. **Cooperative, inside `:run`** — react to `:cancel-chan` closing or poll `:cancelled?` to stop early. Cancellation does not interrupt `:run` automatically; the factory must check for it. -2. **From outside, server-side** - `cancel-factory-run!` asks the CLI to cancel a run. The runtime then sends a reverse `factory.abort` request, which closes the run's `:cancel-chan` and eventually delivers a `:cancelled` terminal status. +2. **From outside, server-side** - `cancel-factory-run!` asks the CLI to cancel a run. The runtime then sends a reverse `factory.abort` request for the active execution token, which closes only that attempt's `:cancel-chan` and eventually delivers a `:cancelled` terminal status. An older attempt cannot cancel a replacement that shares the durable run ID. 3. **Aborting a local wait, not the run** — `wait-for-factory-run!` accepts an optional `:cancel-chan`; closing it makes the *wait* throw `ex-info` with `{:type :factory-wait-cancelled}` without cancelling the run itself. Use this to stop blocking on a run you still want to keep executing. ## Limits diff --git a/doc/reference/API.md b/doc/reference/API.md index 7d0b345..9d1b54c 100644 --- a/doc/reference/API.md +++ b/doc/reference/API.md @@ -1794,16 +1794,17 @@ For schema 1.0.83-1, `:copilot/assistant.server_tool_progress` also belongs to `:copilot/tool_search.activated` intentionally belongs only to the master `copilot/event-types` set, not `copilot/interaction-events` or `copilot/tool-events`. -The generated wire schemas also contain the internal `assistant.turn_retry` -(additional model inference metadata within an existing turn) and -`model.call_start` (model API dispatch metadata) events. They are wire-only and -intentionally excluded from every curated public event set. Experimental -HydraFusion routing events likewise remain generated wire evidence and are not -curated as public idiom events. The experimental `reasoningBlocks` field on -`assistant.message` also remains generated wire evidence rather than a stable -curated idiom field. Runtime schema `1.0.84-5` also adds experimental -permission carry-forward and message-authorization events plus experimental -catalog trust types; these remain outside the stable Clojure API. +The generated wire schemas also contain internal events such as +`assistant.turn_retry`, `model.call_start`, and the durable skill-context +reference events. They are wire-only and intentionally excluded from every +curated public event set. Experimental HydraFusion routing events likewise +remain generated wire evidence and are not curated as public idiom events. +The experimental `reasoningBlocks` field on `assistant.message` and +`:shell-execution` field on `tool.execution_complete` also remain generated +wire evidence rather than stable curated idiom fields. Runtime schema +`1.0.84-8` additionally carries experimental factory pause/checkpoint, +permission, workspace, and managed-catalog protocol declarations that are not +part of the stable Clojure API. ### `evt` — Event Keyword Helper @@ -1868,7 +1869,7 @@ nested schema objects marked closed by upstream reject unknown keys. | `:copilot/assistant.reasoning` | Model reasoning (if supported); optional data: `:rte` (opaque round-trip encrypted reasoning token, for providers that require it to be replayed back) (upstream schema 1.0.79-5/6) | | `:copilot/assistant.reasoning_delta` | Streaming reasoning chunk | | `:copilot/assistant.message_start` | Streaming assistant message start metadata | -| `:copilot/assistant.message` | Complete assistant response; optional data: `:chunk-index`, `:chunk-count` (position/count when the response was split across multiple messages), `:citations` (see [Citations](#citations-experimental)), and `:rte` (upstream schema 1.0.79-5/6). Each `:tool-requests` entry may include `:type` (`"function"` or `"custom"`) and hosted-program attribution as `:caller {:caller-id "..." :type "program"}`. Its `:arguments` is validated only as recursive JSON (`nil`, strings, booleans, finite non-ratio numbers, vectors, and maps with string or keyword keys); source-defined keys are preserved verbatim rather than kebab-cased. | +| `:copilot/assistant.message` | Complete assistant response; optional data includes `:originating-message-id`, the logical primary user message that initiated the run, plus `:chunk-index`, `:chunk-count` (position/count when the response was split across multiple messages), `:citations` (see [Citations](#citations-experimental)), and `:rte` (upstream schema 1.0.79-5/6). Each `:tool-requests` entry may include `:type` (`"function"` or `"custom"`) and hosted-program attribution as `:caller {:caller-id "..." :type "program"}`. Its `:arguments` is validated only as recursive JSON (`nil`, strings, booleans, finite non-ratio numbers, vectors, and maps with string or keyword keys); source-defined keys are preserved verbatim rather than kebab-cased. | | `:copilot/assistant.message_delta` | Streaming response chunk | | `:copilot/assistant.streaming_delta` | Response size update during streaming; data: `{:total-response-size-bytes N}` | | `:copilot/assistant.turn_end` | Assistant turn completed | @@ -1880,20 +1881,20 @@ nested schema objects marked closed by upstream reject unknown keys. | `:copilot/model.call_finished` | Completed model dispatch metadata; data requires `:turn-id`, non-negative `:dispatch-duration-ms`, `:outcome` (`"success"`, `"error"`, `"cancelled"`, or `"rejected"`), and positive `:edit-classifier-version`. Optional fields: `:interaction-id` and `:contains-built-in-file-edit-request`. The payload remains open for additive runtime fields. | | `:copilot/abort` | Current message aborted | | `:copilot/tool.user_requested` | Tool execution requested by user | -| `:copilot/tool.execution_start` | Tool execution started; data includes `:tool-call-id`, `:tool-name`, optional `:arguments` (an opaque JSON object with source-defined, non-kebab-cased keys), `:parent-tool-call-id`, `:mcp-server-name`, `:mcp-tool-name`, optional `:mcp-transport` (`"stdio"`, `"http"`, `"sse"`, or `"memory"`), and `:model` (runtime schema `1.0.84-5`; [upstream PR #2634](https://github.com/github/copilot-sdk/pull/2634)) | +| `:copilot/tool.execution_start` | Tool execution started; data includes `:tool-call-id`, `:tool-name`, optional `:arguments` (an opaque JSON object with source-defined, non-kebab-cased keys), `:parent-tool-call-id`, `:mcp-server-name`, `:mcp-tool-name`, `:mcp-config-server-name`, `:mcp-config-source` (`"user"`, `"workspace"`, `"plugin"`, `"builtin"`, or `"managed"`), `:mcp-transport` (`"stdio"`, `"http"`, `"sse"`, or `"memory"`), and `:model`. Transport metadata was added in runtime schema `1.0.84-5`; configured-server provenance was added by [upstream PR #2658](https://github.com/github/copilot-sdk/pull/2658) and is pinned through runtime schema `1.0.84-8`. | | `:copilot/tool.execution_progress` | Tool execution progress update | | `:copilot/tool.execution_partial_result` | Tool execution partial result | -| `:copilot/tool.execution_complete` | Tool execution completed; data may include optional `:structured-content` (arbitrary structured tool result) (upstream schema 1.0.63) and `:result` (recursive opaque JSON). An error may include `:message`, `:code`, and the same `:remediation` values as `session.error`. Generated wire validation still enforces known result variants, including the shell-exit variant's `:exit-code`/`:shell-id`/`:type "shell_exit"` and optional `:cwd`/`:output-file-path`/`:output-preview`/`:output-truncated`; `:output-file-path` was added in upstream schema 1.0.83-1. | +| `:copilot/tool.execution_complete` | Tool execution completed; data may include optional `:structured-content` (arbitrary structured tool result) (upstream schema 1.0.63) and `:result` (recursive opaque JSON). An error may include `:message`, `:code`, and the same `:remediation` values as `session.error`. Generated wire validation still enforces known result variants, including the shell-exit variant's `:exit-code`/`:shell-id`/`:type "shell_exit"` and optional `:cwd`/`:output-file-path`/`:output-preview`/`:output-truncated`; `:output-file-path` was added in upstream schema 1.0.83-1. Successful skill invocations may keep concise model-facing `:detailed-content`; the authoritative skill body remains on the corresponding `:copilot/skill.invoked` event. | | `:copilot/tool_search.activated` | Persisted generic client-side tool activations restored when a session resumes. Data: `{:strategy :tool-names [ ...]}`. | -| `:copilot/subagent.started` | Subagent started; data includes `:tool-call-id`, `:agent-name`, `:agent-display-name`, and `:agent-description`, with optional `:factory-run-id`, `:model`, `:resumable` (boolean), `:agent-type` (string), `:execution-mode` (string), `:parent-id` (string — task-registry id of the spawning subagent; unrelated to the envelope-level `:parent-id`), and `:task-model-source` (`"task_argument"`, `"subagent_configuration"`, `"custom_agent_definition"`, or `"unset"`). ([upstream PR #2072](https://github.com/github/copilot-sdk/pull/2072); runtime schema `1.0.84-4`) | +| `:copilot/subagent.started` | Subagent started; data includes `:tool-call-id`, `:agent-name`, `:agent-display-name`, and `:agent-description`, with optional `:factory-run-id`, `:model`, `:resumable` (boolean), `:agent-type` (string), `:execution-mode` (string), `:parent-id` (string — task-registry id of the spawning subagent; unrelated to the envelope-level `:parent-id`), `:task-model-source` (`"task_argument"`, `"subagent_configuration"`, `"custom_agent_definition"`, or `"unset"`), and `:model-selection-source` using the same values as `subagent.completed`. ([upstream PR #2658](https://github.com/github/copilot-sdk/pull/2658); runtime schema `1.0.84-8`) | | `:copilot/subagent.configured` | Effective subagent execution configuration; data requires string `:model` and boolean `:multi-turn`, with optional string `:reasoning-effort` and `:context-tier`. The payload remains open for additive runtime fields. | | `:copilot/subagent.completed` | Subagent completed; data includes `:tool-call-id`, `:agent-name`, `:agent-display-name`, and optional `:cancelled`, `:model`, `:total-tool-calls`, `:total-tokens`, `:duration-ms`, `:first-dispatched-model`, `:configured-model-preference`, `:explicit-model-override`, `:model-override-reason` (strings), `:explicit-model-matches-preference`, `:configured-model-matches-actual` (booleans), and `:model-selection-source` (`"explicit_override"`, `"configured_required"`, `"configured_preference"`, `"complementary_default"`, `"session_inheritance"`, `"agent_definition_default"`, or `"runtime_policy"`). `:cancelled true` means cancellation tore down the subagent; cancellation still reports completion rather than failure. | | `:copilot/subagent.failed` | Subagent failed; data includes `:tool-call-id`, `:agent-name`, `:agent-display-name`, `:error`, optional `:model`, `:total-tool-calls`, `:total-tokens`, `:duration-ms`, `:first-dispatched-model`, `:configured-model-preference`, `:explicit-model-override`, `:model-override-reason` (strings), `:explicit-model-matches-preference`, `:configured-model-matches-actual` (booleans), and the same `:model-selection-source` values as `subagent.completed`. | | `:copilot/subagent.selected` | Subagent selected | | `:copilot/subagent.deselected` | Subagent deselected | -| `:copilot/hook.start` | Hook invocation started; data requires `:hook-invocation-id`, `:hook-type`, with optional `:parent-tool-call-id` (upstream schema 1.0.83-1) | +| `:copilot/hook.start` | Hook invocation started; data requires `:hook-invocation-id`, `:hook-type`, with optional `:parent-tool-call-id` (upstream schema 1.0.83-1). Durable or resumed `postToolUse` copies may elide duplicated tool and successful skill payloads; canonical output remains on the adjacent tool and skill events. | | `:copilot/hook.progress` | Ephemeral progress update from a long-running hook; data: `{:message "..."}` (upstream schema 1.0.56). | -| `:copilot/hook.end` | Hook invocation finished; data requires `:hook-invocation-id`, `:hook-type`, and `:success`, with optional `:parent-tool-call-id` and closed `:error` map. The error requires string `:message`, permits optional string `:stack` and `:source`, and rejects other keys (upstream schema 1.0.83-1). | +| `:copilot/hook.end` | Hook invocation finished; data requires `:hook-invocation-id`, `:hook-type`, and `:success`, with optional `:parent-tool-call-id` and closed `:error` map. The error requires string `:message`, permits optional string `:stack` and `:source`, and rejects other keys (upstream schema 1.0.83-1). Durable or resumed `postToolUse` receipts may elide unchanged successful skill output while preserving hook-modified values. | | `:copilot/system.message` | System message emitted | | `:copilot/system.notification` | System notification with a structured `:kind` discriminator: `agent_completed`, `agent_idle`, `new_inbox_message`, `shell_completed`, `shell_detached_completed`, `instruction_discovered`, `factory_completed`, or `unclassified`. Each known kind validates its required and optional fields; agent kinds may include `:display-name`. | | `:copilot/permission.requested` | Permission request initiated; optional `:agent-mode` identifies the requesting mode (`"interactive"`, `"plan"`, or `"autopilot"`), and `:resolved-by-hook` indicates a hook already handled it. For the MCP tool-permission variant (`:server-name`/`:tool-name`/`:tool-title` present), optional `:can-offer-server-wide-approval` indicates the host may offer a server-wide approval option. Shell requests may include `:request-sandbox-bypass`, `:request-sandbox-bypass-reason`, and `:request-sandbox-permissive`; the permissive form remains sandboxed while recording otherwise-blocked file and process access. | @@ -1906,8 +1907,8 @@ nested schema objects marked closed by upstream reject unknown keys. | `:copilot/external_tool.completed` | External tool call completed (v3) | | `:copilot/mcp.oauth_required` | MCP server requires OAuth authentication | | `:copilot/mcp.oauth_completed` | MCP OAuth authentication completed | -| `:copilot/mcp.headers_refresh_required` | Dynamic headers refresh request for a remote MCP server (upstream schema 1.0.66) | -| `:copilot/mcp.headers_refresh_completed` | MCP headers refresh request completed (upstream schema 1.0.66) | +| `:copilot/mcp.headers_refresh_required` | Dynamic headers refresh request for a remote MCP server. Data requires `:request-id`, `:server-name`, `:server-url`, and `:reason` (`"auth-failed"`, `"startup"`, or `"ttl-expired"`). | +| `:copilot/mcp.headers_refresh_completed` | MCP headers refresh request completed. Data requires `:request-id` and `:outcome` (`"headers"`, `"none"`, `"error"`, or `"timeout"`); `"error"` was added in runtime schema `1.0.84-6`. | | `:copilot/mcp.tools.list_changed` | Remote MCP server signalled its tool list changed; data includes `:server-name` (upstream schema 1.0.70) | | `:copilot/mcp.resources.list_changed` | Remote MCP server signalled its resource list changed; data includes `:server-name` (upstream schema 1.0.70) | | `:copilot/mcp.prompts.list_changed` | Remote MCP server signalled its prompt list changed; data includes `:server-name` (upstream schema 1.0.70) | @@ -1922,7 +1923,7 @@ nested schema objects marked closed by upstream reject unknown keys. | `:copilot/session.tools_updated` | Session tools list updated (e.g., after model change) | | `:copilot/session.background_tasks_changed` | Background tasks status changed | | `:copilot/session.skills_loaded` | Skills loaded for the session | -| `:copilot/session.mcp_servers_loaded` | MCP servers loaded for the session. Each server may include `:source`, plugin identity, `:error`, and `:server-metadata {:instructions }`. | +| `:copilot/session.mcp_servers_loaded` | MCP servers loaded for the session. Each server may include a managed-catalog `:display-name`, `:source` (`"user"`, `"workspace"`, `"plugin"`, `"builtin"`, or `"managed"`), plugin identity, `:error`, and `:server-metadata {:instructions }`. | | `:copilot/session.mcp_server_status_changed` | MCP server status changed | | `:copilot/session.mcp_server_removed` | MCP server was removed; data: `{:server-name "..."}` | | `:copilot/session.mcp_server_needs_reconnect` | MCP server requires reconnection; data: `{:server-name "..."}` | @@ -2890,7 +2891,7 @@ and an async `<`-prefixed twin returning a core.async channel: | `list-runs` | `list-factory-runs` | `session.factory.listRuns` | List all durable runs for the session, in creation order. `[session]`. | | `get-run-detail` | `get-factory-run-detail` | `session.factory.getRunDetail` | Read durable phases, agent turns, and recent progress for a run. `[session run-id]`. | | `get-run-progress` | `get-factory-run-progress` | `session.factory.getRunProgress` | Page durable progress lines. 2-arity or 3-arity with an options map merged into the wire params (e.g. pagination cursors). | -| `cancel!` | `cancel-factory-run!` | `session.factory.cancel` | Request cancellation from the CLI. The runtime's reverse `factory.abort` request then marks active local executions cancelled and closes their `:cancel-chan`. Returns the resulting terminal envelope. `[session run-id]`. | +| `cancel!` | `cancel-factory-run!` | `session.factory.cancel` | Request cancellation from the CLI. The runtime's reverse `factory.abort` request targets the active execution token, marks only that attempt cancelled, and closes its `:cancel-chan`. Returns the resulting terminal envelope. `[session run-id]`. | All of the above return a run envelope map with at least `:run-id` and a keywordized `:status` (one of `:running`, `:completed`, `:halted`, `:cancelled`, `:error`, or other diff --git a/resources/github/copilot_sdk/api_surface.edn b/resources/github/copilot_sdk/api_surface.edn index 313cb02..81c5f37 100644 --- a/resources/github/copilot_sdk/api_surface.edn +++ b/resources/github/copilot_sdk/api_surface.edn @@ -290,7 +290,7 @@ handle-command-execute! {:kind :fn, :arglists ([client session-id {:keys [command-name command args]}])} handle-elicitation-request! {:kind :fn, :arglists ([client session-id context])} handle-exit-plan-mode-request! {:kind :fn, :arglists ([client session-id request])} - handle-factory-abort! {:kind :fn, :arglists ([client session-id run-id])} + handle-factory-abort! {:kind :fn, :arglists ([client session-id run-id] [client session-id run-id execution-token])} handle-factory-execute! {:kind :fn, :arglists ([client session-id {:as params, :keys [name run-id execution-token]}])} handle-hooks-invoke! {:kind :fn, :arglists ([client session-id hook-type input])} handle-mcp-auth-request! {:kind :fn, :arglists ([client session-id request])} @@ -792,8 +792,12 @@ :github.copilot-sdk.specs/max-total-subagents :github.copilot-sdk.specs/mcp-args :github.copilot-sdk.specs/mcp-command + :github.copilot-sdk.specs/mcp-config-server-name + :github.copilot-sdk.specs/mcp-config-source :github.copilot-sdk.specs/mcp-defer-tools :github.copilot-sdk.specs/mcp-headers + :github.copilot-sdk.specs/mcp-headers-refresh-completed-outcome + :github.copilot-sdk.specs/mcp-headers-refresh-required-reason :github.copilot-sdk.specs/mcp-http-server :github.copilot-sdk.specs/mcp-loaded-server :github.copilot-sdk.specs/mcp-local-server @@ -810,6 +814,8 @@ :github.copilot-sdk.specs/mcp-tools :github.copilot-sdk.specs/mcp-transport :github.copilot-sdk.specs/mcp-url + :github.copilot-sdk.specs/mcp.headers_refresh_completed-data + :github.copilot-sdk.specs/mcp.headers_refresh_required-data :github.copilot-sdk.specs/memory :github.copilot-sdk.specs/memory-action :github.copilot-sdk.specs/memory-direction @@ -879,6 +885,7 @@ :github.copilot-sdk.specs/operation :github.copilot-sdk.specs/options :github.copilot-sdk.specs/organization-custom-instructions + :github.copilot-sdk.specs/originating-message-id :github.copilot-sdk.specs/otlp-endpoint :github.copilot-sdk.specs/otlp-protocol :github.copilot-sdk.specs/outcome diff --git a/schemas/README.md b/schemas/README.md index f1eeb73..d695f47 100644 --- a/schemas/README.md +++ b/schemas/README.md @@ -4,4 +4,4 @@ These files are fetched verbatim from the `@github/copilot-linux-x64` npm packag **Do not edit by hand.** To update, run `bb schemas:fetch` after bumping `.copilot-schema-version`. -Currently pinned version: `1.0.84-5` +Currently pinned version: `1.0.84-8` diff --git a/schemas/api.schema.json b/schemas/api.schema.json index 5ee9ebe..fe16cfa 100644 --- a/schemas/api.schema.json +++ b/schemas/api.schema.json @@ -1144,25 +1144,9 @@ }, "stability": "experimental" }, - "registerExtensionToolsOnSession": { - "rpcMethod": "sessions.registerExtensionToolsOnSession", - "description": "Registers extension-provided tools on the given session, gated by an optional `enabled` callback. Returns an opaque unsubscribe function the caller must invoke to deregister the tools when the extension is torn down. Marked internal because `loader`, `enabled`, and the returned `unsubscribe` are in-process handles that cannot cross the JSON-RPC boundary. Disappears once extension discovery / launch / tool registration are owned by the runtime: SDK consumers will pass pure config (search paths, disabled ids) via `SessionOptions` and the runtime will resolve, launch, register, and tear down extensions itself.", - "params": { - "$ref": "#/definitions/RegisterExtensionToolsParams", - "description": "Params to attach an extension loader's tools to a session.", - "visibility": "internal" - }, - "result": { - "$ref": "#/definitions/RegisterExtensionToolsResult", - "description": "Handle for releasing the extension tool registration.", - "visibility": "internal" - }, - "stability": "experimental", - "visibility": "internal" - }, "configureSessionExtensions": { "rpcMethod": "sessions.configureSessionExtensions", - "description": "Attaches (or detaches) an in-process ExtensionController delegate for the given session, used by shared-API surfaces that need to query or modify the session's extension state. Pass `controller: undefined` to detach. Marked internal because the controller is an in-process object that cannot cross the JSON-RPC boundary. Disappears alongside `registerExtensionToolsOnSession`: once the runtime owns extension management, the public surface exposes list/enable/disable/reload as dedicated RPCs served by the runtime.", + "description": "Attaches (or detaches) an in-process ExtensionController delegate for the given session in a local host adapter. Pass `controller: undefined` to detach. Internal because the controller cannot cross the JSON-RPC boundary; the runtime manages its own session extension service.", "params": { "$ref": "#/definitions/ConfigureSessionExtensionsParams", "description": "Params to attach or detach an in-process ExtensionController delegate.", @@ -1273,6 +1257,10 @@ }, "description": "Custom HTTP headers to include in outbound model requests for this turn. Merged with session-level provider headers; per-turn headers augment and overwrite session-level headers with the same key." }, + "responseFormat": { + "$ref": "#/definitions/ResponseFormat", + "description": "Provider-native output format for this turn, including all tool-call iterations. Not inherited by later turns or subagents. Ordinary steering inherits the active format; specifying responseFormat with mode: immediate is an error, even while idle. Returned assistant content remains text; the runtime does not parse or validate it. Unsupported models or schemas produce provider errors." + }, "traceparent": { "type": "string", "description": "W3C Trace Context traceparent header for distributed tracing of this agent turn" @@ -1317,7 +1305,7 @@ "$ref": "#/definitions/SendMessageItem", "description": "A single user message to append to the session as part of a `session.sendMessages` turn" }, - "description": "The user messages to append to the conversation, in order. May be empty, in which case a single turn runs over the existing history with no new user message." + "description": "The user messages to append to the conversation, in order, before running one agent loop. When the batch starts a run, its final message is the primary initiating message; earlier messages provide context, not separate runs or replies. May be empty, in which case a single turn runs over the existing history with no new user message or originatingMessageId." }, "mode": { "$ref": "#/definitions/SendMode", @@ -1338,6 +1326,10 @@ }, "description": "Custom HTTP headers to include in outbound model requests for this turn. Merged with session-level provider headers; per-turn headers augment and overwrite session-level headers with the same key." }, + "responseFormat": { + "$ref": "#/definitions/ResponseFormat", + "description": "Provider-native output format for the whole turn, including an empty message batch and all tool-call iterations. Not inherited by later turns or subagents. Ordinary steering inherits the active format; specifying responseFormat with mode: immediate is an error, even while idle. Returned assistant content remains text; the runtime does not parse or validate it. Unsupported models or schemas produce provider errors." + }, "traceparent": { "type": "string", "description": "W3C Trace Context traceparent header for distributed tracing of this agent turn" @@ -1561,6 +1553,10 @@ "reason": { "type": "string", "description": "Optional human-readable reason. Typically the message of the error that triggered shutdown when type is 'error'." + }, + "detachSessionEndHooks": { + "type": "boolean", + "description": "Dispatch deferred sessionEnd hooks in the background with their full per-hook timeoutSec instead of awaiting them under the short shared shutdown budget. Set this when the host process keeps running after the session closes (for example the CLI's /clear), so a slow hook neither blocks the close nor is aborted. Hooks still detached when the process later exits are terminated with it. Defaults to false." } }, "required": [ @@ -1867,7 +1863,7 @@ "debug": { "collectLogs": { "rpcMethod": "session.debug.collectLogs", - "description": "Collects a redacted session debug log bundle into a local archive or staging directory. The runtime includes session-owned logs by default and accepts caller-provided diagnostic entries so host applications can add their own files without changing this API shape.", + "description": "Collects a session debug log bundle into a local archive or staging directory. Logs are redacted by default; redaction can be configured per caller-provided diagnostic entry. The runtime includes session-owned logs by default and accepts caller-provided diagnostic entries so host applications can add their own files without changing this API shape.", "params": { "type": "object", "properties": { @@ -1877,7 +1873,7 @@ }, "destination": { "$ref": "#/definitions/DebugCollectLogsDestination", - "description": "Where the redacted bundle should be written. Use `archive` to produce a .tgz, or `directory` to stage redacted files for caller-managed upload/post-processing." + "description": "Where the bundle should be written. Use `archive` to produce a .tgz, or `directory` to stage files for caller-managed upload/post-processing." }, "include": { "$ref": "#/definitions/DebugCollectLogsInclude", @@ -1897,13 +1893,13 @@ "destination" ], "additionalProperties": false, - "description": "Options for collecting a redacted session debug bundle.", + "description": "Options for collecting a session debug bundle with configurable redaction.", "title": "DebugCollectLogsRequest", "stability": "experimental" }, "result": { "$ref": "#/definitions/DebugCollectLogsResult", - "description": "Result of collecting a redacted debug bundle." + "description": "Result of collecting a session debug bundle." }, "stability": "experimental" } @@ -3492,7 +3488,7 @@ }, "path": { "type": "string", - "description": "Relative path within the workspace files directory" + "description": "Slash-separated relative path within the workspace files directory" } }, "required": [ @@ -3522,7 +3518,7 @@ }, "path": { "type": "string", - "description": "Relative path within the workspace files directory" + "description": "Slash-separated relative path within the workspace files directory" }, "content": { "type": "string", @@ -3544,6 +3540,140 @@ }, "stability": "experimental" }, + "statFile": { + "rpcMethod": "session.workspaces.statFile", + "description": "Returns metadata for a file or directory in the session workspace files directory.", + "params": { + "type": "object", + "properties": { + "sessionId": { + "type": "string", + "description": "Target session identifier" + }, + "path": { + "type": "string", + "description": "Slash-separated relative path within the workspace files directory" + } + }, + "required": [ + "sessionId", + "path" + ], + "additionalProperties": false, + "description": "Relative path of the workspace file or directory to inspect.", + "title": "WorkspacesStatFileRequest", + "stability": "experimental" + }, + "result": { + "$ref": "#/definitions/WorkspacesStatFileResult", + "description": "Filesystem metadata for a path in the session workspace files directory." + }, + "stability": "experimental" + }, + "createDirectory": { + "rpcMethod": "session.workspaces.createDirectory", + "description": "Creates a directory in the session workspace files directory.", + "params": { + "type": "object", + "properties": { + "sessionId": { + "type": "string", + "description": "Target session identifier" + }, + "path": { + "type": "string", + "description": "Slash-separated relative path within the workspace files directory" + }, + "recursive": { + "type": "boolean", + "description": "Whether to create missing parent directories. Defaults to false." + } + }, + "required": [ + "sessionId", + "path" + ], + "additionalProperties": false, + "description": "Directory to create within the session workspace files directory.", + "title": "WorkspacesCreateDirectoryRequest", + "stability": "experimental" + }, + "result": { + "type": "null" + }, + "stability": "experimental" + }, + "removePath": { + "rpcMethod": "session.workspaces.removePath", + "description": "Removes a file or directory from the session workspace files directory.", + "params": { + "type": "object", + "properties": { + "sessionId": { + "type": "string", + "description": "Target session identifier" + }, + "path": { + "type": "string", + "description": "Slash-separated relative path within the workspace files directory" + }, + "recursive": { + "type": "boolean", + "description": "Whether to remove directory contents recursively. Defaults to false." + }, + "force": { + "type": "boolean", + "description": "Whether a missing path should be treated as success. Defaults to false." + } + }, + "required": [ + "sessionId", + "path" + ], + "additionalProperties": false, + "description": "File or directory to remove from the session workspace files directory.", + "title": "WorkspacesRemovePathRequest", + "stability": "experimental" + }, + "result": { + "type": "null" + }, + "stability": "experimental" + }, + "renamePath": { + "rpcMethod": "session.workspaces.renamePath", + "description": "Renames a file or directory within the session workspace files directory.", + "params": { + "type": "object", + "properties": { + "sessionId": { + "type": "string", + "description": "Target session identifier" + }, + "source": { + "type": "string", + "description": "Slash-separated source path relative to the workspace files directory" + }, + "destination": { + "type": "string", + "description": "Slash-separated destination path relative to the workspace files directory" + } + }, + "required": [ + "sessionId", + "source", + "destination" + ], + "additionalProperties": false, + "description": "Source and destination paths for a rename within the session workspace files directory.", + "title": "WorkspacesRenamePathRequest", + "stability": "experimental" + }, + "result": { + "type": "null" + }, + "stability": "experimental" + }, "listCheckpoints": { "rpcMethod": "session.workspaces.listCheckpoints", "description": "Lists workspace checkpoints in chronological order.", @@ -11469,14 +11599,16 @@ "enum": [ "unsupported", "optional", - "required" + "required", + "adaptive_only" ], "description": "Resolved Anthropic adaptive-thinking capability for a model.", "title": "AdaptiveThinkingSupport", "x-enumDescriptions": { "unsupported": "The model does not accept thinking.type='adaptive'", "optional": "The model accepts adaptive thinking but also accepts thinking.type='enabled'", - "required": "The model only accepts adaptive thinking and rejects thinking.type='enabled' with HTTP 400 (e.g. opus-4.7/4.8)" + "required": "The model defaults to adaptive thinking and rejects thinking.type='enabled' with HTTP 400, but still accepts thinking.type='disabled' (e.g. opus-4.7/4.8/5, sonnet-5)", + "adaptive_only": "The model accepts only thinking.type='adaptive'; 'enabled', 'disabled', and an omitted thinking block all fail with HTTP 400 (e.g. fable, mythos)" } }, "AgentDiscoveryPath": { @@ -16840,7 +16972,7 @@ "sizeBytes" ], "additionalProperties": false, - "description": "A file included in the redacted debug bundle.", + "description": "A file included in the session debug bundle.", "title": "DebugCollectLogsCollectedEntry" }, "DebugCollectLogsDestination": { @@ -16873,7 +17005,7 @@ "properties": { "outputDirectory": { "type": "string", - "description": "Directory where redacted files should be staged. The directory is created if needed." + "description": "Directory where files should be staged. The directory is created if needed." }, "kind": { "type": "string", @@ -16888,7 +17020,7 @@ "additionalProperties": false } ], - "description": "Destination for the redacted debug bundle.", + "description": "Destination for the session debug bundle.", "title": "DebugCollectLogsDestination" }, "DebugCollectLogsEntry": { @@ -16908,7 +17040,7 @@ }, "redaction": { "$ref": "#/definitions/DebugCollectLogsRedaction", - "description": "How text content from this entry should be redacted. Defaults to plain-text." + "description": "How text content from this entry should be redacted. Defaults to plain-text. With none, no redaction is applied; the caller must ensure any necessary redaction is performed before this call." }, "required": { "type": "boolean", @@ -16978,13 +17110,15 @@ "type": "string", "enum": [ "plain-text", - "events-jsonl" + "events-jsonl", + "none" ], "description": "How a collected debug entry should be redacted before being staged.", "title": "DebugCollectLogsRedaction", "x-enumDescriptions": { "plain-text": "Redact the file as plain UTF-8 log text.", - "events-jsonl": "Redact each non-empty line as a session event JSON object, falling back to plain-text redaction for malformed lines." + "events-jsonl": "Redact each non-empty line as a session event JSON object, falling back to plain-text redaction for malformed lines.", + "none": "No redaction is applied. The caller must ensure any necessary redaction is performed before this call." } }, "DebugCollectLogsRequest": { @@ -16992,7 +17126,7 @@ "properties": { "destination": { "$ref": "#/definitions/DebugCollectLogsDestination", - "description": "Where the redacted bundle should be written. Use `archive` to produce a .tgz, or `directory` to stage redacted files for caller-managed upload/post-processing." + "description": "Where the bundle should be written. Use `archive` to produce a .tgz, or `directory` to stage files for caller-managed upload/post-processing." }, "include": { "$ref": "#/definitions/DebugCollectLogsInclude", @@ -17011,7 +17145,7 @@ "destination" ], "additionalProperties": false, - "description": "Options for collecting a redacted session debug bundle.", + "description": "Options for collecting a session debug bundle with configurable redaction.", "title": "DebugCollectLogsRequest", "stability": "experimental" }, @@ -17030,9 +17164,9 @@ "type": "array", "items": { "$ref": "#/definitions/DebugCollectLogsCollectedEntry", - "description": "A file included in the redacted debug bundle." + "description": "A file included in the session debug bundle." }, - "description": "Files included in the redacted bundle." + "description": "Files included in the bundle." }, "skippedEntries": { "type": "array", @@ -17049,7 +17183,7 @@ "entries" ], "additionalProperties": false, - "description": "Result of collecting a redacted debug bundle.", + "description": "Result of collecting a session debug bundle.", "title": "DebugCollectLogsResult" }, "DebugCollectLogsResultKind": { @@ -17062,7 +17196,7 @@ "title": "DebugCollectLogsResultKind", "x-enumDescriptions": { "archive": "A .tgz archive was written.", - "directory": "A directory containing redacted files was written." + "directory": "A directory containing the collected files was written." } }, "DebugCollectLogsSkippedEntry": { @@ -21361,6 +21495,10 @@ "installedFrom": { "type": "string", "description": "Absolute path of the marketplace directory a live plugin was resolved from. Present only on live, never-persisted records — a plugin belonging to a directory/local marketplace, which is loaded from its real directory on every pass instead of a copy under the installed-plugins cache. Its presence is what marks a listed plugin as live: such a plugin is always present on disk, so `enabled` is its only meaningful state and it is never \"not installed\"." + }, + "source": { + "type": "string", + "description": "Runtime-reported plugin provenance. Currently set to \"builtin\" only for plugins registered through the trusted host built-in boundary; absent for installed, marketplace, direct, and live plugins." } }, "required": [ @@ -21754,6 +21892,34 @@ "description": "Result of interrupting the main agent turn.", "title": "InterruptMainTurnResult" }, + "JsonSchemaResponseFormat": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Name of the output schema, subject to the provider's naming restrictions." + }, + "schema": { + "description": "JSON Schema passed unchanged to the inference provider. Schemas larger than 32 MiB when JSON-encoded are rejected before admission, using the runtime's existing request-size ceiling. This is not a guarantee that the entire model request fits. Supported keywords and schema restrictions are determined by the provider.", + "x-opaque-json": true + }, + "description": { + "type": "string", + "description": "Optional description passed to OpenAI providers." + }, + "strict": { + "type": "boolean", + "description": "Optional strict enforcement setting for OpenAI providers. Omitted uses the provider default. Anthropic always enforces its supported schema subset." + } + }, + "required": [ + "name", + "schema" + ], + "additionalProperties": false, + "description": "A JSON Schema output contract. OpenAI receives the name, description, schema and strict setting; Anthropic receives the schema in output_config.format and always uses its native strict enforcement.", + "title": "JsonSchemaResponseFormat" + }, "LlmInferenceHeaders": { "type": "object", "additionalProperties": { @@ -22140,6 +22306,45 @@ "title": "LspInitializeRequest", "stability": "experimental" }, + "ManagedMcpServerConfig": { + "type": "object", + "properties": { + "displayName": { + "type": "string", + "description": "Human-readable catalog display name." + }, + "url": { + "type": "string", + "format": "uri", + "description": "Hosted MCP streamable HTTP endpoint." + }, + "tools": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Tools to include. Defaults to all tools when omitted." + }, + "timeout": { + "type": "integer", + "exclusiveMinimum": 0, + "format": "duration", + "description": "Timeout in milliseconds for tool discovery and tool calls." + }, + "headersRefreshTtlMs": { + "type": "integer", + "minimum": 0, + "description": "Maximum dynamic-header cache lifetime in milliseconds." + } + }, + "required": [ + "displayName", + "url" + ], + "additionalProperties": false, + "description": "Non-secret host-managed HTTP MCP server configuration. The containing map key is the stable managed identity; credentials are supplied dynamically by the host.", + "title": "ManagedMcpServerConfig" + }, "ManagedSettingsReadResult": { "type": "object", "properties": { @@ -23196,6 +23401,11 @@ }, "description": "Headers to overlay onto the MCP request. Dynamic headers override static config headers but do not replace SDK-managed request headers." }, + "ttlMs": { + "type": "integer", + "minimum": 0, + "description": "Optional lifetime in milliseconds for these returned headers. The runtime clamps its configured cache lifetime to this value." + }, "kind": { "type": "string", "const": "headers", @@ -23219,6 +23429,24 @@ "required": [ "kind" ] + }, + { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Host credential broker failure, denial, or revocation reason." + }, + "kind": { + "type": "string", + "const": "error", + "description": "Headers-refresh response variant discriminator." + } + }, + "required": [ + "kind", + "message" + ] } ], "description": "Host response: supply dynamic headers or decline this refresh.", @@ -25224,7 +25452,7 @@ }, "source": { "$ref": "#/definitions/McpServerSource", - "description": "Configuration source: user, workspace, plugin, or builtin" + "description": "Configuration source: user, workspace, plugin, builtin, or managed" }, "sourcePlugin": { "type": "string", @@ -25234,6 +25462,10 @@ "type": "string", "description": "Plugin version that provided this server, when source is plugin." }, + "displayName": { + "type": "string", + "description": "Human-readable display name supplied by a managed server catalog." + }, "error": { "type": "string", "description": "Error message if the server failed to connect" @@ -25937,15 +26169,17 @@ "user", "workspace", "plugin", - "builtin" + "builtin", + "managed" ], - "description": "Configuration source: user, workspace, plugin, or builtin", + "description": "Configuration source: user, workspace, plugin, builtin, or managed", "title": "McpServerSource", "x-enumDescriptions": { "user": "Server configured in the user's global MCP configuration.", "workspace": "Server configured by the current workspace.", "plugin": "Server contributed by an installed plugin.", - "builtin": "Server bundled with the runtime." + "builtin": "Server bundled with the runtime.", + "managed": "Server supplied by a trusted host-managed catalog." } }, "McpServerStatus": { @@ -27031,7 +27265,7 @@ }, "adaptive_thinking": { "$ref": "#/definitions/AdaptiveThinkingSupport", - "description": "Resolved Anthropic adaptive-thinking capability — unsupported / optional / required. 'required' models reject thinking.type='enabled' with HTTP 400 (e.g. opus-4.7/4.8)." + "description": "Resolved Anthropic adaptive-thinking capability — unsupported / optional / required / adaptive_only. 'required' models reject thinking.type='enabled' with HTTP 400 but still accept 'disabled' (e.g. opus-4.7/4.8/5, sonnet-5); 'adaptive_only' models accept nothing but 'adaptive' (e.g. fable, mythos)." } }, "additionalProperties": false, @@ -27051,7 +27285,7 @@ }, "adaptive_thinking": { "$ref": "#/definitions/AdaptiveThinkingSupport", - "description": "Resolved Anthropic adaptive-thinking capability — unsupported / optional / required. 'required' models reject thinking.type='enabled' with HTTP 400 (e.g. opus-4.7/4.8)." + "description": "Resolved Anthropic adaptive-thinking capability — unsupported / optional / required / adaptive_only. 'required' models reject thinking.type='enabled' with HTTP 400 but still accept 'disabled' (e.g. opus-4.7/4.8/5, sonnet-5); 'adaptive_only' models accept nothing but 'adaptive' (e.g. fable, mythos)." } }, "additionalProperties": false, @@ -29351,6 +29585,7 @@ "slash_command", "autopilot_confirmation", "user_setting", + "organization_targeting", "rpc" ], "description": "Optional source for permission-mode telemetry. Defaults to `rpc` when omitted for SDK callers.", @@ -29360,6 +29595,7 @@ "slash_command": "The mode was set by a slash command.", "autopilot_confirmation": "The mode was set by confirming autopilot behavior.", "user_setting": "The mode was set at startup by the `defaultPermissionMode` user setting.", + "organization_targeting": "The mode was set at startup by authenticated organization targeting.", "rpc": "The mode was set through an RPC caller." }, "stability": "experimental" @@ -31028,6 +31264,18 @@ "description": "Result of updating a single plugin.", "title": "PluginUpdateResult" }, + "ProtocolAppendMode": { + "type": "string", + "enum": [ + "append" + ] + }, + "ProtocolCustomizeMode": { + "type": "string", + "enum": [ + "customize" + ] + }, "ProtocolExternalToolDefer": { "type": "string", "enum": [ @@ -31097,6 +31345,162 @@ "title": "ProtocolExternalToolDefinition", "stability": "experimental" }, + "ProtocolMarkerSectionOverride": { + "anyOf": [ + { + "type": "object", + "properties": { + "action": { + "type": "string", + "const": "transform", + "description": "Section override action discriminator." + } + }, + "required": [ + "action" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "action": { + "type": "string", + "const": "preserve", + "description": "Section override action discriminator." + } + }, + "required": [ + "action" + ], + "additionalProperties": false + } + ] + }, + "ProtocolReplaceMode": { + "type": "string", + "enum": [ + "replace" + ] + }, + "ProtocolSectionOverride": { + "anyOf": [ + { + "$ref": "#/definitions/ProtocolStaticSectionOverride" + }, + { + "$ref": "#/definitions/ProtocolMarkerSectionOverride" + } + ] + }, + "ProtocolStaticSectionAction": { + "type": "string", + "enum": [ + "replace", + "remove", + "append", + "prepend" + ], + "x-enumDescriptions": { + "replace": "Replace the section content.", + "remove": "Remove the section content.", + "append": "Append content to the section.", + "prepend": "Prepend content to the section." + } + }, + "ProtocolStaticSectionOverride": { + "type": "object", + "properties": { + "action": { + "$ref": "#/definitions/ProtocolStaticSectionAction", + "description": "Declarative operation applied to the section." + }, + "content": { + "type": "string", + "description": "Optional content used by replace, append, and prepend operations." + } + }, + "required": [ + "action" + ], + "additionalProperties": false + }, + "ProtocolSystemMessageAppendConfig": { + "type": "object", + "properties": { + "mode": { + "$ref": "#/definitions/ProtocolAppendMode", + "description": "Append-mode discriminator. Omission also selects append mode." + }, + "content": { + "type": "string", + "description": "Text appended to the standard system prompt." + } + }, + "additionalProperties": false + }, + "ProtocolSystemMessageConfig": { + "anyOf": [ + { + "$ref": "#/definitions/ProtocolSystemMessageAppendConfig" + }, + { + "$ref": "#/definitions/ProtocolSystemMessageReplaceConfig" + }, + { + "$ref": "#/definitions/ProtocolSystemMessageCustomizeConfig" + } + ] + }, + "ProtocolSystemMessageCustomizeConfig": { + "type": "object", + "properties": { + "mode": { + "$ref": "#/definitions/ProtocolCustomizeMode", + "description": "Customize-mode discriminator." + }, + "sections": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/ProtocolSectionOverride" + }, + "description": "Named standard-prompt section overrides." + }, + "content": { + "type": "string", + "description": "Text appended after the customized sections." + } + }, + "required": [ + "mode" + ], + "additionalProperties": false + }, + "ProtocolSystemMessageReplaceConfig": { + "type": "object", + "properties": { + "mode": { + "$ref": "#/definitions/ProtocolReplaceMode", + "description": "Replace-mode discriminator." + }, + "content": { + "type": "string", + "description": "Complete replacement system-message text." + }, + "contentBlocks": { + "type": "array", + "items": { + "$ref": "#/definitions/SystemMessageBlock" + }, + "description": "Optional structured blocks corresponding to the replacement content." + } + }, + "required": [ + "mode", + "content" + ], + "additionalProperties": false + }, "ProviderAddRequest": { "type": "object", "properties": { @@ -31417,6 +31821,10 @@ "capabilities": { "$ref": "#/definitions/ModelCapabilitiesOverride", "description": "Optional capability overrides (vision, tool_calls, reasoning, etc.)." + }, + "systemMessage": { + "$ref": "#/definitions/ProtocolSystemMessageConfig", + "description": "System-message configuration used when the runtime builds the standard prompt for this provider-qualified model, including general-purpose subagents. It uses the same object hierarchy as session-level systemMessage configuration, except transform actions are rejected because the current callback protocol is not model-scoped. When present, it overrides the session-wide configuration on those prompt paths. Selected custom-agent and specialized-subagent prompts remain authoritative." } }, "required": [ @@ -32853,49 +33261,6 @@ "description": "Opaque handle representing an event-type interest registration.", "title": "RegisterEventInterestResult" }, - "RegisterExtensionToolsParams": { - "type": "object", - "properties": { - "sessionId": { - "type": "string", - "description": "Session to register extension tools on." - }, - "loader": { - "description": "In-process ExtensionLoader handle used only by the CLI and excluded from the public SDK surface.", - "visibility": "internal", - "x-opaque-in-process": true - }, - "options": { - "$ref": "#/definitions/SessionsRegisterExtensionToolsOnSessionOptions", - "description": "Optional registration options." - } - }, - "required": [ - "sessionId", - "loader" - ], - "additionalProperties": false, - "description": "Params to attach an extension loader's tools to a session.", - "title": "RegisterExtensionToolsParams", - "visibility": "internal" - }, - "RegisterExtensionToolsResult": { - "type": "object", - "properties": { - "unsubscribe": { - "description": "In-process unsubscribe function used only by the CLI.", - "visibility": "internal", - "x-opaque-in-process": true - } - }, - "required": [ - "unsubscribe" - ], - "additionalProperties": false, - "description": "Handle for releasing the extension tool registration.", - "title": "RegisterExtensionToolsResult", - "visibility": "internal" - }, "ReleaseEventInterestParams": { "type": "object", "properties": { @@ -33423,6 +33788,31 @@ "description": "Repository context for the remote session.", "title": "RemoteSessionRepository" }, + "ResponseFormat": { + "anyOf": [ + { + "type": "object", + "properties": { + "jsonSchema": { + "$ref": "#/definitions/JsonSchemaResponseFormat", + "description": "JSON Schema and provider options for the turn's output." + }, + "type": { + "type": "string", + "const": "json_schema", + "description": "Output format discriminator. Currently only json_schema is supported." + } + }, + "required": [ + "type", + "jsonSchema" + ], + "additionalProperties": false + } + ], + "description": "Provider-native structured output format. JSON Schema is forwarded without rewriting or validating the schema or the generated output.", + "title": "ResponseFormat" + }, "RunOptions": { "type": "object", "properties": { @@ -33626,6 +34016,20 @@ "SandboxConfigUserPolicyNetwork": { "type": "object", "properties": { + "allowedHosts": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Hosts allowed through the built-in sandbox proxy. A non-empty list denies unmatched hosts; an absent or empty list allows all hosts not blocked. Supports exact hostnames, IP addresses, and *.example.com for strict subdomains. Host rules do not override the outbound or local-network toggles." + }, + "blockedHosts": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Hosts denied by the built-in sandbox proxy. Deny rules take precedence over allowedHosts. A domain also denies all its subdomains. IP addresses match exactly; *.example.com matches strict subdomains, and * denies every host." + }, "allowOutbound": { "type": "boolean", "description": "Whether outbound network traffic is allowed at all." @@ -33636,7 +34040,7 @@ }, "proxy": { "$ref": "#/definitions/SandboxConfigUserPolicyNetworkProxy", - "description": "HTTP proxy for sandboxed process traffic. Linux restricts egress to the proxy endpoint, requires that endpoint to be reachable over IPv4 (the [::] dual-stack wildcard is accepted and routed through the IPv4 gateway), and does not support proxy credentials. macOS relies on applications honoring proxy environment variables. Windows also configures a per-AppContainer WinHTTP proxy, but enforcement depends on the application's networking stack. Configure supported credentials in the separate `username` and `password` fields. A credential-free http:// loopback URL uses the localhost proxy form, while an https:// or authenticated loopback URL uses the URL form." + "description": "HTTP(S) proxy for sandboxed traffic. With host rules, this is the built-in local proxy's upstream; credentials stay in the runtime, and Linux and macOS restrict the child to the local listener. Without host rules, Linux restricts egress to this endpoint but rejects credentials, and macOS proxying is cooperative. Windows enforcement depends on the application's networking stack. Configure credentials in the separate username/password fields. The transient local listener URL is never persisted." } }, "additionalProperties": false, @@ -33742,6 +34146,19 @@ "description": "Managed sandbox enforcement state for a session.", "title": "SandboxEnforcementStatus" }, + "SandboxSessionChange": { + "type": "string", + "enum": [ + "disabled", + "restored" + ], + "description": "A session-scoped sandbox transition applied while handling a slash command", + "title": "SandboxSessionChange", + "x-enumDescriptions": { + "disabled": "The sandbox is off for the rest of this session; nothing was persisted and a new session starts from managed policy.", + "restored": "A previous session-scoped opt-out was cleared and the sandbox is enforced again." + } + }, "ScheduleAddAtRequest": { "type": "object", "properties": { @@ -34139,7 +34556,7 @@ "$ref": "#/definitions/SendMessageItem", "description": "A single user message to append to the session as part of a `session.sendMessages` turn" }, - "description": "The user messages to append to the conversation, in order. May be empty, in which case a single turn runs over the existing history with no new user message." + "description": "The user messages to append to the conversation, in order, before running one agent loop. When the batch starts a run, its final message is the primary initiating message; earlier messages provide context, not separate runs or replies. May be empty, in which case a single turn runs over the existing history with no new user message or originatingMessageId." }, "mode": { "$ref": "#/definitions/SendMode", @@ -34160,6 +34577,10 @@ }, "description": "Custom HTTP headers to include in outbound model requests for this turn. Merged with session-level provider headers; per-turn headers augment and overwrite session-level headers with the same key." }, + "responseFormat": { + "$ref": "#/definitions/ResponseFormat", + "description": "Provider-native output format for the whole turn, including an empty message batch and all tool-call iterations. Not inherited by later turns or subagents. Ordinary steering inherits the active format; specifying responseFormat with mode: immediate is an error, even while idle. Returned assistant content remains text; the runtime does not parse or validate it. Unsupported models or schemas produce provider errors." + }, "traceparent": { "type": "string", "description": "W3C Trace Context traceparent header for distributed tracing of this agent turn" @@ -34189,7 +34610,7 @@ "items": { "type": "string" }, - "description": "Unique identifiers assigned to the messages, one per provided message in order. Empty when no messages were provided." + "description": "Unique identifiers assigned to the messages, one per provided message in order. For a batch that starts a run, assistant messages use the final ID as originatingMessageId throughout that run, including tool iterations and stop-hook corrections. Immediate steering does not replace the active run's origin. Empty when no messages were provided; that run has no originatingMessageId." } }, "required": [ @@ -34264,6 +34685,10 @@ }, "description": "Custom HTTP headers to include in outbound model requests for this turn. Merged with session-level provider headers; per-turn headers augment and overwrite session-level headers with the same key." }, + "responseFormat": { + "$ref": "#/definitions/ResponseFormat", + "description": "Provider-native output format for this turn, including all tool-call iterations. Not inherited by later turns or subagents. Ordinary steering inherits the active format; specifying responseFormat with mode: immediate is an error, even while idle. Returned assistant content remains text; the runtime does not parse or validate it. Unsupported models or schemas produce provider errors." + }, "traceparent": { "type": "string", "description": "W3C Trace Context traceparent header for distributed tracing of this agent turn" @@ -36392,7 +36817,7 @@ }, "enableManagedSettings": { "type": "boolean", - "description": "Opt-in: self-fetch and enforce enterprise managed settings at session bootstrap." + "description": "Opt-in: self-fetch and enforce enterprise managed settings, including managed hook policies, at session bootstrap." }, "managedSettings": { "$ref": "#/definitions/SessionManagedSettings", @@ -36550,6 +36975,15 @@ }, "description": "MCP server names disabled for this session. Disabled servers are not started or authenticated on create or cold resume." }, + "managedMcpServers": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/ManagedMcpServerConfig", + "description": "Non-secret host-managed HTTP MCP server configuration. The containing map key is the stable managed identity; credentials are supplied dynamically by the host." + }, + "description": "Non-secret host-managed HTTP MCP servers keyed by stable managed identity. Managed provenance is runtime-established from this separate field and credentials are supplied through dynamic-header refresh.", + "stability": "experimental" + }, "allowAllMcpServerInstructions": { "type": "boolean", "description": "Whether to include instructions from every MCP server in the system prompt instead of only allowlisted servers." @@ -38270,19 +38704,6 @@ "description": "Pagination options for reading an inactive or active local session's persisted event journal.", "title": "SessionsReadPersistedEventsRequest" }, - "SessionsRegisterExtensionToolsOnSessionOptions": { - "type": "object", - "properties": { - "enabled": { - "description": "In-process `() => boolean` gating callback used only by the CLI.", - "visibility": "internal", - "x-opaque-in-process": true - } - }, - "additionalProperties": false, - "description": "Optional registration options.", - "title": "SessionsRegisterExtensionToolsOnSessionOptions" - }, "SessionsReleaseLockRequest": { "type": "object", "properties": { @@ -39166,6 +39587,10 @@ "reason": { "type": "string", "description": "Optional human-readable reason. Typically the message of the error that triggered shutdown when type is 'error'." + }, + "detachSessionEndHooks": { + "type": "boolean", + "description": "Dispatch deferred sessionEnd hooks in the background with their full per-hook timeoutSec instead of awaiting them under the short shared shutdown budget. Set this when the host process keeps running after the session closes (for example the CLI's /clear), so a slow hook neither blocks the close nor is aborted. Hooks still detached when the process later exits are terminated with it. Defaults to false." } }, "additionalProperties": false, @@ -40100,6 +40525,10 @@ "runtimeSettingsChanged": { "type": "boolean", "description": "True when the invocation mutated user runtime settings; consumers caching settings should refresh" + }, + "sandboxSessionChange": { + "$ref": "#/definitions/SandboxSessionChange", + "description": "Present when the invocation changed the sandbox for this session only. Nothing was persisted, so consumers must mirror the change onto the live session rather than reloading settings, and must not treat it as a settings change." } }, "required": [ @@ -40222,6 +40651,23 @@ "long_context": "Pin the subagent to the long-context tier when supported." } }, + "SystemMessageBlock": { + "type": "object", + "properties": { + "content": { + "type": "string", + "description": "Text content for this system-message block." + }, + "isStatic": { + "type": "boolean", + "description": "Whether the block is static and may be cached independently of dynamic prompt content." + } + }, + "required": [ + "content" + ], + "additionalProperties": false + }, "TaskAgentInfo": { "type": "object", "properties": { @@ -43908,12 +44354,32 @@ "description": "Workspace checkpoint metadata with assigned number, human-readable title, and checkpoint filename.", "title": "WorkspacesCheckpoints" }, + "WorkspacesCreateDirectoryRequest": { + "type": "object", + "properties": { + "path": { + "type": "string", + "description": "Slash-separated relative path within the workspace files directory" + }, + "recursive": { + "type": "boolean", + "description": "Whether to create missing parent directories. Defaults to false." + } + }, + "required": [ + "path" + ], + "additionalProperties": false, + "description": "Directory to create within the session workspace files directory.", + "title": "WorkspacesCreateDirectoryRequest", + "stability": "experimental" + }, "WorkspacesCreateFileRequest": { "type": "object", "properties": { "path": { "type": "string", - "description": "Relative path within the workspace files directory" + "description": "Slash-separated relative path within the workspace files directory" }, "content": { "type": "string", @@ -44110,7 +44576,7 @@ "items": { "type": "string" }, - "description": "Relative file paths in the workspace files directory" + "description": "Slash-separated relative file paths in the workspace files directory" } }, "required": [ @@ -44179,7 +44645,7 @@ "properties": { "path": { "type": "string", - "description": "Relative path within the workspace files directory" + "description": "Slash-separated relative path within the workspace files directory" } }, "required": [ @@ -44205,6 +44671,51 @@ "description": "Contents of the requested workspace file as a UTF-8 string.", "title": "WorkspacesReadFileResult" }, + "WorkspacesRemovePathRequest": { + "type": "object", + "properties": { + "path": { + "type": "string", + "description": "Slash-separated relative path within the workspace files directory" + }, + "recursive": { + "type": "boolean", + "description": "Whether to remove directory contents recursively. Defaults to false." + }, + "force": { + "type": "boolean", + "description": "Whether a missing path should be treated as success. Defaults to false." + } + }, + "required": [ + "path" + ], + "additionalProperties": false, + "description": "File or directory to remove from the session workspace files directory.", + "title": "WorkspacesRemovePathRequest", + "stability": "experimental" + }, + "WorkspacesRenamePathRequest": { + "type": "object", + "properties": { + "source": { + "type": "string", + "description": "Slash-separated source path relative to the workspace files directory" + }, + "destination": { + "type": "string", + "description": "Slash-separated destination path relative to the workspace files directory" + } + }, + "required": [ + "source", + "destination" + ], + "additionalProperties": false, + "description": "Source and destination paths for a rename within the session workspace files directory.", + "title": "WorkspacesRenamePathRequest", + "stability": "experimental" + }, "WorkspacesSaveLargePasteRequest": { "type": "object", "properties": { @@ -44264,6 +44775,57 @@ "description": "Descriptor for the saved paste file, or null when the workspace is unavailable.", "title": "WorkspacesSaveLargePasteResult" }, + "WorkspacesStatFileRequest": { + "type": "object", + "properties": { + "path": { + "type": "string", + "description": "Slash-separated relative path within the workspace files directory" + } + }, + "required": [ + "path" + ], + "additionalProperties": false, + "description": "Relative path of the workspace file or directory to inspect.", + "title": "WorkspacesStatFileRequest", + "stability": "experimental" + }, + "WorkspacesStatFileResult": { + "type": "object", + "properties": { + "isFile": { + "type": "boolean", + "description": "Whether the path identifies a regular file" + }, + "isDirectory": { + "type": "boolean", + "description": "Whether the path identifies a directory" + }, + "size": { + "type": "number", + "description": "Size in bytes" + }, + "mtimeMs": { + "type": "number", + "description": "Last modification time in Unix epoch milliseconds" + }, + "birthtimeMs": { + "type": "number", + "description": "Creation time in Unix epoch milliseconds" + } + }, + "required": [ + "isFile", + "isDirectory", + "size", + "mtimeMs", + "birthtimeMs" + ], + "additionalProperties": false, + "description": "Filesystem metadata for a path in the session workspace files directory.", + "title": "WorkspacesStatFileResult" + }, "WorkspacesTruncateSummariesRequest": { "type": "object", "properties": { diff --git a/schemas/session-events.schema.json b/schemas/session-events.schema.json index c98f4dd..313f95e 100644 --- a/schemas/session-events.schema.json +++ b/schemas/session-events.schema.json @@ -637,6 +637,10 @@ "type": "string", "description": "Unique identifier for this assistant message" }, + "originatingMessageId": { + "type": "string", + "description": "Logical ID of the primary user message that initiated this run, matching the messageId returned by session.send (or the last messageId of session.sendMessages). Stable across model/tool iterations, steering messages, and stop-hook corrections. Subagent runs use their own initiating message ID, not the parent's. Absent for runs without an associated initiating message, such as empty batches." + }, "model": { "type": "string", "description": "Model that produced this assistant message, if known" @@ -9010,7 +9014,7 @@ "description": "Type of hook that was invoked (e.g., \"preToolUse\", \"postToolUse\", \"sessionStart\")" }, "output": { - "description": "Output data produced by the hook", + "description": "Output data produced by the hook. Durable and resumed postToolUse receipts may omit messages owned by a successful skill invocation and replace an unchanged skill sessionLog copy with an elision marker; hook-modified or re-sourced values are preserved, and the authoritative body remains in the skill invocation event.", "x-opaque-json": true }, "success": { @@ -9199,7 +9203,7 @@ "description": "Type of hook being invoked (e.g., \"preToolUse\", \"postToolUse\", \"sessionStart\")" }, "input": { - "description": "Input data passed to the hook. For postToolUse hooks the retained copy served by session.eventLog.read (and by a resumed session) elides the tool result's inline `contents`/`uiResource` and replaces an over-long `textResultForLlm` with a `[copilot:elided ...]` marker, to keep a multi-megabyte payload out of the durable event log; the live subscription stream still delivers the full value. Read the adjacent tool.execution_complete event for the tool result itself.", + "description": "Input data passed to the hook. For postToolUse hooks the retained copy served by session.eventLog.read (and by a resumed session) drops the tool result's inline `contents`/`uiResource`/`skillInvocation` and replaces duplicated text result fields with a `[copilot:elided ...]` marker; the live subscription stream still delivers the full value. Canonical tool output remains in the adjacent tool.execution_complete event, while an invoked skill's authoritative body remains in its skill invocation event.", "x-opaque-json": true }, "parentToolCallId": { @@ -9922,6 +9926,7 @@ "enum": [ "headers", "none", + "error", "timeout" ], "description": "How the pending MCP headers refresh request resolved.", @@ -9929,6 +9934,7 @@ "x-enumDescriptions": { "headers": "The host supplied dynamic headers.", "none": "The host responded with no dynamic headers.", + "error": "The host credential broker rejected or failed the refresh.", "timeout": "No response arrived within the bounded window." } }, @@ -10694,7 +10700,11 @@ }, "source": { "$ref": "#/definitions/McpServerSource", - "description": "Configuration source: user, workspace, plugin, or builtin" + "description": "Configuration source: user, workspace, plugin, builtin, or managed" + }, + "displayName": { + "type": "string", + "description": "Human-readable display name supplied by a managed server catalog." }, "error": { "type": "string", @@ -10731,15 +10741,17 @@ "user", "workspace", "plugin", - "builtin" + "builtin", + "managed" ], - "description": "Configuration source: user, workspace, plugin, or builtin", + "description": "Configuration source: user, workspace, plugin, builtin, or managed", "title": "McpServerSource", "x-enumDescriptions": { "user": "Server configured in the user's global MCP configuration.", "workspace": "Server configured by the current workspace.", "plugin": "Server contributed by an installed plugin.", - "builtin": "Server bundled with the runtime." + "builtin": "Server bundled with the runtime.", + "managed": "Server supplied by a trusted host-managed catalog." } }, "McpServerStatus": { @@ -15956,6 +15968,7 @@ "service_access", "network_outbound", "network_local", + "network_host", "other_access" ], "description": "Bounded class of access an enforcement check refused. Raw resources, commands, and process names accompany it only when content capture is enabled; diagnostic text and matched rules are never exported.", @@ -15972,6 +15985,7 @@ "service_access": "Windows service-control-manager access was refused and correlated with capture evidence.", "network_outbound": "An outbound connection was refused by `network.allowOutbound`.", "network_local": "A connection to a local or loopback destination was refused by `network.allowLocalNetwork`.", + "network_host": "A destination was refused by the sandbox host allow/deny rules.", "other_access": "The sandbox's denial capture recorded a refusal its record does not attribute to a more specific control: an unclassified resource (registry, COM, section object) or an AppContainer capability with no network meaning. Deliberately generic — the capture proves the denial happened, and naming a narrower class than the record supports would be a guess." } }, @@ -16693,6 +16707,21 @@ "$ref": "#/definitions/SkillInvokedEvent", "description": "Session event \"skill.invoked\". Skill invocation details including content, allowed tools, and plugin metadata" }, + { + "$ref": "#/definitions/SkillInvokedRefEvent", + "description": "Session event \"skill.invoked_ref\". Internal durable skill invocation receipt whose content resolves from an earlier inline skill event in the same session.", + "visibility": "internal" + }, + { + "$ref": "#/definitions/SkillContextDeliveredEvent", + "description": "Session event \"skill.context_delivered\". Exact skill context delivered to the model during a tool phase. This is not a user submission or another skill invocation.", + "visibility": "internal" + }, + { + "$ref": "#/definitions/SkillContextDeliveredRefEvent", + "description": "Session event \"skill.context_delivered_ref\". Internal durable receipt that reconstructs exact model-visible skill context from earlier session content.", + "visibility": "internal" + }, { "$ref": "#/definitions/SandboxDecisionEvent", "description": "Session event \"sandbox.decision\". Payload of `sandbox.decision`, a bounded governance record of what the process sandbox was configured to do and whether it took effect. Discriminated by `kind`.", @@ -17647,6 +17676,174 @@ "error": "The session ended because of a crash or fatal error." } }, + "SkillContextDeliveredData": { + "type": "object", + "properties": { + "content": { + "type": "string", + "description": "Exact model-facing skill wrapper, including its invocation-time file context" + }, + "source": { + "type": "string", + "description": "Unmodified injection provenance, in the form skill-" + }, + "interactionId": { + "type": "string", + "description": "Interaction that delivered this context, when known" + } + }, + "required": [ + "content", + "source" + ], + "additionalProperties": false, + "description": "Exact skill context delivered to the model during a tool phase. This is not a user submission or another skill invocation.", + "title": "SkillContextDeliveredData" + }, + "SkillContextDeliveredEvent": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid", + "description": "Unique event identifier (UUID v4), generated when the event is emitted" + }, + "timestamp": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 timestamp when the event was created" + }, + "parentId": { + "anyOf": [ + { + "type": "string", + "format": "uuid" + }, + { + "type": "null" + } + ], + "description": "ID of the chronologically preceding event in the session, forming a linked chain. Null for the first event." + }, + "ephemeral": { + "type": "boolean", + "description": "When true, the event is transient and not persisted to the session event log on disk" + }, + "agentId": { + "type": "string", + "description": "Sub-agent instance identifier. Absent for events from the root/main agent and session-level events." + }, + "type": { + "type": "string", + "const": "skill.context_delivered", + "description": "Type discriminator. Always \"skill.context_delivered\"." + }, + "data": { + "$ref": "#/definitions/SkillContextDeliveredData", + "description": "Exact skill context delivered to the model during a tool phase. This is not a user submission or another skill invocation." + } + }, + "required": [ + "id", + "timestamp", + "parentId", + "type", + "data" + ], + "additionalProperties": false, + "description": "Session event \"skill.context_delivered\". Exact skill context delivered to the model during a tool phase. This is not a user submission or another skill invocation.", + "title": "SkillContextDeliveredEvent", + "visibility": "internal", + "stability": "experimental" + }, + "SkillContextDeliveredRefData": { + "type": "object", + "properties": { + "contentId": { + "type": "string", + "description": "Content identifier of an earlier inline skill event in this session, in the prefixed form `sha256:` over the UTF-8 bytes of that event's `content`" + }, + "prefix": { + "type": "string", + "description": "Exact text preceding the referenced content in the delivered wrapper" + }, + "suffix": { + "type": "string", + "description": "Exact text following the referenced content in the delivered wrapper" + }, + "source": { + "type": "string", + "description": "Unmodified injection provenance, in the form skill-" + }, + "interactionId": { + "type": "string", + "description": "Interaction that delivered this context, when known" + } + }, + "required": [ + "contentId", + "source" + ], + "additionalProperties": false, + "description": "Internal durable receipt that reconstructs exact model-visible skill context from earlier session content.", + "title": "SkillContextDeliveredRefData" + }, + "SkillContextDeliveredRefEvent": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid", + "description": "Unique event identifier (UUID v4), generated when the event is emitted" + }, + "timestamp": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 timestamp when the event was created" + }, + "parentId": { + "anyOf": [ + { + "type": "string", + "format": "uuid" + }, + { + "type": "null" + } + ], + "description": "ID of the chronologically preceding event in the session, forming a linked chain. Null for the first event." + }, + "ephemeral": { + "type": "boolean", + "description": "When true, the event is transient and not persisted to the session event log on disk" + }, + "agentId": { + "type": "string", + "description": "Sub-agent instance identifier. Absent for events from the root/main agent and session-level events." + }, + "type": { + "type": "string", + "const": "skill.context_delivered_ref", + "description": "Type discriminator. Always \"skill.context_delivered_ref\"." + }, + "data": { + "$ref": "#/definitions/SkillContextDeliveredRefData", + "description": "Internal durable receipt that reconstructs exact model-visible skill context from earlier session content." + } + }, + "required": [ + "id", + "timestamp", + "parentId", + "type", + "data" + ], + "additionalProperties": false, + "description": "Session event \"skill.context_delivered_ref\". Internal durable receipt that reconstructs exact model-visible skill context from earlier session content.", + "title": "SkillContextDeliveredRefEvent", + "visibility": "internal", + "stability": "experimental" + }, "SkillInvokedData": { "type": "object", "properties": { @@ -17761,6 +17958,128 @@ "description": "Session event \"skill.invoked\". Skill invocation details including content, allowed tools, and plugin metadata", "title": "SkillInvokedEvent" }, + "SkillInvokedRefData": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Name of the invoked skill" + }, + "model": { + "type": "string", + "description": "Model identifier active when the skill was invoked, when known" + }, + "path": { + "type": "string", + "description": "File path to the SKILL.md definition, or an empty string for an SDK-provided skill without a filesystem identity" + }, + "contentId": { + "type": "string", + "description": "Content identifier of an earlier inline skill event in this session, in the prefixed form `sha256:` over the UTF-8 bytes of that event's `content`" + }, + "contentLength": { + "type": "integer", + "minimum": 0, + "description": "UTF-16 code unit length of the referenced skill content. Derived from the referenced body and validated against it when the reference is expanded; a reference whose length disagrees with the body it names is rejected instead of expanded" + }, + "allowedTools": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Tool names that should be auto-approved when this skill is active" + }, + "disableModelInvocation": { + "type": "boolean", + "description": "Whether model invocation is disabled for this skill" + }, + "source": { + "type": "string", + "description": "Source identifier for where the skill was discovered" + }, + "pluginName": { + "type": "string", + "description": "Name of the plugin this skill originated from, when applicable" + }, + "pluginVersion": { + "type": "string", + "description": "Version of the plugin this skill originated from, when applicable" + }, + "description": { + "type": "string", + "description": "Description of the skill from its SKILL.md frontmatter" + }, + "trigger": { + "$ref": "#/definitions/SkillInvokedTrigger", + "description": "What triggered the skill invocation" + } + }, + "required": [ + "name", + "path", + "contentId", + "contentLength" + ], + "additionalProperties": false, + "description": "Internal durable skill invocation receipt whose content resolves from an earlier inline skill event in the same session.", + "title": "SkillInvokedRefData" + }, + "SkillInvokedRefEvent": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid", + "description": "Unique event identifier (UUID v4), generated when the event is emitted" + }, + "timestamp": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 timestamp when the event was created" + }, + "parentId": { + "anyOf": [ + { + "type": "string", + "format": "uuid" + }, + { + "type": "null" + } + ], + "description": "ID of the chronologically preceding event in the session, forming a linked chain. Null for the first event." + }, + "ephemeral": { + "type": "boolean", + "description": "When true, the event is transient and not persisted to the session event log on disk" + }, + "agentId": { + "type": "string", + "description": "Sub-agent instance identifier. Absent for events from the root/main agent and session-level events." + }, + "type": { + "type": "string", + "const": "skill.invoked_ref", + "description": "Type discriminator. Always \"skill.invoked_ref\"." + }, + "data": { + "$ref": "#/definitions/SkillInvokedRefData", + "description": "Internal durable skill invocation receipt whose content resolves from an earlier inline skill event in the same session." + } + }, + "required": [ + "id", + "timestamp", + "parentId", + "type", + "data" + ], + "additionalProperties": false, + "description": "Session event \"skill.invoked_ref\". Internal durable skill invocation receipt whose content resolves from an earlier inline skill event in the same session.", + "title": "SkillInvokedRefEvent", + "visibility": "internal", + "stability": "experimental" + }, "SkillInvokedTrigger": { "type": "string", "enum": [ @@ -18691,6 +19010,10 @@ "$ref": "#/definitions/SubagentTaskModelSource", "description": "Where the model input for this sub-agent came from. Present when the task planner resolved the launch (the task tool and factory agents); absent for sub-agents created through other runtime paths." }, + "modelSelectionSource": { + "$ref": "#/definitions/SubagentModelSelectionSource", + "description": "Authority or runtime mechanism responsible for sub-agent model selection, when known at start." + }, "factoryRunId": { "type": "string", "description": "Root id of the factory run that spawned this sub-agent, when it was spawned by one." @@ -19987,6 +20310,11 @@ "type": "boolean", "description": "Whether this tool execution ran inside a sandbox container" }, + "shellExecution": { + "$ref": "#/definitions/ToolExecutionCompleteShellExecution", + "description": "Experimental shell completion facts captured before the persisted result contents are stripped.", + "stability": "experimental" + }, "parentToolCallId": { "type": "string", "description": "Tool call ID of the parent tool invocation when this event originates from a sub-agent", @@ -20092,7 +20420,7 @@ }, "detailedContent": { "type": "string", - "description": "Full detailed tool result for UI/timeline display, preserving complete content such as diffs. Falls back to content when absent." + "description": "Detailed tool result for UI/timeline display, preserving complete content such as diffs for most tools. Successful skill invocations intentionally use the concise model-facing content here; the authoritative skill body is carried by the corresponding skill invocation event. Falls back to content when absent." }, "contents": { "type": "array", @@ -20141,6 +20469,22 @@ "description": "Tool execution result on success", "title": "ToolExecutionCompleteResult" }, + "ToolExecutionCompleteShellExecution": { + "type": "object", + "properties": { + "exitCode": { + "type": "integer", + "description": "Process exit code reported by the shell driver." + } + }, + "required": [ + "exitCode" + ], + "additionalProperties": false, + "description": "Experimental shell completion facts retained independently of the full tool result.", + "title": "ToolExecutionCompleteShellExecution", + "stability": "experimental" + }, "ToolExecutionCompleteToolDescription": { "type": "object", "properties": { @@ -20543,6 +20887,10 @@ "type": "string", "description": "Name of the MCP server hosting this tool, when the tool is an MCP tool" }, + "mcpConfigServerName": { + "type": "string", + "description": "Preferred lookup name for the MCP server hosting this tool: the configured (namespaced) config-map key when the tool carries one, otherwise the display name from `mcpServerName`. Present when the tool is an MCP tool; this is the name unrestricted provenance telemetry hashes so it joins with `mcp_server_setup`, which keys off the configured name too." + }, "mcpToolName": { "type": "string", "description": "Original tool name on the MCP server, when the tool is an MCP tool" @@ -20551,6 +20899,10 @@ "$ref": "#/definitions/McpServerTransport", "description": "Transport the MCP server hosting this tool is connected over, when the tool is an MCP tool and the server is configured" }, + "mcpConfigSource": { + "$ref": "#/definitions/McpServerSource", + "description": "Where the MCP server's configuration came from (`user`, `workspace`, `plugin`, or `builtin`), when the tool is an MCP tool and the server is configured" + }, "turnId": { "type": "string", "description": "Identifier for the agent loop turn this tool was invoked in, matching the corresponding assistant.turn_start event" diff --git a/src/github/copilot_sdk/client.clj b/src/github/copilot_sdk/client.clj index f7c5fb4..17d9154 100644 --- a/src/github/copilot_sdk/client.clj +++ b/src/github/copilot_sdk/client.clj @@ -1825,8 +1825,11 @@ session? (fn [session-id] (get-in @(:state client) [:sessions session-id])) unknown-session (fn [session-id] (delivered-chan - {:error {:code -32001 :message (str "Unknown session: " session-id)}}))] - (proto/set-request-handler! + {:error {:code -32001 :message (str "Unknown session: " session-id)}})) + prepare-request + (fn [method params] + (session/prepare-factory-request! client method params))] + (proto/set-request-dispatch! connection-io (fn [method params] (case method @@ -1884,10 +1887,13 @@ (session/handle-factory-execute! client session-id params))) "factory.abort" - (let [{:keys [session-id run-id]} params] + (let [{:keys [session-id run-id execution-token]} params] (if-not (session? session-id) (unknown-session session-id) - (session/handle-factory-abort! client session-id run-id))) + (if (::session/factory-abort-prepared? params) + (session/prepared-factory-abort-response) + (session/handle-factory-abort! + client session-id run-id execution-token)))) ;; System message transform (PR #816). Runs inline on the reverse-request ;; worker, so the registered transform callbacks are covered by the @@ -1914,7 +1920,8 @@ (session/handle-session-fs-request! client session-id method params))) (delivered-chan - {:error {:code -32601 :message (str "Unknown method: " method)}})))))) + {:error {:code -32601 :message (str "Unknown method: " method)}}))) + prepare-request))) (defn- connect-stdio! "Connect via stdio to the CLI process." diff --git a/src/github/copilot_sdk/generated/event_specs.clj b/src/github/copilot_sdk/generated/event_specs.clj index 9194f09..bb4fb22 100644 --- a/src/github/copilot_sdk/generated/event_specs.clj +++ b/src/github/copilot_sdk/generated/event_specs.clj @@ -139,7 +139,7 @@ (s/def :github.copilot-sdk.generated.event-specs/mcp-server-metadata-shape (s/and clojure.core/map? (fn [m] (and (contains? m :instructions) (s/valid? (s/nilable clojure.core/string?) (:instructions m)))) (fn [m] (every? #{:instructions} (keys m))))) -(s/def :github.copilot-sdk.generated.event-specs/mcp-servers-loaded-server-shape (s/and clojure.core/map? (fn [m] (or (not (contains? m :error)) (s/valid? clojure.core/string? (:error m)))) (fn [m] (and (contains? m :name) (s/valid? clojure.core/string? (:name m)))) (fn [m] (or (not (contains? m :plugin-name)) (s/valid? clojure.core/string? (:plugin-name m)))) (fn [m] (or (not (contains? m :plugin-version)) (s/valid? clojure.core/string? (:plugin-version m)))) (fn [m] (or (not (contains? m :server-metadata)) (s/valid? :github.copilot-sdk.generated.event-specs/mcp-server-metadata-shape (:server-metadata m)))) (fn [m] (or (not (contains? m :source)) (s/valid? #{"plugin" "user" "workspace" "builtin"} (:source m)))) (fn [m] (and (contains? m :status) (s/valid? #{"failed" "not_configured" "needs-auth" "stopped" "connected" "disabled" "pending"} (:status m)))) (fn [m] (or (not (contains? m :transport)) (s/valid? #{"http" "sse" "memory" "stdio"} (:transport m)))) (fn [m] (every? #{:error :name :plugin-name :plugin-version :server-metadata :source :status :transport} (keys m))))) +(s/def :github.copilot-sdk.generated.event-specs/mcp-servers-loaded-server-shape (s/and clojure.core/map? (fn [m] (or (not (contains? m :display-name)) (s/valid? clojure.core/string? (:display-name m)))) (fn [m] (or (not (contains? m :error)) (s/valid? clojure.core/string? (:error m)))) (fn [m] (and (contains? m :name) (s/valid? clojure.core/string? (:name m)))) (fn [m] (or (not (contains? m :plugin-name)) (s/valid? clojure.core/string? (:plugin-name m)))) (fn [m] (or (not (contains? m :plugin-version)) (s/valid? clojure.core/string? (:plugin-version m)))) (fn [m] (or (not (contains? m :server-metadata)) (s/valid? :github.copilot-sdk.generated.event-specs/mcp-server-metadata-shape (:server-metadata m)))) (fn [m] (or (not (contains? m :source)) (s/valid? #{"managed" "plugin" "user" "workspace" "builtin"} (:source m)))) (fn [m] (and (contains? m :status) (s/valid? #{"failed" "not_configured" "needs-auth" "stopped" "connected" "disabled" "pending"} (:status m)))) (fn [m] (or (not (contains? m :transport)) (s/valid? #{"http" "sse" "memory" "stdio"} (:transport m)))) (fn [m] (every? #{:display-name :error :name :plugin-name :plugin-version :server-metadata :source :status :transport} (keys m))))) (s/def :github.copilot-sdk.generated.event-specs/model-call-failure-request-fingerprint-shape (s/and clojure.core/map? (fn [m] (and (contains? m :image-part-count) (s/valid? (s/and clojure.core/integer? (fn [n] (<= 0 n))) (:image-part-count m)))) (fn [m] (and (contains? m :image-parts-missing-media-type) (s/valid? (s/and clojure.core/integer? (fn [n] (<= 0 n))) (:image-parts-missing-media-type m)))) (fn [m] (or (not (contains? m :last-message-role)) (s/valid? clojure.core/string? (:last-message-role m)))) (fn [m] (and (contains? m :message-count) (s/valid? (s/and clojure.core/integer? (fn [n] (<= 0 n))) (:message-count m)))) (fn [m] (and (contains? m :nameless-tool-call-count) (s/valid? (s/and clojure.core/integer? (fn [n] (<= 0 n))) (:nameless-tool-call-count m)))) (fn [m] (and (contains? m :tool-call-count) (s/valid? (s/and clojure.core/integer? (fn [n] (<= 0 n))) (:tool-call-count m)))) (fn [m] (and (contains? m :tool-result-message-count) (s/valid? (s/and clojure.core/integer? (fn [n] (<= 0 n))) (:tool-result-message-count m)))) (fn [m] (every? #{:image-part-count :image-parts-missing-media-type :last-message-role :message-count :nameless-tool-call-count :tool-call-count :tool-result-message-count} (keys m))))) @@ -277,6 +277,8 @@ (s/def :github.copilot-sdk.generated.event-specs/tool-execution-complete-result-shape (s/and clojure.core/map? (fn [m] (or (not (contains? m :binary-results-for-llm)) (s/valid? (s/coll-of (s/or :branch-0 :github.copilot-sdk.generated.event-specs/persisted-binary-image-shape :branch-1 :github.copilot-sdk.generated.event-specs/omitted-binary-result-shape :branch-2 :github.copilot-sdk.generated.event-specs/binary-asset-reference-shape)) (:binary-results-for-llm m)))) (fn [m] (or (not (contains? m :citable-sources)) (s/valid? (s/coll-of :github.copilot-sdk.generated.event-specs/citable-source-shape) (:citable-sources m)))) (fn [m] (and (contains? m :content) (s/valid? clojure.core/string? (:content m)))) (fn [m] (or (not (contains? m :contents)) (s/valid? (s/coll-of (s/or :branch-0 :github.copilot-sdk.generated.event-specs/tool-execution-complete-content-text-shape :branch-1 :github.copilot-sdk.generated.event-specs/tool-execution-complete-content-terminal-shape :branch-2 :github.copilot-sdk.generated.event-specs/tool-execution-complete-content-shell-exit-shape :branch-3 :github.copilot-sdk.generated.event-specs/tool-execution-complete-content-image-shape :branch-4 :github.copilot-sdk.generated.event-specs/tool-execution-complete-content-audio-shape :branch-5 :github.copilot-sdk.generated.event-specs/tool-execution-complete-content-resource-link-shape :branch-6 :github.copilot-sdk.generated.event-specs/tool-execution-complete-content-resource-shape)) (:contents m)))) (fn [m] (or (not (contains? m :detailed-content)) (s/valid? clojure.core/string? (:detailed-content m)))) (fn [m] (or (not (contains? m :mcp-meta)) (s/valid? (fn [root] (loop [pending [root]] (if (empty? pending) true (let [value (peek pending) remaining (pop pending)] (cond (or (nil? value) (string? value) (and (number? value) (not (ratio? value)) (cond (instance? Double value) (Double/isFinite value) (instance? Float value) (Float/isFinite value) :else true)) (boolean? value)) (recur remaining) (vector? value) (recur (into remaining value)) (map? value) (and (every? (fn* [%1] (or (keyword? %1) (string? %1))) (keys value)) (recur (into remaining (vals value)))) :else false))))) (:mcp-meta m)))) (fn [m] (or (not (contains? m :structured-content)) (s/valid? (fn [root] (loop [pending [root]] (if (empty? pending) true (let [value (peek pending) remaining (pop pending)] (cond (or (nil? value) (string? value) (and (number? value) (not (ratio? value)) (cond (instance? Double value) (Double/isFinite value) (instance? Float value) (Float/isFinite value) :else true)) (boolean? value)) (recur remaining) (vector? value) (recur (into remaining value)) (map? value) (and (every? (fn* [%1] (or (keyword? %1) (string? %1))) (keys value)) (recur (into remaining (vals value)))) :else false))))) (:structured-content m)))) (fn [m] (or (not (contains? m :ui-resource)) (s/valid? :github.copilot-sdk.generated.event-specs/tool-execution-complete-ui-resource-shape (:ui-resource m)))) (fn [m] (every? #{:binary-results-for-llm :citable-sources :content :contents :detailed-content :mcp-meta :structured-content :ui-resource} (keys m))))) +(s/def :github.copilot-sdk.generated.event-specs/tool-execution-complete-shell-execution-shape (s/and clojure.core/map? (fn [m] (and (contains? m :exit-code) (s/valid? clojure.core/integer? (:exit-code m)))) (fn [m] (every? #{:exit-code} (keys m))))) + (s/def :github.copilot-sdk.generated.event-specs/tool-execution-complete-tool-description-meta-shape (s/and clojure.core/map? (fn [m] (or (not (contains? m :ui)) (s/valid? :github.copilot-sdk.generated.event-specs/tool-execution-complete-tool-description-meta-ui-shape (:ui m)))) (fn [m] (every? #{:ui} (keys m))))) (s/def :github.copilot-sdk.generated.event-specs/tool-execution-complete-tool-description-meta-ui-shape (s/and clojure.core/map? (fn [m] (or (not (contains? m :resource-uri)) (s/valid? clojure.core/string? (:resource-uri m)))) (fn [m] (or (not (contains? m :visibility)) (s/valid? (s/coll-of #{"model" "app"}) (:visibility m)))) (fn [m] (every? #{:resource-uri :visibility} (keys m))))) @@ -483,6 +485,10 @@ (s/def :github.copilot-sdk.generated.event-specs/content-filter-triggered clojure.core/boolean?) +(s/def :github.copilot-sdk.generated.event-specs/content-id clojure.core/string?) + +(s/def :github.copilot-sdk.generated.event-specs/content-length (s/and clojure.core/integer? (fn [n] (<= 0 n)))) + (s/def :github.copilot-sdk.generated.event-specs/context (s/spec (fn [v1130] (or (s/valid? :github.copilot-sdk.generated.event-specs/working-directory-context-shape v1130) (s/valid? clojure.core/string? v1130))))) (s/def :github.copilot-sdk.generated.event-specs/context-tier (s/spec (fn [v1122] (or (s/valid? (s/nilable #{"long_context" "default"}) v1122) (s/valid? clojure.core/string? v1122))))) @@ -705,6 +711,10 @@ (s/def :github.copilot-sdk.generated.event-specs/max-prompt-tokens (s/and clojure.core/integer? (fn [n] (<= 0 n)))) +(s/def :github.copilot-sdk.generated.event-specs/mcp-config-server-name clojure.core/string?) + +(s/def :github.copilot-sdk.generated.event-specs/mcp-config-source #{"managed" "plugin" "user" "workspace" "builtin"}) + (s/def :github.copilot-sdk.generated.event-specs/mcp-meta (fn [root] (loop [pending [root]] (if (empty? pending) true (let [value (peek pending) remaining (pop pending)] (cond (or (nil? value) (string? value) (and (number? value) (not (ratio? value)) (cond (instance? Double value) (Double/isFinite value) (instance? Float value) (Float/isFinite value) :else true)) (boolean? value)) (recur remaining) (vector? value) (recur (into remaining value)) (map? value) (and (every? (fn* [%1] (or (keyword? %1) (string? %1))) (keys value)) (recur (into remaining (vals value)))) :else false)))))) (s/def :github.copilot-sdk.generated.event-specs/mcp-request-id (fn [root] (loop [pending [root]] (if (empty? pending) true (let [value (peek pending) remaining (pop pending)] (cond (or (nil? value) (string? value) (and (number? value) (not (ratio? value)) (cond (instance? Double value) (Double/isFinite value) (instance? Float value) (Float/isFinite value) :else true)) (boolean? value)) (recur remaining) (vector? value) (recur (into remaining value)) (map? value) (and (every? (fn* [%1] (or (keyword? %1) (string? %1))) (keys value)) (recur (into remaining (vals value)))) :else false)))))) @@ -769,7 +779,9 @@ (s/def :github.copilot-sdk.generated.event-specs/origin #{"model" "user"}) -(s/def :github.copilot-sdk.generated.event-specs/outcome (s/spec (fn [v1147] (or (s/valid? #{"completed" "continue" "blocked"} v1147) (s/valid? clojure.core/string? v1147) (s/valid? #{"success" "error" "cancelled" "rejected"} v1147) (s/valid? #{"token" "cancelled"} v1147) (s/valid? #{"none" "timeout" "headers"} v1147))))) +(s/def :github.copilot-sdk.generated.event-specs/originating-message-id clojure.core/string?) + +(s/def :github.copilot-sdk.generated.event-specs/outcome (s/spec (fn [v1147] (or (s/valid? #{"completed" "continue" "blocked"} v1147) (s/valid? clojure.core/string? v1147) (s/valid? #{"success" "error" "cancelled" "rejected"} v1147) (s/valid? #{"token" "cancelled"} v1147) (s/valid? #{"none" "error" "timeout" "headers"} v1147))))) (s/def :github.copilot-sdk.generated.event-specs/output (fn [root] (loop [pending [root]] (if (empty? pending) true (let [value (peek pending) remaining (pop pending)] (cond (or (nil? value) (string? value) (and (number? value) (not (ratio? value)) (cond (instance? Double value) (Double/isFinite value) (instance? Float value) (Float/isFinite value) :else true)) (boolean? value)) (recur remaining) (vector? value) (recur (into remaining value)) (map? value) (and (every? (fn* [%1] (or (keyword? %1) (string? %1))) (keys value)) (recur (into remaining (vals value)))) :else false)))))) @@ -843,6 +855,8 @@ (s/def :github.copilot-sdk.generated.event-specs/predicted-label clojure.core/string?) +(s/def :github.copilot-sdk.generated.event-specs/prefix clojure.core/string?) + (s/def :github.copilot-sdk.generated.event-specs/previous-auto-tier #{"balance" "intelligence" "fast" "efficiency"}) (s/def :github.copilot-sdk.generated.event-specs/previous-mode (s/spec (fn [v1118] (or (s/valid? #{"interactive" "autopilot" "plan"} v1118) (s/valid? #{"allow-all" "assisted" "manual"} v1118))))) @@ -1021,6 +1035,8 @@ (s/def :github.copilot-sdk.generated.event-specs/settings (fn [root] (loop [pending [root]] (if (empty? pending) true (let [value (peek pending) remaining (pop pending)] (cond (or (nil? value) (string? value) (and (number? value) (not (ratio? value)) (cond (instance? Double value) (Double/isFinite value) (instance? Float value) (Float/isFinite value) :else true)) (boolean? value)) (recur remaining) (vector? value) (recur (into remaining value)) (map? value) (and (every? (fn* [%1] (or (keyword? %1) (string? %1))) (keys value)) (recur (into remaining (vals value)))) :else false)))))) +(s/def :github.copilot-sdk.generated.event-specs/shell-execution :github.copilot-sdk.generated.event-specs/tool-execution-complete-shell-execution-shape) + (s/def :github.copilot-sdk.generated.event-specs/shell-tool-info :github.copilot-sdk.generated.event-specs/tool-execution-start-shell-tool-info-shape) (s/def :github.copilot-sdk.generated.event-specs/shortfall-tokens (s/and clojure.core/integer? (fn [n] (<= 0 n)))) @@ -1067,6 +1083,8 @@ (s/def :github.copilot-sdk.generated.event-specs/successful-tool-count (s/and clojure.core/integer? (fn [n] (<= 0 n)))) +(s/def :github.copilot-sdk.generated.event-specs/suffix clojure.core/string?) + (s/def :github.copilot-sdk.generated.event-specs/summary clojure.core/string?) (s/def :github.copilot-sdk.generated.event-specs/summary-content clojure.core/string?) @@ -1179,7 +1197,7 @@ (s/def :github.copilot-sdk.generated.event-specs/turn-kind #{"user" "compaction"}) -(s/def :github.copilot-sdk.generated.event-specs/type (s/spec (fn [v1139] (or (s/valid? #{"session.start"} v1139) (s/valid? #{"session.resume"} v1139) (s/valid? #{"session.remote_steerable_changed"} v1139) (s/valid? #{"session.error"} v1139) (s/valid? #{"session.idle"} v1139) (s/valid? #{"session.title_changed"} v1139) (s/valid? #{"session.schedule_created"} v1139) (s/valid? #{"session.schedule_cancelled"} v1139) (s/valid? #{"session.schedule_rearmed"} v1139) (s/valid? #{"session.autopilot_objective_changed"} v1139) (s/valid? #{"session.info"} v1139) (s/valid? #{"session.warning"} v1139) (s/valid? #{"session.model_change"} v1139) (s/valid? #{"session.auto_tier_recommendation"} v1139) (s/valid? #{"session.auto_tier_switch_failed"} v1139) (s/valid? #{"session.mode_changed"} v1139) (s/valid? #{"session.mode_notice_delivered"} v1139) (s/valid? #{"session.session_limits_changed"} v1139) (s/valid? #{"session.permissions_changed"} v1139) (s/valid? #{"session.plan_changed"} v1139) (s/valid? #{"session.todos_changed"} v1139) (s/valid? #{"session.memory_changed"} v1139) (s/valid? #{"session.workspace_file_changed"} v1139) (s/valid? #{"session.handoff"} v1139) (s/valid? #{"session.truncation"} v1139) (s/valid? #{"session.snapshot_rewind"} v1139) (s/valid? #{"session.shutdown"} v1139) (s/valid? #{"session.usage_checkpoint"} v1139) (s/valid? #{"session.context_changed"} v1139) (s/valid? #{"session.usage_info"} v1139) (s/valid? #{"session.context_cleared"} v1139) (s/valid? #{"session.compaction_start"} v1139) (s/valid? #{"session.compaction_complete"} v1139) (s/valid? #{"session.task_complete"} v1139) (s/valid? #{"session.completion_receipt"} v1139) (s/valid? #{"session.fusion_route_started"} v1139) (s/valid? #{"session.fusion_route_failed"} v1139) (s/valid? #{"session.fusion_resolved"} v1139) (s/valid? #{"session.fusion_handoff"} v1139) (s/valid? #{"session.fusion_commit_started"} v1139) (s/valid? #{"session.fusion_completed"} v1139) (s/valid? #{"user.message"} v1139) (s/valid? #{"pending_messages.modified"} v1139) (s/valid? #{"assistant.turn_start"} v1139) (s/valid? #{"assistant.turn_retry"} v1139) (s/valid? #{"agent.interrupted"} v1139) (s/valid? #{"assistant.intent"} v1139) (s/valid? #{"assistant.fusion_phase_started"} v1139) (s/valid? #{"assistant.fusion_phase_activity"} v1139) (s/valid? #{"assistant.fusion_phase_completed"} v1139) (s/valid? #{"assistant.fusion_phase_failed"} v1139) (s/valid? #{"assistant.server_tool_progress"} v1139) (s/valid? #{"assistant.reasoning"} v1139) (s/valid? #{"assistant.reasoning_delta"} v1139) (s/valid? #{"assistant.tool_call_delta"} v1139) (s/valid? #{"assistant.streaming_delta"} v1139) (s/valid? #{"assistant.message"} v1139) (s/valid? #{"assistant.message_start"} v1139) (s/valid? #{"assistant.message_delta"} v1139) (s/valid? #{"assistant.turn_end"} v1139) (s/valid? #{"assistant.idle"} v1139) (s/valid? #{"assistant.usage"} v1139) (s/valid? #{"prompt_cache_break"} v1139) (s/valid? #{"model.call_failure"} v1139) (s/valid? #{"model.call_finished"} v1139) (s/valid? #{"model.call_start"} v1139) (s/valid? #{"abort"} v1139) (s/valid? #{"tool.user_requested"} v1139) (s/valid? #{"tool.execution_start"} v1139) (s/valid? #{"tool.execution_partial_result"} v1139) (s/valid? #{"tool.execution_progress"} v1139) (s/valid? #{"tool.execution_complete"} v1139) (s/valid? #{"tool_search.activated"} v1139) (s/valid? #{"skill.invoked"} v1139) (s/valid? #{"sandbox.decision"} v1139) (s/valid? #{"subagent.started"} v1139) (s/valid? #{"subagent.configured"} v1139) (s/valid? #{"subagent.completed"} v1139) (s/valid? #{"subagent.failed"} v1139) (s/valid? #{"subagent.selected"} v1139) (s/valid? #{"subagent.deselected"} v1139) (s/valid? #{"hook.start"} v1139) (s/valid? #{"hook.end"} v1139) (s/valid? #{"hook.progress"} v1139) (s/valid? #{"session.binary_asset"} v1139) (s/valid? #{"system.message"} v1139) (s/valid? #{"system.notification"} v1139) (s/valid? #{"permission.requested"} v1139) (s/valid? #{"permission.completed"} v1139) (s/valid? #{"permission.carriedForward"} v1139) (s/valid? #{"permission.messageAuthorization"} v1139) (s/valid? #{"permission.messageAuthorizationRead"} v1139) (s/valid? #{"permission.messageAuthorizationDegraded"} v1139) (s/valid? #{"user_input.requested"} v1139) (s/valid? #{"user_input.completed"} v1139) (s/valid? #{"elicitation.requested"} v1139) (s/valid? #{"elicitation.completed"} v1139) (s/valid? #{"sampling.requested"} v1139) (s/valid? #{"sampling.completed"} v1139) (s/valid? #{"mcp.oauth_required"} v1139) (s/valid? #{"mcp.oauth_completed"} v1139) (s/valid? #{"mcp.headers_refresh_required"} v1139) (s/valid? #{"mcp.headers_refresh_completed"} v1139) (s/valid? #{"session.custom_notification"} v1139) (s/valid? #{"ui.ephemeral_query"} v1139) (s/valid? #{"external_tool.requested"} v1139) (s/valid? #{"external_tool.completed"} v1139) (s/valid? #{"command.queued"} v1139) (s/valid? #{"command.execute"} v1139) (s/valid? #{"command.completed"} v1139) (s/valid? #{"auto_mode_switch.requested"} v1139) (s/valid? #{"auto_mode_switch.completed"} v1139) (s/valid? #{"session_limits_exhausted.requested"} v1139) (s/valid? #{"session_limits_exhausted.completed"} v1139) (s/valid? #{"session.auto_mode_resolved"} v1139) (s/valid? #{"session.managed_settings_resolved"} v1139) (s/valid? #{"session.managed_settings_enforced"} v1139) (s/valid? #{"commands.changed"} v1139) (s/valid? #{"capabilities.changed"} v1139) (s/valid? #{"exit_plan_mode.requested"} v1139) (s/valid? #{"exit_plan_mode.completed"} v1139) (s/valid? #{"session.tools_updated"} v1139) (s/valid? #{"session.background_tasks_changed"} v1139) (s/valid? #{"factory.run_updated"} v1139) (s/valid? #{"factory.run_started"} v1139) (s/valid? #{"factory.run_settled"} v1139) (s/valid? #{"session.skills_loaded"} v1139) (s/valid? #{"session.custom_agents_updated"} v1139) (s/valid? #{"session.mcp_servers_loaded"} v1139) (s/valid? #{"session.mcp_server_status_changed"} v1139) (s/valid? #{"session.mcp_server_removed"} v1139) (s/valid? #{"session.mcp_server_needs_reconnect"} v1139) (s/valid? #{"mcp.tools.list_changed"} v1139) (s/valid? #{"mcp.resources.list_changed"} v1139) (s/valid? #{"mcp.prompts.list_changed"} v1139) (s/valid? #{"session.extensions_loaded"} v1139) (s/valid? #{"session.canvas.opened"} v1139) (s/valid? #{"session.canvas.registry_changed"} v1139) (s/valid? #{"session.canvas.closed"} v1139) (s/valid? #{"session.canvas.unavailable"} v1139) (s/valid? #{"session.canvas.recorded"} v1139) (s/valid? #{"session.canvas.removed"} v1139) (s/valid? #{"session.extensions.attachments_pushed"} v1139) (s/valid? #{"mcp_app.tool_call_complete"} v1139) (s/valid? #{"resource" "image"} v1139))))) +(s/def :github.copilot-sdk.generated.event-specs/type (s/spec (fn [v1139] (or (s/valid? #{"session.start"} v1139) (s/valid? #{"session.resume"} v1139) (s/valid? #{"session.remote_steerable_changed"} v1139) (s/valid? #{"session.error"} v1139) (s/valid? #{"session.idle"} v1139) (s/valid? #{"session.title_changed"} v1139) (s/valid? #{"session.schedule_created"} v1139) (s/valid? #{"session.schedule_cancelled"} v1139) (s/valid? #{"session.schedule_rearmed"} v1139) (s/valid? #{"session.autopilot_objective_changed"} v1139) (s/valid? #{"session.info"} v1139) (s/valid? #{"session.warning"} v1139) (s/valid? #{"session.model_change"} v1139) (s/valid? #{"session.auto_tier_recommendation"} v1139) (s/valid? #{"session.auto_tier_switch_failed"} v1139) (s/valid? #{"session.mode_changed"} v1139) (s/valid? #{"session.mode_notice_delivered"} v1139) (s/valid? #{"session.session_limits_changed"} v1139) (s/valid? #{"session.permissions_changed"} v1139) (s/valid? #{"session.plan_changed"} v1139) (s/valid? #{"session.todos_changed"} v1139) (s/valid? #{"session.memory_changed"} v1139) (s/valid? #{"session.workspace_file_changed"} v1139) (s/valid? #{"session.handoff"} v1139) (s/valid? #{"session.truncation"} v1139) (s/valid? #{"session.snapshot_rewind"} v1139) (s/valid? #{"session.shutdown"} v1139) (s/valid? #{"session.usage_checkpoint"} v1139) (s/valid? #{"session.context_changed"} v1139) (s/valid? #{"session.usage_info"} v1139) (s/valid? #{"session.context_cleared"} v1139) (s/valid? #{"session.compaction_start"} v1139) (s/valid? #{"session.compaction_complete"} v1139) (s/valid? #{"session.task_complete"} v1139) (s/valid? #{"session.completion_receipt"} v1139) (s/valid? #{"session.fusion_route_started"} v1139) (s/valid? #{"session.fusion_route_failed"} v1139) (s/valid? #{"session.fusion_resolved"} v1139) (s/valid? #{"session.fusion_handoff"} v1139) (s/valid? #{"session.fusion_commit_started"} v1139) (s/valid? #{"session.fusion_completed"} v1139) (s/valid? #{"user.message"} v1139) (s/valid? #{"pending_messages.modified"} v1139) (s/valid? #{"assistant.turn_start"} v1139) (s/valid? #{"assistant.turn_retry"} v1139) (s/valid? #{"agent.interrupted"} v1139) (s/valid? #{"assistant.intent"} v1139) (s/valid? #{"assistant.fusion_phase_started"} v1139) (s/valid? #{"assistant.fusion_phase_activity"} v1139) (s/valid? #{"assistant.fusion_phase_completed"} v1139) (s/valid? #{"assistant.fusion_phase_failed"} v1139) (s/valid? #{"assistant.server_tool_progress"} v1139) (s/valid? #{"assistant.reasoning"} v1139) (s/valid? #{"assistant.reasoning_delta"} v1139) (s/valid? #{"assistant.tool_call_delta"} v1139) (s/valid? #{"assistant.streaming_delta"} v1139) (s/valid? #{"assistant.message"} v1139) (s/valid? #{"assistant.message_start"} v1139) (s/valid? #{"assistant.message_delta"} v1139) (s/valid? #{"assistant.turn_end"} v1139) (s/valid? #{"assistant.idle"} v1139) (s/valid? #{"assistant.usage"} v1139) (s/valid? #{"prompt_cache_break"} v1139) (s/valid? #{"model.call_failure"} v1139) (s/valid? #{"model.call_finished"} v1139) (s/valid? #{"model.call_start"} v1139) (s/valid? #{"abort"} v1139) (s/valid? #{"tool.user_requested"} v1139) (s/valid? #{"tool.execution_start"} v1139) (s/valid? #{"tool.execution_partial_result"} v1139) (s/valid? #{"tool.execution_progress"} v1139) (s/valid? #{"tool.execution_complete"} v1139) (s/valid? #{"tool_search.activated"} v1139) (s/valid? #{"skill.invoked"} v1139) (s/valid? #{"skill.invoked_ref"} v1139) (s/valid? #{"skill.context_delivered"} v1139) (s/valid? #{"skill.context_delivered_ref"} v1139) (s/valid? #{"sandbox.decision"} v1139) (s/valid? #{"subagent.started"} v1139) (s/valid? #{"subagent.configured"} v1139) (s/valid? #{"subagent.completed"} v1139) (s/valid? #{"subagent.failed"} v1139) (s/valid? #{"subagent.selected"} v1139) (s/valid? #{"subagent.deselected"} v1139) (s/valid? #{"hook.start"} v1139) (s/valid? #{"hook.end"} v1139) (s/valid? #{"hook.progress"} v1139) (s/valid? #{"session.binary_asset"} v1139) (s/valid? #{"system.message"} v1139) (s/valid? #{"system.notification"} v1139) (s/valid? #{"permission.requested"} v1139) (s/valid? #{"permission.completed"} v1139) (s/valid? #{"permission.carriedForward"} v1139) (s/valid? #{"permission.messageAuthorization"} v1139) (s/valid? #{"permission.messageAuthorizationRead"} v1139) (s/valid? #{"permission.messageAuthorizationDegraded"} v1139) (s/valid? #{"user_input.requested"} v1139) (s/valid? #{"user_input.completed"} v1139) (s/valid? #{"elicitation.requested"} v1139) (s/valid? #{"elicitation.completed"} v1139) (s/valid? #{"sampling.requested"} v1139) (s/valid? #{"sampling.completed"} v1139) (s/valid? #{"mcp.oauth_required"} v1139) (s/valid? #{"mcp.oauth_completed"} v1139) (s/valid? #{"mcp.headers_refresh_required"} v1139) (s/valid? #{"mcp.headers_refresh_completed"} v1139) (s/valid? #{"session.custom_notification"} v1139) (s/valid? #{"ui.ephemeral_query"} v1139) (s/valid? #{"external_tool.requested"} v1139) (s/valid? #{"external_tool.completed"} v1139) (s/valid? #{"command.queued"} v1139) (s/valid? #{"command.execute"} v1139) (s/valid? #{"command.completed"} v1139) (s/valid? #{"auto_mode_switch.requested"} v1139) (s/valid? #{"auto_mode_switch.completed"} v1139) (s/valid? #{"session_limits_exhausted.requested"} v1139) (s/valid? #{"session_limits_exhausted.completed"} v1139) (s/valid? #{"session.auto_mode_resolved"} v1139) (s/valid? #{"session.managed_settings_resolved"} v1139) (s/valid? #{"session.managed_settings_enforced"} v1139) (s/valid? #{"commands.changed"} v1139) (s/valid? #{"capabilities.changed"} v1139) (s/valid? #{"exit_plan_mode.requested"} v1139) (s/valid? #{"exit_plan_mode.completed"} v1139) (s/valid? #{"session.tools_updated"} v1139) (s/valid? #{"session.background_tasks_changed"} v1139) (s/valid? #{"factory.run_updated"} v1139) (s/valid? #{"factory.run_started"} v1139) (s/valid? #{"factory.run_settled"} v1139) (s/valid? #{"session.skills_loaded"} v1139) (s/valid? #{"session.custom_agents_updated"} v1139) (s/valid? #{"session.mcp_servers_loaded"} v1139) (s/valid? #{"session.mcp_server_status_changed"} v1139) (s/valid? #{"session.mcp_server_removed"} v1139) (s/valid? #{"session.mcp_server_needs_reconnect"} v1139) (s/valid? #{"mcp.tools.list_changed"} v1139) (s/valid? #{"mcp.resources.list_changed"} v1139) (s/valid? #{"mcp.prompts.list_changed"} v1139) (s/valid? #{"session.extensions_loaded"} v1139) (s/valid? #{"session.canvas.opened"} v1139) (s/valid? #{"session.canvas.registry_changed"} v1139) (s/valid? #{"session.canvas.closed"} v1139) (s/valid? #{"session.canvas.unavailable"} v1139) (s/valid? #{"session.canvas.recorded"} v1139) (s/valid? #{"session.canvas.removed"} v1139) (s/valid? #{"session.extensions.attachments_pushed"} v1139) (s/valid? #{"mcp_app.tool_call_complete"} v1139) (s/valid? #{"resource" "image"} v1139))))) (s/def :github.copilot-sdk.generated.event-specs/tz clojure.core/string?) @@ -1227,7 +1245,7 @@ (s/def :github.copilot-sdk.generated.event-specs/assistant.intent-data (s/keys :req-un [:github.copilot-sdk.generated.event-specs/intent])) -(s/def :github.copilot-sdk.generated.event-specs/assistant.message-data (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/content :github.copilot-sdk.generated.event-specs/message-id] :opt-un [:github.copilot-sdk.generated.event-specs/api-call-id :github.copilot-sdk.generated.event-specs/chunk-count :github.copilot-sdk.generated.event-specs/chunk-index :github.copilot-sdk.generated.event-specs/citations :github.copilot-sdk.generated.event-specs/client-request-id :github.copilot-sdk.generated.event-specs/encrypted-content :github.copilot-sdk.generated.event-specs/fusion :github.copilot-sdk.generated.event-specs/interaction-id :github.copilot-sdk.generated.event-specs/model :github.copilot-sdk.generated.event-specs/output-tokens :github.copilot-sdk.generated.event-specs/parent-tool-call-id :github.copilot-sdk.generated.event-specs/phase :github.copilot-sdk.generated.event-specs/reasoning-blocks :github.copilot-sdk.generated.event-specs/reasoning-opaque :github.copilot-sdk.generated.event-specs/reasoning-text :github.copilot-sdk.generated.event-specs/reasoning-wire-field :github.copilot-sdk.generated.event-specs/request-id :github.copilot-sdk.generated.event-specs/rte :github.copilot-sdk.generated.event-specs/server-tools :github.copilot-sdk.generated.event-specs/service-request-id :github.copilot-sdk.generated.event-specs/tool-requests :github.copilot-sdk.generated.event-specs/turn-id]) (fn [data] (s/valid? clojure.core/string? (:content data))) (fn [data] (or (not (contains? data :phase)) (s/valid? clojure.core/string? (:phase data)))))) +(s/def :github.copilot-sdk.generated.event-specs/assistant.message-data (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/content :github.copilot-sdk.generated.event-specs/message-id] :opt-un [:github.copilot-sdk.generated.event-specs/api-call-id :github.copilot-sdk.generated.event-specs/chunk-count :github.copilot-sdk.generated.event-specs/chunk-index :github.copilot-sdk.generated.event-specs/citations :github.copilot-sdk.generated.event-specs/client-request-id :github.copilot-sdk.generated.event-specs/encrypted-content :github.copilot-sdk.generated.event-specs/fusion :github.copilot-sdk.generated.event-specs/interaction-id :github.copilot-sdk.generated.event-specs/model :github.copilot-sdk.generated.event-specs/originating-message-id :github.copilot-sdk.generated.event-specs/output-tokens :github.copilot-sdk.generated.event-specs/parent-tool-call-id :github.copilot-sdk.generated.event-specs/phase :github.copilot-sdk.generated.event-specs/reasoning-blocks :github.copilot-sdk.generated.event-specs/reasoning-opaque :github.copilot-sdk.generated.event-specs/reasoning-text :github.copilot-sdk.generated.event-specs/reasoning-wire-field :github.copilot-sdk.generated.event-specs/request-id :github.copilot-sdk.generated.event-specs/rte :github.copilot-sdk.generated.event-specs/server-tools :github.copilot-sdk.generated.event-specs/service-request-id :github.copilot-sdk.generated.event-specs/tool-requests :github.copilot-sdk.generated.event-specs/turn-id]) (fn [data] (s/valid? clojure.core/string? (:content data))) (fn [data] (or (not (contains? data :phase)) (s/valid? clojure.core/string? (:phase data)))))) (s/def :github.copilot-sdk.generated.event-specs/assistant.message_delta-data (s/keys :req-un [:github.copilot-sdk.generated.event-specs/delta-content :github.copilot-sdk.generated.event-specs/message-id] :opt-un [:github.copilot-sdk.generated.event-specs/parent-tool-call-id])) @@ -1289,7 +1307,7 @@ (s/def :github.copilot-sdk.generated.event-specs/hook.start-data (s/keys :req-un [:github.copilot-sdk.generated.event-specs/hook-invocation-id :github.copilot-sdk.generated.event-specs/hook-type] :opt-un [:github.copilot-sdk.generated.event-specs/input :github.copilot-sdk.generated.event-specs/parent-tool-call-id])) -(s/def :github.copilot-sdk.generated.event-specs/mcp.headers_refresh_completed-data (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/outcome :github.copilot-sdk.generated.event-specs/request-id]) (fn [data] (s/valid? #{"none" "timeout" "headers"} (:outcome data))))) +(s/def :github.copilot-sdk.generated.event-specs/mcp.headers_refresh_completed-data (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/outcome :github.copilot-sdk.generated.event-specs/request-id]) (fn [data] (s/valid? #{"none" "error" "timeout" "headers"} (:outcome data))))) (s/def :github.copilot-sdk.generated.event-specs/mcp.headers_refresh_required-data (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/reason :github.copilot-sdk.generated.event-specs/request-id :github.copilot-sdk.generated.event-specs/server-name :github.copilot-sdk.generated.event-specs/server-url]) (fn [data] (s/valid? #{"auth-failed" "startup" "ttl-expired"} (:reason data))))) @@ -1461,8 +1479,14 @@ (s/def :github.copilot-sdk.generated.event-specs/session_limits_exhausted.requested-data (s/keys :req-un [:github.copilot-sdk.generated.event-specs/max-ai-credits :github.copilot-sdk.generated.event-specs/request-id :github.copilot-sdk.generated.event-specs/used-ai-credits])) +(s/def :github.copilot-sdk.generated.event-specs/skill.context_delivered-data (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/content :github.copilot-sdk.generated.event-specs/source] :opt-un [:github.copilot-sdk.generated.event-specs/interaction-id]) (fn [data] (s/valid? clojure.core/string? (:content data))) (fn [data] (s/valid? clojure.core/string? (:source data))))) + +(s/def :github.copilot-sdk.generated.event-specs/skill.context_delivered_ref-data (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/content-id :github.copilot-sdk.generated.event-specs/source] :opt-un [:github.copilot-sdk.generated.event-specs/interaction-id :github.copilot-sdk.generated.event-specs/prefix :github.copilot-sdk.generated.event-specs/suffix]) (fn [data] (s/valid? clojure.core/string? (:source data))))) + (s/def :github.copilot-sdk.generated.event-specs/skill.invoked-data (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/content :github.copilot-sdk.generated.event-specs/name :github.copilot-sdk.generated.event-specs/path] :opt-un [:github.copilot-sdk.generated.event-specs/allowed-tools :github.copilot-sdk.generated.event-specs/description :github.copilot-sdk.generated.event-specs/disable-model-invocation :github.copilot-sdk.generated.event-specs/model :github.copilot-sdk.generated.event-specs/plugin-name :github.copilot-sdk.generated.event-specs/plugin-version :github.copilot-sdk.generated.event-specs/source :github.copilot-sdk.generated.event-specs/trigger]) (fn [data] (s/valid? clojure.core/string? (:content data))) (fn [data] (or (not (contains? data :source)) (s/valid? clojure.core/string? (:source data)))) (fn [data] (or (not (contains? data :trigger)) (s/valid? #{"agent-invoked" "user-invoked" "context-load"} (:trigger data)))))) +(s/def :github.copilot-sdk.generated.event-specs/skill.invoked_ref-data (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/content-id :github.copilot-sdk.generated.event-specs/content-length :github.copilot-sdk.generated.event-specs/name :github.copilot-sdk.generated.event-specs/path] :opt-un [:github.copilot-sdk.generated.event-specs/allowed-tools :github.copilot-sdk.generated.event-specs/description :github.copilot-sdk.generated.event-specs/disable-model-invocation :github.copilot-sdk.generated.event-specs/model :github.copilot-sdk.generated.event-specs/plugin-name :github.copilot-sdk.generated.event-specs/plugin-version :github.copilot-sdk.generated.event-specs/source :github.copilot-sdk.generated.event-specs/trigger]) (fn [data] (or (not (contains? data :source)) (s/valid? clojure.core/string? (:source data)))) (fn [data] (or (not (contains? data :trigger)) (s/valid? #{"agent-invoked" "user-invoked" "context-load"} (:trigger data)))))) + (s/def :github.copilot-sdk.generated.event-specs/subagent.completed-data (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/agent-display-name :github.copilot-sdk.generated.event-specs/agent-name :github.copilot-sdk.generated.event-specs/tool-call-id] :opt-un [:github.copilot-sdk.generated.event-specs/cancelled :github.copilot-sdk.generated.event-specs/configured-model-matches-actual :github.copilot-sdk.generated.event-specs/configured-model-preference :github.copilot-sdk.generated.event-specs/duration-ms :github.copilot-sdk.generated.event-specs/explicit-model-matches-preference :github.copilot-sdk.generated.event-specs/explicit-model-override :github.copilot-sdk.generated.event-specs/first-dispatched-model :github.copilot-sdk.generated.event-specs/model :github.copilot-sdk.generated.event-specs/model-override-reason :github.copilot-sdk.generated.event-specs/model-selection-source :github.copilot-sdk.generated.event-specs/total-tokens :github.copilot-sdk.generated.event-specs/total-tool-calls]) (fn [data] (or (not (contains? data :duration-ms)) (s/valid? (s/and clojure.core/integer? (fn [n] (<= 0 n))) (:duration-ms data)))) (fn [data] (s/valid? clojure.core/string? (:tool-call-id data))))) (s/def :github.copilot-sdk.generated.event-specs/subagent.configured-data (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/model :github.copilot-sdk.generated.event-specs/multi-turn] :opt-un [:github.copilot-sdk.generated.event-specs/context-tier :github.copilot-sdk.generated.event-specs/reasoning-effort]) (fn [data] (or (not (contains? data :context-tier)) (s/valid? clojure.core/string? (:context-tier data)))) (fn [data] (or (not (contains? data :reasoning-effort)) (s/valid? clojure.core/string? (:reasoning-effort data)))))) @@ -1473,19 +1497,19 @@ (s/def :github.copilot-sdk.generated.event-specs/subagent.selected-data (s/keys :req-un [:github.copilot-sdk.generated.event-specs/agent-display-name :github.copilot-sdk.generated.event-specs/agent-name :github.copilot-sdk.generated.event-specs/tools])) -(s/def :github.copilot-sdk.generated.event-specs/subagent.started-data (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/agent-description :github.copilot-sdk.generated.event-specs/agent-display-name :github.copilot-sdk.generated.event-specs/agent-name :github.copilot-sdk.generated.event-specs/tool-call-id] :opt-un [:github.copilot-sdk.generated.event-specs/agent-type :github.copilot-sdk.generated.event-specs/execution-mode :github.copilot-sdk.generated.event-specs/factory-run-id :github.copilot-sdk.generated.event-specs/model :github.copilot-sdk.generated.event-specs/parent-id :github.copilot-sdk.generated.event-specs/resumable :github.copilot-sdk.generated.event-specs/task-model-source]) (fn [data] (or (not (contains? data :parent-id)) (s/valid? clojure.core/string? (:parent-id data)))) (fn [data] (s/valid? clojure.core/string? (:tool-call-id data))))) +(s/def :github.copilot-sdk.generated.event-specs/subagent.started-data (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/agent-description :github.copilot-sdk.generated.event-specs/agent-display-name :github.copilot-sdk.generated.event-specs/agent-name :github.copilot-sdk.generated.event-specs/tool-call-id] :opt-un [:github.copilot-sdk.generated.event-specs/agent-type :github.copilot-sdk.generated.event-specs/execution-mode :github.copilot-sdk.generated.event-specs/factory-run-id :github.copilot-sdk.generated.event-specs/model :github.copilot-sdk.generated.event-specs/model-selection-source :github.copilot-sdk.generated.event-specs/parent-id :github.copilot-sdk.generated.event-specs/resumable :github.copilot-sdk.generated.event-specs/task-model-source]) (fn [data] (or (not (contains? data :parent-id)) (s/valid? clojure.core/string? (:parent-id data)))) (fn [data] (s/valid? clojure.core/string? (:tool-call-id data))))) (s/def :github.copilot-sdk.generated.event-specs/system.message-data (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/content :github.copilot-sdk.generated.event-specs/role] :opt-un [:github.copilot-sdk.generated.event-specs/interaction-id :github.copilot-sdk.generated.event-specs/metadata :github.copilot-sdk.generated.event-specs/name]) (fn [data] (s/valid? clojure.core/string? (:content data))) (fn [data] (or (not (contains? data :metadata)) (s/valid? :github.copilot-sdk.generated.event-specs/system-message-metadata-shape (:metadata data)))) (fn [data] (s/valid? #{"developer" "system"} (:role data))))) (s/def :github.copilot-sdk.generated.event-specs/system.notification-data (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/content :github.copilot-sdk.generated.event-specs/kind]) (fn [data] (s/valid? clojure.core/string? (:content data))) (fn [data] (s/valid? (s/or :branch-0 :github.copilot-sdk.generated.event-specs/system-notification-agent-completed-shape :branch-1 :github.copilot-sdk.generated.event-specs/system-notification-agent-idle-shape :branch-2 :github.copilot-sdk.generated.event-specs/system-notification-new-inbox-message-shape :branch-3 :github.copilot-sdk.generated.event-specs/system-notification-shell-completed-shape :branch-4 :github.copilot-sdk.generated.event-specs/system-notification-shell-detached-completed-shape :branch-5 :github.copilot-sdk.generated.event-specs/system-notification-instruction-discovered-shape :branch-6 :github.copilot-sdk.generated.event-specs/system-notification-factory-completed-shape :branch-7 :github.copilot-sdk.generated.event-specs/system-notification-unclassified-shape) (:kind data))))) -(s/def :github.copilot-sdk.generated.event-specs/tool.execution_complete-data (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/success :github.copilot-sdk.generated.event-specs/tool-call-id] :opt-un [:github.copilot-sdk.generated.event-specs/error :github.copilot-sdk.generated.event-specs/fusion :github.copilot-sdk.generated.event-specs/interaction-id :github.copilot-sdk.generated.event-specs/is-user-requested :github.copilot-sdk.generated.event-specs/mcp-meta :github.copilot-sdk.generated.event-specs/model :github.copilot-sdk.generated.event-specs/parent-tool-call-id :github.copilot-sdk.generated.event-specs/result :github.copilot-sdk.generated.event-specs/rte :github.copilot-sdk.generated.event-specs/sandboxed :github.copilot-sdk.generated.event-specs/tool-description :github.copilot-sdk.generated.event-specs/tool-telemetry :github.copilot-sdk.generated.event-specs/turn-id]) (fn [data] (or (not (contains? data :error)) (s/valid? :github.copilot-sdk.generated.event-specs/tool-execution-complete-error-shape (:error data)))) (fn [data] (or (not (contains? data :result)) (s/valid? :github.copilot-sdk.generated.event-specs/tool-execution-complete-result-shape (:result data)))) (fn [data] (s/valid? clojure.core/string? (:tool-call-id data))) (fn [data] (or (not (contains? data :tool-description)) (s/valid? :github.copilot-sdk.generated.event-specs/tool-execution-complete-tool-description-shape (:tool-description data)))))) +(s/def :github.copilot-sdk.generated.event-specs/tool.execution_complete-data (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/success :github.copilot-sdk.generated.event-specs/tool-call-id] :opt-un [:github.copilot-sdk.generated.event-specs/error :github.copilot-sdk.generated.event-specs/fusion :github.copilot-sdk.generated.event-specs/interaction-id :github.copilot-sdk.generated.event-specs/is-user-requested :github.copilot-sdk.generated.event-specs/mcp-meta :github.copilot-sdk.generated.event-specs/model :github.copilot-sdk.generated.event-specs/parent-tool-call-id :github.copilot-sdk.generated.event-specs/result :github.copilot-sdk.generated.event-specs/rte :github.copilot-sdk.generated.event-specs/sandboxed :github.copilot-sdk.generated.event-specs/shell-execution :github.copilot-sdk.generated.event-specs/tool-description :github.copilot-sdk.generated.event-specs/tool-telemetry :github.copilot-sdk.generated.event-specs/turn-id]) (fn [data] (or (not (contains? data :error)) (s/valid? :github.copilot-sdk.generated.event-specs/tool-execution-complete-error-shape (:error data)))) (fn [data] (or (not (contains? data :result)) (s/valid? :github.copilot-sdk.generated.event-specs/tool-execution-complete-result-shape (:result data)))) (fn [data] (s/valid? clojure.core/string? (:tool-call-id data))) (fn [data] (or (not (contains? data :tool-description)) (s/valid? :github.copilot-sdk.generated.event-specs/tool-execution-complete-tool-description-shape (:tool-description data)))))) (s/def :github.copilot-sdk.generated.event-specs/tool.execution_partial_result-data (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/partial-output :github.copilot-sdk.generated.event-specs/tool-call-id]) (fn [data] (s/valid? clojure.core/string? (:tool-call-id data))))) (s/def :github.copilot-sdk.generated.event-specs/tool.execution_progress-data (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/progress-message :github.copilot-sdk.generated.event-specs/tool-call-id]) (fn [data] (s/valid? clojure.core/string? (:tool-call-id data))))) -(s/def :github.copilot-sdk.generated.event-specs/tool.execution_start-data (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/tool-call-id :github.copilot-sdk.generated.event-specs/tool-name] :opt-un [:github.copilot-sdk.generated.event-specs/arguments :github.copilot-sdk.generated.event-specs/display-verbatim :github.copilot-sdk.generated.event-specs/fusion :github.copilot-sdk.generated.event-specs/mcp-server-name :github.copilot-sdk.generated.event-specs/mcp-tool-name :github.copilot-sdk.generated.event-specs/mcp-transport :github.copilot-sdk.generated.event-specs/model :github.copilot-sdk.generated.event-specs/parent-tool-call-id :github.copilot-sdk.generated.event-specs/rte :github.copilot-sdk.generated.event-specs/shell-tool-info :github.copilot-sdk.generated.event-specs/tool-description :github.copilot-sdk.generated.event-specs/turn-id]) (fn [data] (or (not (contains? data :arguments)) (s/valid? (fn [root] (loop [pending [root]] (if (empty? pending) true (let [value (peek pending) remaining (pop pending)] (cond (or (nil? value) (string? value) (and (number? value) (not (ratio? value)) (cond (instance? Double value) (Double/isFinite value) (instance? Float value) (Float/isFinite value) :else true)) (boolean? value)) (recur remaining) (vector? value) (recur (into remaining value)) (map? value) (and (every? (fn* [%1] (or (keyword? %1) (string? %1))) (keys value)) (recur (into remaining (vals value)))) :else false))))) (:arguments data)))) (fn [data] (s/valid? clojure.core/string? (:tool-call-id data))) (fn [data] (or (not (contains? data :tool-description)) (s/valid? :github.copilot-sdk.generated.event-specs/tool-execution-start-tool-description-shape (:tool-description data)))))) +(s/def :github.copilot-sdk.generated.event-specs/tool.execution_start-data (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/tool-call-id :github.copilot-sdk.generated.event-specs/tool-name] :opt-un [:github.copilot-sdk.generated.event-specs/arguments :github.copilot-sdk.generated.event-specs/display-verbatim :github.copilot-sdk.generated.event-specs/fusion :github.copilot-sdk.generated.event-specs/mcp-config-server-name :github.copilot-sdk.generated.event-specs/mcp-config-source :github.copilot-sdk.generated.event-specs/mcp-server-name :github.copilot-sdk.generated.event-specs/mcp-tool-name :github.copilot-sdk.generated.event-specs/mcp-transport :github.copilot-sdk.generated.event-specs/model :github.copilot-sdk.generated.event-specs/parent-tool-call-id :github.copilot-sdk.generated.event-specs/rte :github.copilot-sdk.generated.event-specs/shell-tool-info :github.copilot-sdk.generated.event-specs/tool-description :github.copilot-sdk.generated.event-specs/turn-id]) (fn [data] (or (not (contains? data :arguments)) (s/valid? (fn [root] (loop [pending [root]] (if (empty? pending) true (let [value (peek pending) remaining (pop pending)] (cond (or (nil? value) (string? value) (and (number? value) (not (ratio? value)) (cond (instance? Double value) (Double/isFinite value) (instance? Float value) (Float/isFinite value) :else true)) (boolean? value)) (recur remaining) (vector? value) (recur (into remaining value)) (map? value) (and (every? (fn* [%1] (or (keyword? %1) (string? %1))) (keys value)) (recur (into remaining (vals value)))) :else false))))) (:arguments data)))) (fn [data] (s/valid? clojure.core/string? (:tool-call-id data))) (fn [data] (or (not (contains? data :tool-description)) (s/valid? :github.copilot-sdk.generated.event-specs/tool-execution-start-tool-description-shape (:tool-description data)))))) (s/def :github.copilot-sdk.generated.event-specs/tool.user_requested-data (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/tool-call-id :github.copilot-sdk.generated.event-specs/tool-name] :opt-un [:github.copilot-sdk.generated.event-specs/arguments]) (fn [data] (or (not (contains? data :arguments)) (s/valid? (fn [root] (loop [pending [root]] (if (empty? pending) true (let [value (peek pending) remaining (pop pending)] (cond (or (nil? value) (string? value) (and (number? value) (not (ratio? value)) (cond (instance? Double value) (Double/isFinite value) (instance? Float value) (Float/isFinite value) :else true)) (boolean? value)) (recur remaining) (vector? value) (recur (into remaining value)) (map? value) (and (every? (fn* [%1] (or (keyword? %1) (string? %1))) (keys value)) (recur (into remaining (vals value)))) :else false))))) (:arguments data)))) (fn [data] (s/valid? clojure.core/string? (:tool-call-id data))))) @@ -1749,8 +1773,14 @@ (s/def :github.copilot-sdk.generated.event-specs/session_limits_exhausted.requested (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/data :github.copilot-sdk.generated.event-specs/ephemeral :github.copilot-sdk.generated.event-specs/id :github.copilot-sdk.generated.event-specs/parent-id :github.copilot-sdk.generated.event-specs/timestamp :github.copilot-sdk.generated.event-specs/type] :opt-un [:github.copilot-sdk.generated.event-specs/agent-id]) (fn [event] (clojure.core/= true (:ephemeral event))) (fn [event] (clojure.core/= "session_limits_exhausted.requested" (:type event))) (fn [event] (s/valid? clojure.core/string? (:id event))) (fn [event] (s/valid? (s/nilable clojure.core/string?) (:parent-id event))) (fn [event] (every? #{:agent-id :data :ephemeral :id :parent-id :timestamp :type} (keys event))) (fn [event] (s/valid? :github.copilot-sdk.generated.event-specs/session_limits_exhausted.requested-data (:data event))))) +(s/def :github.copilot-sdk.generated.event-specs/skill.context_delivered (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/data :github.copilot-sdk.generated.event-specs/id :github.copilot-sdk.generated.event-specs/parent-id :github.copilot-sdk.generated.event-specs/timestamp :github.copilot-sdk.generated.event-specs/type] :opt-un [:github.copilot-sdk.generated.event-specs/agent-id :github.copilot-sdk.generated.event-specs/ephemeral]) (fn [event] (clojure.core/= "skill.context_delivered" (:type event))) (fn [event] (s/valid? clojure.core/string? (:id event))) (fn [event] (s/valid? (s/nilable clojure.core/string?) (:parent-id event))) (fn [event] (every? #{:agent-id :data :ephemeral :id :parent-id :timestamp :type} (keys event))) (fn [event] (s/valid? :github.copilot-sdk.generated.event-specs/skill.context_delivered-data (:data event))))) + +(s/def :github.copilot-sdk.generated.event-specs/skill.context_delivered_ref (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/data :github.copilot-sdk.generated.event-specs/id :github.copilot-sdk.generated.event-specs/parent-id :github.copilot-sdk.generated.event-specs/timestamp :github.copilot-sdk.generated.event-specs/type] :opt-un [:github.copilot-sdk.generated.event-specs/agent-id :github.copilot-sdk.generated.event-specs/ephemeral]) (fn [event] (clojure.core/= "skill.context_delivered_ref" (:type event))) (fn [event] (s/valid? clojure.core/string? (:id event))) (fn [event] (s/valid? (s/nilable clojure.core/string?) (:parent-id event))) (fn [event] (every? #{:agent-id :data :ephemeral :id :parent-id :timestamp :type} (keys event))) (fn [event] (s/valid? :github.copilot-sdk.generated.event-specs/skill.context_delivered_ref-data (:data event))))) + (s/def :github.copilot-sdk.generated.event-specs/skill.invoked (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/data :github.copilot-sdk.generated.event-specs/id :github.copilot-sdk.generated.event-specs/parent-id :github.copilot-sdk.generated.event-specs/timestamp :github.copilot-sdk.generated.event-specs/type] :opt-un [:github.copilot-sdk.generated.event-specs/agent-id :github.copilot-sdk.generated.event-specs/ephemeral]) (fn [event] (clojure.core/= "skill.invoked" (:type event))) (fn [event] (s/valid? clojure.core/string? (:id event))) (fn [event] (s/valid? (s/nilable clojure.core/string?) (:parent-id event))) (fn [event] (every? #{:agent-id :data :ephemeral :id :parent-id :timestamp :type} (keys event))) (fn [event] (s/valid? :github.copilot-sdk.generated.event-specs/skill.invoked-data (:data event))))) +(s/def :github.copilot-sdk.generated.event-specs/skill.invoked_ref (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/data :github.copilot-sdk.generated.event-specs/id :github.copilot-sdk.generated.event-specs/parent-id :github.copilot-sdk.generated.event-specs/timestamp :github.copilot-sdk.generated.event-specs/type] :opt-un [:github.copilot-sdk.generated.event-specs/agent-id :github.copilot-sdk.generated.event-specs/ephemeral]) (fn [event] (clojure.core/= "skill.invoked_ref" (:type event))) (fn [event] (s/valid? clojure.core/string? (:id event))) (fn [event] (s/valid? (s/nilable clojure.core/string?) (:parent-id event))) (fn [event] (every? #{:agent-id :data :ephemeral :id :parent-id :timestamp :type} (keys event))) (fn [event] (s/valid? :github.copilot-sdk.generated.event-specs/skill.invoked_ref-data (:data event))))) + (s/def :github.copilot-sdk.generated.event-specs/subagent.completed (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/data :github.copilot-sdk.generated.event-specs/id :github.copilot-sdk.generated.event-specs/parent-id :github.copilot-sdk.generated.event-specs/timestamp :github.copilot-sdk.generated.event-specs/type] :opt-un [:github.copilot-sdk.generated.event-specs/agent-id :github.copilot-sdk.generated.event-specs/ephemeral]) (fn [event] (clojure.core/= "subagent.completed" (:type event))) (fn [event] (s/valid? clojure.core/string? (:id event))) (fn [event] (s/valid? (s/nilable clojure.core/string?) (:parent-id event))) (fn [event] (every? #{:agent-id :data :ephemeral :id :parent-id :timestamp :type} (keys event))) (fn [event] (s/valid? :github.copilot-sdk.generated.event-specs/subagent.completed-data (:data event))))) (s/def :github.copilot-sdk.generated.event-specs/subagent.configured (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/data :github.copilot-sdk.generated.event-specs/id :github.copilot-sdk.generated.event-specs/parent-id :github.copilot-sdk.generated.event-specs/timestamp :github.copilot-sdk.generated.event-specs/type] :opt-un [:github.copilot-sdk.generated.event-specs/agent-id :github.copilot-sdk.generated.event-specs/ephemeral]) (fn [event] (clojure.core/= "subagent.configured" (:type event))) (fn [event] (s/valid? clojure.core/string? (:id event))) (fn [event] (s/valid? (s/nilable clojure.core/string?) (:parent-id event))) (fn [event] (every? #{:agent-id :data :ephemeral :id :parent-id :timestamp :type} (keys event))) (fn [event] (s/valid? :github.copilot-sdk.generated.event-specs/subagent.configured-data (:data event))))) @@ -1787,7 +1817,7 @@ (s/def :github.copilot-sdk.generated.event-specs/user_input.requested (s/and (s/keys :req-un [:github.copilot-sdk.generated.event-specs/data :github.copilot-sdk.generated.event-specs/ephemeral :github.copilot-sdk.generated.event-specs/id :github.copilot-sdk.generated.event-specs/parent-id :github.copilot-sdk.generated.event-specs/timestamp :github.copilot-sdk.generated.event-specs/type] :opt-un [:github.copilot-sdk.generated.event-specs/agent-id]) (fn [event] (clojure.core/= true (:ephemeral event))) (fn [event] (clojure.core/= "user_input.requested" (:type event))) (fn [event] (s/valid? clojure.core/string? (:id event))) (fn [event] (s/valid? (s/nilable clojure.core/string?) (:parent-id event))) (fn [event] (every? #{:agent-id :data :ephemeral :id :parent-id :timestamp :type} (keys event))) (fn [event] (s/valid? :github.copilot-sdk.generated.event-specs/user_input.requested-data (:data event))))) -(def event-types "Set of all event-type strings known to the schema." #{"abort" "agent.interrupted" "assistant.fusion_phase_activity" "assistant.fusion_phase_completed" "assistant.fusion_phase_failed" "assistant.fusion_phase_started" "assistant.idle" "assistant.intent" "assistant.message" "assistant.message_delta" "assistant.message_start" "assistant.reasoning" "assistant.reasoning_delta" "assistant.server_tool_progress" "assistant.streaming_delta" "assistant.tool_call_delta" "assistant.turn_end" "assistant.turn_retry" "assistant.turn_start" "assistant.usage" "auto_mode_switch.completed" "auto_mode_switch.requested" "capabilities.changed" "command.completed" "command.execute" "command.queued" "commands.changed" "elicitation.completed" "elicitation.requested" "exit_plan_mode.completed" "exit_plan_mode.requested" "external_tool.completed" "external_tool.requested" "factory.run_settled" "factory.run_started" "factory.run_updated" "hook.end" "hook.progress" "hook.start" "mcp.headers_refresh_completed" "mcp.headers_refresh_required" "mcp.oauth_completed" "mcp.oauth_required" "mcp.prompts.list_changed" "mcp.resources.list_changed" "mcp.tools.list_changed" "mcp_app.tool_call_complete" "model.call_failure" "model.call_finished" "model.call_start" "pending_messages.modified" "permission.carriedForward" "permission.completed" "permission.messageAuthorization" "permission.messageAuthorizationDegraded" "permission.messageAuthorizationRead" "permission.requested" "prompt_cache_break" "sampling.completed" "sampling.requested" "sandbox.decision" "session.auto_mode_resolved" "session.auto_tier_recommendation" "session.auto_tier_switch_failed" "session.autopilot_objective_changed" "session.background_tasks_changed" "session.binary_asset" "session.canvas.closed" "session.canvas.opened" "session.canvas.recorded" "session.canvas.registry_changed" "session.canvas.removed" "session.canvas.unavailable" "session.compaction_complete" "session.compaction_start" "session.completion_receipt" "session.context_changed" "session.context_cleared" "session.custom_agents_updated" "session.custom_notification" "session.error" "session.extensions.attachments_pushed" "session.extensions_loaded" "session.fusion_commit_started" "session.fusion_completed" "session.fusion_handoff" "session.fusion_resolved" "session.fusion_route_failed" "session.fusion_route_started" "session.handoff" "session.idle" "session.info" "session.managed_settings_enforced" "session.managed_settings_resolved" "session.mcp_server_needs_reconnect" "session.mcp_server_removed" "session.mcp_server_status_changed" "session.mcp_servers_loaded" "session.memory_changed" "session.mode_changed" "session.mode_notice_delivered" "session.model_change" "session.permissions_changed" "session.plan_changed" "session.remote_steerable_changed" "session.resume" "session.schedule_cancelled" "session.schedule_created" "session.schedule_rearmed" "session.session_limits_changed" "session.shutdown" "session.skills_loaded" "session.snapshot_rewind" "session.start" "session.task_complete" "session.title_changed" "session.todos_changed" "session.tools_updated" "session.truncation" "session.usage_checkpoint" "session.usage_info" "session.warning" "session.workspace_file_changed" "session_limits_exhausted.completed" "session_limits_exhausted.requested" "skill.invoked" "subagent.completed" "subagent.configured" "subagent.deselected" "subagent.failed" "subagent.selected" "subagent.started" "system.message" "system.notification" "tool.execution_complete" "tool.execution_partial_result" "tool.execution_progress" "tool.execution_start" "tool.user_requested" "tool_search.activated" "ui.ephemeral_query" "user.message" "user_input.completed" "user_input.requested"}) +(def event-types "Set of all event-type strings known to the schema." #{"abort" "agent.interrupted" "assistant.fusion_phase_activity" "assistant.fusion_phase_completed" "assistant.fusion_phase_failed" "assistant.fusion_phase_started" "assistant.idle" "assistant.intent" "assistant.message" "assistant.message_delta" "assistant.message_start" "assistant.reasoning" "assistant.reasoning_delta" "assistant.server_tool_progress" "assistant.streaming_delta" "assistant.tool_call_delta" "assistant.turn_end" "assistant.turn_retry" "assistant.turn_start" "assistant.usage" "auto_mode_switch.completed" "auto_mode_switch.requested" "capabilities.changed" "command.completed" "command.execute" "command.queued" "commands.changed" "elicitation.completed" "elicitation.requested" "exit_plan_mode.completed" "exit_plan_mode.requested" "external_tool.completed" "external_tool.requested" "factory.run_settled" "factory.run_started" "factory.run_updated" "hook.end" "hook.progress" "hook.start" "mcp.headers_refresh_completed" "mcp.headers_refresh_required" "mcp.oauth_completed" "mcp.oauth_required" "mcp.prompts.list_changed" "mcp.resources.list_changed" "mcp.tools.list_changed" "mcp_app.tool_call_complete" "model.call_failure" "model.call_finished" "model.call_start" "pending_messages.modified" "permission.carriedForward" "permission.completed" "permission.messageAuthorization" "permission.messageAuthorizationDegraded" "permission.messageAuthorizationRead" "permission.requested" "prompt_cache_break" "sampling.completed" "sampling.requested" "sandbox.decision" "session.auto_mode_resolved" "session.auto_tier_recommendation" "session.auto_tier_switch_failed" "session.autopilot_objective_changed" "session.background_tasks_changed" "session.binary_asset" "session.canvas.closed" "session.canvas.opened" "session.canvas.recorded" "session.canvas.registry_changed" "session.canvas.removed" "session.canvas.unavailable" "session.compaction_complete" "session.compaction_start" "session.completion_receipt" "session.context_changed" "session.context_cleared" "session.custom_agents_updated" "session.custom_notification" "session.error" "session.extensions.attachments_pushed" "session.extensions_loaded" "session.fusion_commit_started" "session.fusion_completed" "session.fusion_handoff" "session.fusion_resolved" "session.fusion_route_failed" "session.fusion_route_started" "session.handoff" "session.idle" "session.info" "session.managed_settings_enforced" "session.managed_settings_resolved" "session.mcp_server_needs_reconnect" "session.mcp_server_removed" "session.mcp_server_status_changed" "session.mcp_servers_loaded" "session.memory_changed" "session.mode_changed" "session.mode_notice_delivered" "session.model_change" "session.permissions_changed" "session.plan_changed" "session.remote_steerable_changed" "session.resume" "session.schedule_cancelled" "session.schedule_created" "session.schedule_rearmed" "session.session_limits_changed" "session.shutdown" "session.skills_loaded" "session.snapshot_rewind" "session.start" "session.task_complete" "session.title_changed" "session.todos_changed" "session.tools_updated" "session.truncation" "session.usage_checkpoint" "session.usage_info" "session.warning" "session.workspace_file_changed" "session_limits_exhausted.completed" "session_limits_exhausted.requested" "skill.context_delivered" "skill.context_delivered_ref" "skill.invoked" "skill.invoked_ref" "subagent.completed" "subagent.configured" "subagent.deselected" "subagent.failed" "subagent.selected" "subagent.started" "system.message" "system.notification" "tool.execution_complete" "tool.execution_partial_result" "tool.execution_progress" "tool.execution_start" "tool.user_requested" "tool_search.activated" "ui.ephemeral_query" "user.message" "user_input.completed" "user_input.requested"}) (defmulti event-mm :type) @@ -2041,8 +2071,14 @@ (defmethod event-mm "session_limits_exhausted.requested" [_] (s/get-spec :github.copilot-sdk.generated.event-specs/session_limits_exhausted.requested)) +(defmethod event-mm "skill.context_delivered" [_] (s/get-spec :github.copilot-sdk.generated.event-specs/skill.context_delivered)) + +(defmethod event-mm "skill.context_delivered_ref" [_] (s/get-spec :github.copilot-sdk.generated.event-specs/skill.context_delivered_ref)) + (defmethod event-mm "skill.invoked" [_] (s/get-spec :github.copilot-sdk.generated.event-specs/skill.invoked)) +(defmethod event-mm "skill.invoked_ref" [_] (s/get-spec :github.copilot-sdk.generated.event-specs/skill.invoked_ref)) + (defmethod event-mm "subagent.completed" [_] (s/get-spec :github.copilot-sdk.generated.event-specs/subagent.completed)) (defmethod event-mm "subagent.configured" [_] (s/get-spec :github.copilot-sdk.generated.event-specs/subagent.configured)) diff --git a/src/github/copilot_sdk/protocol.clj b/src/github/copilot_sdk/protocol.clj index b7d6678..b5893d8 100644 --- a/src/github/copilot_sdk/protocol.clj +++ b/src/github/copilot_sdk/protocol.clj @@ -440,23 +440,45 @@ "Submit an incoming request message (e.g. hooks.invoke) to the connection's bounded reverse-request worker pool. - Called on the reader thread. Submission is non-blocking, so the reader keeps - routing responses and notifications while handlers run." + Called on the reader thread. An optional request preparer may perform + non-blocking, order-sensitive SDK bookkeeping before submission. Handler + execution remains on the bounded worker pool." [conn msg] (let [{:keys [state-atom outgoing-ch]} conn ^ThreadPoolExecutor executor (:request-executor conn) - request-handler (:request-handler (conn-state state-atom)) + {:keys [request-handler request-preparer]} (conn-state state-atom) id (:id msg) method (:method msg) params (:params msg)] (log/debug "Received request: method=" method " id=" id) (try - (.execute executor - ^Runnable (fn [] - (run-request-handler! request-handler outgoing-ch - method id params))) + (let [preparation + (if request-preparer + (request-preparer method params) + {:params params}) + on-reject (:on-reject preparation) + prepared-params (:params preparation)] + (try + (.execute executor + ^Runnable (fn [] + (run-request-handler! + request-handler outgoing-ch + method id prepared-params))) + (catch RejectedExecutionException failure + (when on-reject + (on-reject)) + (throw failure)))) (catch RejectedExecutionException _ - (reject-request! executor (:rejected-requests conn) outgoing-ch method id))))) + (reject-request! executor (:rejected-requests conn) outgoing-ch method id)) + (catch Exception failure + (log/error failure + "Reverse request preparation failed" + {:method method :id id}) + (put! outgoing-ch + {:jsonrpc "2.0" + :id id + :error {:code -32603 + :message "Internal error preparing reverse request"}}))))) (defn- restore-opaque-path [raw converted wire-path idiom-path] @@ -779,6 +801,7 @@ {:running? true :pending-requests {} :request-handler nil + :request-preparer nil :writer-thread nil}) (defn connect @@ -1105,11 +1128,18 @@ :params wire-params}] (put! (:outgoing-ch conn) msg))) +(defn ^:no-doc set-request-dispatch! + "Set reverse-request handling and non-blocking inline preparation atomically." + [conn handler request-preparer] + (update-conn! (:state-atom conn) assoc + :request-handler handler + :request-preparer request-preparer)) + (defn set-request-handler! - "Set handler for incoming requests. + "Set handler for incoming requests. Handler is (fn [method params] -> channel with {:result ...} or {:error ...})" [conn handler] - (update-conn! (:state-atom conn) assoc :request-handler handler)) + (set-request-dispatch! conn handler nil)) (defn notifications "Returns the channel that receives incoming notifications." diff --git a/src/github/copilot_sdk/session.clj b/src/github/copilot_sdk/session.clj index e618dbe..38c66e2 100644 --- a/src/github/copilot_sdk/session.clj +++ b/src/github/copilot_sdk/session.clj @@ -1113,11 +1113,16 @@ value))) futures))) +(defn- new-factory-execution + [run-id execution-token] + {:run-id run-id + :execution-token execution-token + :cancelled? (atom false) + :cancel-chan (chan)}) + (defn- register-factory-execution! [client session-id run-id execution-token] - (let [execution {:run-id run-id - :execution-token execution-token - :cancelled? (atom false) - :cancel-chan (chan)}] + (let [path [:sessions session-id :factory-executions run-id execution-token] + execution (new-factory-execution run-id execution-token)] (when (identical? execution (get-in @@ -1125,11 +1130,9 @@ (fn [state] (let [session (get-in state [:sessions session-id])] (if (and session (not (:destroyed? session))) - (assoc-in state - [:sessions session-id :factory-executions run-id execution-token] - execution) + (assoc-in state path execution) state)))) - [:sessions session-id :factory-executions run-id execution-token])) + path)) execution))) (defn- remove-factory-execution! @@ -1159,6 +1162,44 @@ (vals (get-in @(:state client) [:sessions session-id :factory-executions run-id])))) +(defn- cancel-factory-execution! + [client session-id run-id execution-token] + (when-let [execution + (get-in @(:state client) + [:sessions session-id :factory-executions + run-id execution-token])] + (cancel-executions! [execution]))) + +(defn ^:no-doc prepare-factory-request! + "Apply order-sensitive factory bookkeeping before reverse-request dispatch." + [client method params] + (case method + "factory.execute" + (let [{:keys [session-id name run-id execution-token]} params + handle (get-in @(:state client) + [:sessions session-id :factories name]) + execution + (when handle + (register-factory-execution! + client session-id run-id execution-token))] + (cond-> {:params + (cond-> params + execution + (assoc ::prepared-factory-handle handle + ::prepared-factory-execution execution))} + execution + (assoc :on-reject + #(remove-factory-execution! + client session-id run-id execution-token execution)))) + + "factory.abort" + (let [{:keys [session-id run-id execution-token]} params] + (cancel-factory-execution! + client session-id run-id execution-token) + {:params (assoc params ::factory-abort-prepared? true)}) + + {:params params})) + (defn- throw-cleanup-failures! [failures] (when-let [primary (first failures)] @@ -1327,11 +1368,27 @@ (defn handle-factory-execute! "Execute an extension-authored factory for a runtime reverse RPC." [client session-id {:keys [name run-id execution-token] :as params}] - (async/thread-call - (fn [] - (if-let [handle (get-in @(:state client) [:sessions session-id :factories name])] - (if-let [execution (register-factory-execution! - client session-id run-id execution-token)] + (let [handle + (or (::prepared-factory-handle params) + (get-in @(:state client) [:sessions session-id :factories name])) + execution + (when handle + (or (::prepared-factory-execution params) + (register-factory-execution! + client session-id run-id execution-token)))] + (async/thread-call + (fn [] + (cond + (nil? handle) + {:error {:code -32602 + :message (str "No factory registered with name " (pr-str name)) + :data {:code "factory_not_found" :name name}}} + + (nil? execution) + {:error {:code -32001 + :message (str "Session has been disconnected: " session-id)}} + + :else (let [flush!* (volatile! nil)] (try (let [context (factory-context client session-id params execution) @@ -1361,18 +1418,30 @@ :run-id run-id :error (ex-message error)})))) (remove-factory-execution! - client session-id run-id execution-token execution)))) - {:error {:code -32001 - :message (str "Session has been disconnected: " session-id)}}) - {:error {:code -32602 - :message (str "No factory registered with name " (pr-str name)) - :data {:code "factory_not_found" :name name}}})) - :io)) + client session-id run-id execution-token execution)))))) + :io))) (defn handle-factory-abort! - "Cooperatively cancel active executions for a durable factory run." - [client session-id run-id] - (cancel-factory-executions! client session-id run-id) + "Cooperatively cancel factory executions. + + The four-arity form targets one execution attempt. The three-arity form + retains run-wide cancellation for direct callers of this low-level handler." + ([client session-id run-id] + (cancel-factory-executions! client session-id run-id) + (let [result (chan 1)] + (put! result {:result {}}) + (close! result) + result)) + ([client session-id run-id execution-token] + (cancel-factory-execution! client session-id run-id execution-token) + (let [result (chan 1)] + (put! result {:result {}}) + (close! result) + result))) + +(defn ^:no-doc prepared-factory-abort-response + "Acknowledge an abort already applied by [[prepare-factory-request!]]." + [] (let [result (chan 1)] (put! result {:result {}}) (close! result) diff --git a/src/github/copilot_sdk/specs.clj b/src/github/copilot_sdk/specs.clj index e08ac8b..5b0f2ff 100644 --- a/src/github/copilot_sdk/specs.clj +++ b/src/github/copilot_sdk/specs.clj @@ -1801,6 +1801,9 @@ :copilot/external_tool.requested :copilot/external_tool.completed ;; MCP OAuth events :copilot/mcp.oauth_required :copilot/mcp.oauth_completed + ;; MCP dynamic-header refresh lifecycle + :copilot/mcp.headers_refresh_required + :copilot/mcp.headers_refresh_completed ;; Command events :copilot/command.queued :copilot/command.execute :copilot/command.completed :copilot/commands.changed @@ -2043,13 +2046,15 @@ (s/valid? ::assistant-message-tool-request-type (:type %))))) (s/def ::tool-requests (s/coll-of ::assistant-message-tool-request :kind vector?)) +(s/def ::originating-message-id string?) (s/def ::assistant.message-data (s/keys :req-un [::message-id ::content] :opt-un [::tool-requests ::parent-tool-call-id ::encrypted-content ::interaction-id ::output-tokens ::phase ::reasoning-opaque ::reasoning-text ::request-id ::api-call-id ::server-tools ::service-request-id ::turn-id ::model - ::chunk-index ::chunk-count ::rte])) + ::chunk-index ::chunk-count ::rte + ::originating-message-id])) (s/def ::total-response-size-bytes nat-int?) (s/def ::turn-id ::non-blank-string) @@ -2153,12 +2158,16 @@ (s/def ::mcp-server-name string?) (s/def ::mcp-tool-name string?) +(s/def ::mcp-config-server-name string?) +(s/def ::mcp-config-source + #{"user" "workspace" "plugin" "builtin" "managed"}) (s/def ::mcp-transport #{"stdio" "http" "sse" "memory"}) (s/def ::tool.execution_start-data (s/and (s/keys :req-un [::tool-call-id ::tool-name] :opt-un [::parent-tool-call-id ::mcp-server-name ::mcp-tool-name + ::mcp-config-server-name ::mcp-config-source ::mcp-transport ::model]) #(optional-field? % :arguments opaque-json-value?))) @@ -2499,7 +2508,8 @@ (s/keys :req-un [::tool-call-id ::agent-name ::agent-display-name ::agent-description] :opt-un [::factory-run-id ::model ::resumable - ::agent-type ::execution-mode ::task-model-source]) + ::agent-type ::execution-mode ::task-model-source + ::model-selection-source]) #(or (not (contains? % :parent-id)) (s/valid? ::subagent-parent-id (:parent-id %))))) @@ -2576,9 +2586,11 @@ (s/keys :req-un [::instructions])) (s/def ::mcp-loaded-server (s/and (s/keys :req-un [::name ::status] - :opt-un [::source ::plugin-name ::plugin-version + :opt-un [::display-name ::source ::plugin-name ::plugin-version ::server-metadata]) #(optional-field? % :error string?) + #(optional-field? % :source + (partial s/valid? ::mcp-config-source)) #(s/valid? ::mcp-server-status (:status %)))) (s/def ::servers (s/coll-of ::mcp-loaded-server)) (s/def ::session.mcp_servers_loaded-data @@ -2752,6 +2764,23 @@ (s/merge ::base-event (s/keys :req-un [::event-type ::data]))) +(s/def ::mcp-headers-refresh-required-reason + #{"auth-failed" "startup" "ttl-expired"}) +(s/def ::mcp-headers-refresh-completed-outcome + #{"headers" "none" "error" "timeout"}) +(s/def ::mcp.headers_refresh_required-data + (s/and + map? + #(s/valid? ::request-id (:request-id %)) + #(s/valid? ::server-name (:server-name %)) + #(string? (:server-url %)) + #(s/valid? ::mcp-headers-refresh-required-reason (:reason %)))) +(s/def ::mcp.headers_refresh_completed-data + (s/and + map? + #(s/valid? ::request-id (:request-id %)) + #(s/valid? ::mcp-headers-refresh-completed-outcome (:outcome %)))) + ;; ----------------------------------------------------------------------------- ;; Tool call/result types ;; ----------------------------------------------------------------------------- diff --git a/test/github/copilot_sdk/codegen_test.clj b/test/github/copilot_sdk/codegen_test.clj index 6e0a380..d4fa1bf 100644 --- a/test/github/copilot_sdk/codegen_test.clj +++ b/test/github/copilot_sdk/codegen_test.clj @@ -449,6 +449,16 @@ {:server-name "server-1" :status "connected"} + "mcp.headers_refresh_required" + {:request-id "refresh-1" + :server-name "server-1" + :server-url "https://example.test/mcp" + :reason "ttl-expired"} + + "mcp.headers_refresh_completed" + {:request-id "refresh-1" + :outcome "error"} + "permission.requested" {:request-id "permission-1" :permission-request {:kind "memory" diff --git a/test/github/copilot_sdk/integration/factory_test.clj b/test/github/copilot_sdk/integration/factory_test.clj index c8f2fd5..08027be 100644 --- a/test/github/copilot_sdk/integration/factory_test.clj +++ b/test/github/copilot_sdk/integration/factory_test.clj @@ -313,11 +313,170 @@ *mock-server* "factory.abort" {:sessionId session-id - :runId "run-abort"})))) + :runId "run-abort" + :executionToken "attempt-1"})))) (is (= {:cancelled true} (get-in (deref execution 1000 :github.copilot-sdk.integration-test/timeout) [:result :result]))))) +(deftest test-agent-factory-abort-before-registration-is-observed + (let [handler-entered (promise) + allow-handler (promise) + observed-cancellation (promise) + handle + (factory/define-factory + {:meta {:name "early-abort" + :description "Observe an abort that overtakes registration" + :phases []} + :run + (fn [{:keys [cancel-chan cancelled?]}] + (let [observed {:cancelled? (cancelled?) + :channel-closed? + (async-protocols/closed? cancel-chan)}] + (deliver observed-cancellation observed) + observed))}) + session-id "factory-early-abort-session" + _ (session/create-session *test-client* session-id + {:config {:factories [handle]}}) + original-execute @#'session/handle-factory-execute!] + (with-redefs-fn + {#'session/handle-factory-execute! + (fn [& args] + (deliver handler-entered true) + @allow-handler + (apply original-execute args))} + (fn [] + (let [execution + (future + (mock/send-rpc-request! + *mock-server* + "factory.execute" + {:sessionId session-id + :name "early-abort" + :runId "run-early-abort" + :executionToken "attempt-1" + :args {}}))] + (try + (is (true? (deref handler-entered 1000 false))) + (let [registered + (get-in @(:state *test-client*) + [:sessions session-id :factory-executions + "run-early-abort" "attempt-1"])] + (is (some? registered)) + (is (false? @(:cancelled? registered))) + (is (= {} + (:result + (mock/send-rpc-request! + *mock-server* + "factory.abort" + {:sessionId session-id + :runId "run-early-abort" + :executionToken "attempt-1"})))) + (is (true? @(:cancelled? registered))) + (is (true? (async-protocols/closed? (:cancel-chan registered))))) + (finally + (deliver allow-handler true))) + (is (= {:cancelled? true + :channel-closed? true} + (deref observed-cancellation + 1000 + :github.copilot-sdk.integration-test/timeout))) + (is (= {:cancelled? true + :channel-closed? true} + (get-in (deref execution + 1000 + :github.copilot-sdk.integration-test/timeout) + [:result :result]))) + (is (empty? (get-in @(:state *test-client*) + [:sessions session-id :factory-executions])))))))) + +(deftest test-agent-factory-abort-miss-does-not-retain-state + (let [handle + (factory/define-factory + {:meta {:name "abort-miss" + :description "No execution is active" + :phases []} + :run (fn [_] {:unreachable true})}) + session-id "factory-abort-miss-session" + _ (session/create-session *test-client* session-id + {:config {:factories [handle]}})] + (doseq [execution-token ["missing-attempt" nil]] + (is (= {:result {}} + (> (str/replace body #"(?s)/\*.*?\*/|//[^\n]*" "") + (re-seq #"[\"']([^\"']+)[\"']") + (map second) + set))) + (def ^:private exported-declaration-pattern #"(?m)^\s*export\s+(type|interface)\s+([A-Za-z_$][A-Za-z0-9_$]*)\b") diff --git a/test/github/copilot_sdk/reverse_rpc_test.clj b/test/github/copilot_sdk/reverse_rpc_test.clj index c6a6224..bee7fa8 100644 --- a/test/github/copilot_sdk/reverse_rpc_test.clj +++ b/test/github/copilot_sdk/reverse_rpc_test.clj @@ -4,6 +4,7 @@ These tests drive the real NIO/JSON-RPC transport over piped streams -- no protocol mocking -- and assert the bounded worker contract: + - order-sensitive request preparation runs inline on the reader thread - arbitrary handler code runs on the connection's own bounded worker pool, never on core.async `go` dispatch - concurrency never exceeds the configured bound @@ -160,6 +161,37 @@ (.countDown release) (protocol/disconnect conn)))))) +(deftest test-request-preparer-runs-before-worker-dispatch + (testing "order-sensitive preparation runs on the reader before worker handling" + (let [{:keys [conn ->client <-client]} (open-connection {}) + prepared (promise) + handled (promise)] + (try + (protocol/set-request-dispatch! + conn + (fn [_method params] + (deliver handled {:params params + :thread (.getName (Thread/currentThread))}) + (delivered {:result {}})) + (fn [method params] + (deliver prepared {:method method + :thread (.getName (Thread/currentThread))}) + {:params (assoc params :prepared true)})) + (write-framed! ->client (request "r1" "factory.execute" {:value 1})) + (let [{prepared-thread :thread + prepared-method :method} + (deref prepared 2000 ::timeout) + {handled-thread :thread + handled-params :params} + (deref handled 2000 ::timeout)] + (is (= "factory.execute" prepared-method)) + (is (not (str/starts-with? prepared-thread worker-thread-name-prefix))) + (is (str/starts-with? handled-thread worker-thread-name-prefix)) + (is (= {:value 1 :prepared true} handled-params))) + (is (not= ::timeout (collect-by-id! <-client 1 3000))) + (finally + (protocol/disconnect conn)))))) + (deftest test-concurrency-is-bounded (testing "no more handlers run concurrently than :request-handler-threads" (let [{:keys [conn ->client]} (open-connection {:request-handler-threads 2 @@ -189,10 +221,15 @@ (let [{:keys [conn ->client <-client]} (open-connection {:request-handler-threads 2 :request-handler-queue-size 2}) release (CountDownLatch. 1) - entered (atom 0)] + entered (atom 0) + rejected-preparations (atom 0)] (try - (protocol/set-request-handler! - conn (blocking-handler {:entered entered :release release})) + (protocol/set-request-dispatch! + conn + (blocking-handler {:entered entered :release release}) + (fn [_method params] + {:params params + :on-reject #(swap! rejected-preparations inc)})) ;; 2 execute, 2 queue, the 5th has nowhere to go. (dotimes [i 5] (write-framed! ->client (request (str "r" i) "hooks.invoke" {}))) @@ -208,6 +245,7 @@ (is (= 2 (get-in msg [:error :data :maxConcurrency]))) (is (= 2 (get-in msg [:error :data :queueSize])))))) (is (= 1 (:rejected-requests (connection-stats conn)))) + (is (= 1 @rejected-preparations)) (testing "accepted requests still complete once handlers unblock" (.countDown release) (let [responses (collect-by-id! <-client 4 5000)] diff --git a/test/resources/stable_upstream_delta_e9df3938.edn b/test/resources/stable_upstream_delta_e9df3938.edn new file mode 100644 index 0000000..52568d8 --- /dev/null +++ b/test/resources/stable_upstream_delta_e9df3938.edn @@ -0,0 +1,696 @@ +{:certification + {:clojure-base-commit "27bd8e8353964eb10e425bfaef84683e9c013f2e" + :historical-oracle + {:resource "resources/stable_upstream_delta_f45c46fd.edn" + :sha256 + "75800967a04b20a5a17ba6edae7d20ba4ce52eabea2dc301c950f4d3eda5dc82"}} + + :upstream + {:base-commit "f45c46fd1812f8bed5b4cbc250f47177c83068f0" + :target-commit "e9df3938b0f2bb028b203f4095d48b75f155c008" + :commit-count 16 + :commits-sha256 + "0fd64deb00fe2a4ff0e52e5f2f6b0a40f40183aaf6781e4003c0104e7dcbada6" + :hash-format :newline-joined-without-trailing-newline + :target-package-version "0.0.0-dev" + :runtime-version "1.0.84-8"} + + :commit-classifications + [{:commit "8aab138e899dde9426aec6a671a824790e780089" + :subject "Disable failing Java docs deployment (#2653)" + :source-url "https://github.com/github/copilot-sdk/pull/2653" + :classification :internal + :status :reviewed-no-port + :reason "Disables a repository Java documentation workflow." + :changed-path-count 1 + :changed-paths-sha256 + "3a6b64f37927ae56132274fe6e469afb5529a7a1ebc041ccfa53f0890492e272"} + {:commit "ef64ceaf881ccc0e694be3d24101641bf9938f1a" + :subject "Fix SDK test completion races and timeout cleanup (#2642)" + :source-url "https://github.com/github/copilot-sdk/pull/2642" + :classification :internal + :status :reviewed-no-port + :reason "Changes cross-SDK test harness synchronization and timeout diagnostics." + :changed-path-count 47 + :changed-paths-sha256 + "01c489725406058581cc3f3507a522f2c68cc856d1ae297312e91ed3d9fec3a6"} + {:commit "ad0d5e7299454cadec20cd0f015cd408a2b7ab7b" + :subject "docs: update version references to 1.0.14-preview.0" + :source-url "https://github.com/github/copilot-sdk/commit/ad0d5e7299454cadec20cd0f015cd408a2b7ab7b" + :classification :language-specific + :status :reviewed-no-port + :reason "Updates Java preview-release documentation only." + :changed-path-count 2 + :changed-paths-sha256 + "f948844641038add31a81e2623ebac512d7d1686fdeb60f1191b92c117742772"} + {:commit "7bd35d769652cb73960353288707c589b7d04524" + :subject "[maven-release-plugin] prepare release java/v1.0.14-preview.0" + :source-url "https://github.com/github/copilot-sdk/commit/7bd35d769652cb73960353288707c589b7d04524" + :classification :language-specific + :status :reviewed-no-port + :reason "Updates Java release metadata." + :changed-path-count 3 + :changed-paths-sha256 + "e82a33e17eaa61919d80232a6690c48963cd2e1ab5e3bb7f22eae122c6ce7668"} + {:commit "bab21f2f599e933e5657ee364b6fa75d0794363e" + :subject "[maven-release-plugin] prepare for next development iteration" + :source-url "https://github.com/github/copilot-sdk/commit/bab21f2f599e933e5657ee364b6fa75d0794363e" + :classification :language-specific + :status :reviewed-no-port + :reason "Updates Java development-version metadata." + :changed-path-count 3 + :changed-paths-sha256 + "e82a33e17eaa61919d80232a6690c48963cd2e1ab5e3bb7f22eae122c6ce7668"} + {:commit "557e496195c5c8dc1a964812f25790636930763e" + :subject "[Core] Add Factory Pause Checkpoints (#2537)" + :source-url "https://github.com/github/copilot-sdk/pull/2537" + :classification :experimental + :status :ported-compatible-fix + :reason + "Adds experimental factory pause/checkpoint APIs. Those APIs remain excluded; the execution-token abort routing fix is ported because the existing Clojure factory runtime already receives the same reverse RPC." + :changed-path-count 7 + :changed-paths-sha256 + "777bfb4b7dd2acaebacf24f6628449462cf6beda0f8556e394b506c0c013c848"} + {:commit "d494785763c6a5a93e37916cee6572f0d6943c16" + :subject "docs: update version references to 1.0.14-preview.1" + :source-url "https://github.com/github/copilot-sdk/commit/d494785763c6a5a93e37916cee6572f0d6943c16" + :classification :language-specific + :status :reviewed-no-port + :reason "Updates Java preview-release documentation only." + :changed-path-count 2 + :changed-paths-sha256 + "f948844641038add31a81e2623ebac512d7d1686fdeb60f1191b92c117742772"} + {:commit "81be283a5d9b043fcb0d115bb5f8c7adf7e561eb" + :subject "[maven-release-plugin] prepare release java/v1.0.14-preview.1" + :source-url "https://github.com/github/copilot-sdk/commit/81be283a5d9b043fcb0d115bb5f8c7adf7e561eb" + :classification :language-specific + :status :reviewed-no-port + :reason "Updates Java release metadata." + :changed-path-count 3 + :changed-paths-sha256 + "e82a33e17eaa61919d80232a6690c48963cd2e1ab5e3bb7f22eae122c6ce7668"} + {:commit "dcf488292b1f7852efe7da79de378836e1e144ab" + :subject "[maven-release-plugin] prepare for next development iteration" + :source-url "https://github.com/github/copilot-sdk/commit/dcf488292b1f7852efe7da79de378836e1e144ab" + :classification :language-specific + :status :reviewed-no-port + :reason "Updates Java development-version metadata." + :changed-path-count 3 + :changed-paths-sha256 + "e82a33e17eaa61919d80232a6690c48963cd2e1ab5e3bb7f22eae122c6ce7668"} + {:commit "8d1545db2e33a4e1435c55ca96a02a8059c2fa07" + :subject "Fix C# codegen for runtime schema unions (#2656)" + :source-url "https://github.com/github/copilot-sdk/pull/2656" + :classification :generated-only + :status :reviewed-no-port + :reason "Changes C# generator behavior and its generated validation." + :changed-path-count 2 + :changed-paths-sha256 + "e9d04a7ecbd947086533df525933f6b5bb4a7412cde35d3c5d4f2450becdd6af"} + {:commit "e5c53790c6a8aa253aa3887ebb8958646bd60452" + :subject "Update Copilot CLI to 1.0.84-6 (#2658)" + :source-url "https://github.com/github/copilot-sdk/pull/2658" + :classification :stable-public + :status :ported + :reason + "Adds stable event provenance, managed MCP source metadata, the MCP refresh error outcome, and durable skill-output elision semantics alongside generated and experimental changes." + :changed-path-count 64 + :changed-paths-sha256 + "18ea2472eda96b8ae2e10ef85f914d8d5c46c3a1bd600650ec27a223479387ee"} + {:commit "22e860719494262832504b1301237c05b0f4fab8" + :subject "[Java] Replace maven-release-plugin with CI-friendly versions (#2603)" + :source-url "https://github.com/github/copilot-sdk/pull/2603" + :classification :language-specific + :status :reviewed-no-port + :reason "Changes Java publishing and release tooling." + :changed-path-count 15 + :changed-paths-sha256 + "641cc4ccae645cbdff66441cd207cb9ea8a207e2a41e4c5083ec87ff5c131546"} + {:commit "c03400d12d2175efdc557ef9eb04b36d0d055ff3" + :subject "Update Copilot CLI to 1.0.84-8 (#2662)" + :source-url "https://github.com/github/copilot-sdk/pull/2662" + :classification :stable-public + :status :regenerated + :reason + "Advances the bundled runtime/schema pin to 1.0.84-8; its additional RPC declarations are generated wire evidence rather than new stable Node SDK APIs." + :changed-path-count 31 + :changed-paths-sha256 + "231f7d0fc3eba340f23fd07549477f3efb7486a6c214f83feb5e23608794510a"} + {:commit "a675b55531a9dfc647ee015e32d74279568550f3" + :subject "Stabilize C# rewind E2E initialization (#2661)" + :source-url "https://github.com/github/copilot-sdk/pull/2661" + :classification :language-specific + :status :reviewed-no-port + :reason "Changes C# end-to-end test initialization." + :changed-path-count 1 + :changed-paths-sha256 + "dc27e1a2f1c1866d9dfb726d12a64594ad3f0b78e4d7d0ea994a265cecd14e51"} + {:commit "9553d5224c73df2d02aee5ec01eeb8353acc736a" + :subject "Isolate hostless runtime cache (#2659)" + :source-url "https://github.com/github/copilot-sdk/pull/2659" + :classification :language-specific + :status :reviewed-no-port + :reason "Changes Rust hostless runtime cache isolation and resolution tests." + :changed-path-count 6 + :changed-paths-sha256 + "0c44831ef5a8d357a198ef52441b472144b925fab615498c5db1351320afff6a"} + {:commit "e9df3938b0f2bb028b203f4095d48b75f155c008" + :subject "Publish .NET SDK to the internal feed (#2614)" + :source-url "https://github.com/github/copilot-sdk/pull/2614" + :classification :language-specific + :status :reviewed-no-port + :reason "Adds .NET publication steps to the repository release workflow." + :changed-path-count 1 + :changed-paths-sha256 + "551d1fcf87f78cc3bc18a7b332a4dc5d8773a512062df881c5aba28a6f5c48d7"}] + + :changed-paths + {:count 157 + :sha256 + "0e499a8d33d295d714f737cd3b806c9441c1f5a536fbcd19775269c93d2f40cc" + :hash-format :newline-joined-without-trailing-newline + :classification-counts + {:stable-public 1 + :experimental 7 + :generated-only 4 + :internal 16 + :language-specific 129} + :exact-classifications + {"nodejs/src/generated/session-events.ts" :stable-public + + "nodejs/docs/factories.md" :experimental + "nodejs/src/factory.ts" :experimental + "nodejs/src/index.ts" :experimental + "nodejs/src/session.ts" :experimental + "nodejs/test/e2e/factory.e2e.test.ts" :experimental + "nodejs/test/e2e/fixtures/factory-extension.mjs" :experimental + "nodejs/test/factory.test.ts" :experimental + + "nodejs/src/generated/rpc.ts" :generated-only + "nodejs/test/csharp-codegen.test.ts" :generated-only + "scripts/codegen/csharp.ts" :generated-only + "scripts/codegen/rust.ts" :generated-only + + ".github/workflows/java-publish-maven.yml" :internal + ".github/workflows/java-publish-snapshot.yml" :internal + ".github/workflows/java-sdk-tests.yml" :internal + ".github/workflows/java-smoke-test.yml" :internal + ".github/workflows/publish.yml" :internal + ".github/workflows/required-checks.yml" :internal + "docs/developer-docs/secrets.md" :internal + "nodejs/package.json" :internal + "nodejs/src/cliVersion.ts" :internal + "nodejs/test/e2e/harness/sdkTestHelper.ts" :internal + "nodejs/test/e2e/mcp_oauth.e2e.test.ts" :internal + "nodejs/test/e2e/permissions.e2e.test.ts" :internal + "nodejs/test/e2e/session.e2e.test.ts" :internal + "nodejs/test/e2e/session_lifecycle.e2e.test.ts" :internal + "nodejs/test/e2e/telemetry.e2e.test.ts" :internal + "nodejs/test/session-send-and-wait.test.ts" :internal} + :language-specific-prefixes + #{"dotnet/" "go/" "java/" "python/" "rust/"}} + + :target-public-surface + {:source-blobs + {"nodejs/package.json" + {:base "8805d0d4d86381e1b103b23199c0c61d62339b00" + :target "152726d2064c9e8e713afe057b0b2631ca2cb49c"} + "nodejs/src/index.ts" + {:base "6251df4fc766e40730a57d5f668a91efbd5bed73" + :target "8a5a730b5a5cb8f329b245298e132c7a77e085d5"} + "nodejs/src/types.ts" + {:base "9c4258d9c92b9ad396bf25bb070386ef3d51f258" + :target "9c4258d9c92b9ad396bf25bb070386ef3d51f258"} + "nodejs/src/client.ts" + {:base "6e4b4fb5b4e9b656a21c0fc5fd6988a848ae8501" + :target "6e4b4fb5b4e9b656a21c0fc5fd6988a848ae8501"} + "nodejs/src/session.ts" + {:base "4c2be1429921b0c6cb397b2d45266c1b41b3dc7d" + :target "896eded0bda69f9e08454ea8866fb1c335ce2415"} + "nodejs/src/extension.ts" + {:base "ac0ccdb7ce3102cabc98e8457f4c7f684b76fa28" + :target "ac0ccdb7ce3102cabc98e8457f4c7f684b76fa28"} + "nodejs/src/toolSet.ts" + {:base "559e9234e5b178036d88a7045a632bd16a97f534" + :target "559e9234e5b178036d88a7045a632bd16a97f534"} + "nodejs/src/factory.ts" + {:base "6212f462b4aff716417baf2180b9a8b887c12ad1" + :target "174318394f98c735e54f9bd80d75901e77ce17fa"} + "nodejs/src/generated/session-events.ts" + {:base "5bd67b80423643ec7a6416d6e331f856e3ff71ac" + :target "08bf03b5d74cd4af78cad67652e2cd1d1b8026c5"} + "nodejs/src/generated/rpc.ts" + {:base "09bfc8ffa750cfa138eb4a6f2a882db891fb736e" + :target "b0bbbb56e4e58cc9ff57b4498763e83a29bd425f"}} + :trees + {"nodejs" + {:base "e4992f186074f412dee23eeda89ca454f9512b09" + :target "0a2a277deba48afe26765af5a173be23315462e3"} + "nodejs/src/generated" + {:base "c0acf2163f88205d06f4ec3102cd76cdc242cc83" + :target "27e9418fbe6f6c98e9aed1445b11aae9633245e4"} + "nodejs/test" + {:base "d089de141f21082bf28338fe520e78702a3c8987" + :target "0471055418b2be08dafc463b322f2276afc3e414"}} + :package-root + {:path "nodejs/src/index.ts" + :explicit-symbol-count 200 + :explicit-symbols-sha256 + "1b2816aedc2309f5558aefec744b9b88e9343cad2a14ad5d6c4622b7063c637c" + :session-events + {:path "nodejs/src/generated/session-events.ts" + :symbol-count 546 + :symbols-sha256 + "a115d977b4aedf9618f1004ccb20b4655f27e5a23c06dcd6de4e7258c9515e8f"} + :symbol-count 736 + :symbols-sha256 + "3de6498a42d7b61980030896e1fca113a05dbe1371c5596e6bd271c1e7f7ce46" + :added + {:experimental #{"FactoryLimitOverrides" + "ToolExecutionCompleteShellExecution"}} + :removed #{}} + :types + {:path "nodejs/src/types.ts" + :symbol-count 207 + :symbols-sha256 + "ac251977aae64ea16c156a4d8d7a10dab4d5a3a89bd201bf5856459cd668aeff"} + :extension + {:path "nodejs/src/extension.ts" + :symbol-count 39 + :symbols-sha256 + "13316e0859171d9c8be79a0bdbe2626b9c21a1947dae04a24fc51ddede17b52c"} + :tool-set + {:path "nodejs/src/toolSet.ts" + :symbol-count 2 + :symbols-sha256 + "99ef0910d8d65bfb0074e664122cd7d9642aa6c89d8d476bb26d0a9ce1d748ca"} + :factory + {:path "nodejs/src/factory.ts" + :symbol-count 29 + :symbols-sha256 + "ba187595890c21116008b7276801db3a64510815df0c744e32e85c20c961afba"} + :classes + {:client + {:path "nodejs/src/client.ts" + :class-name "CopilotClient" + :method-count 20 + :methods-sha256 + "bcf31a8e931df3230402c11670d9e14dd03b90564fb6e7192356d746edd273e6"} + :session + {:path "nodejs/src/session.ts" + :class-name "CopilotSession" + :method-count 41 + :methods-sha256 + "e3d8d11b11ae017b7485e25197283d41f64aac2be2a582c73cc96be3685480bb"}}} + + :symbol-inventory + {:added-exported-symbols + {"nodejs/src/index.ts" + {:experimental #{"FactoryLimitOverrides"}} + "nodejs/src/factory.ts" + {:experimental #{"FactoryLimitOverrides"}} + "nodejs/src/generated/session-events.ts" + {:experimental #{"ToolExecutionCompleteShellExecution"}} + "nodejs/src/generated/rpc.ts" + {:generated-only + #{"JsonSchemaResponseFormat" + "ManagedMcpServerConfig" + "ProtocolAppendMode" + "ProtocolCustomizeMode" + "ProtocolMarkerSectionOverride" + "ProtocolReplaceMode" + "ProtocolSectionOverride" + "ProtocolStaticSectionAction" + "ProtocolStaticSectionOverride" + "ProtocolSystemMessageAppendConfig" + "ProtocolSystemMessageConfig" + "ProtocolSystemMessageCustomizeConfig" + "ProtocolSystemMessageReplaceConfig" + "ResponseFormat" + "SandboxSessionChange" + "SystemMessageBlock" + "WorkspacesCreateDirectoryRequest" + "WorkspacesRemovePathRequest" + "WorkspacesRenamePathRequest" + "WorkspacesStatFileRequest" + "WorkspacesStatFileResult"}}} + :removed-exported-symbols + {"nodejs/src/generated/rpc.ts" + {:generated-only + #{"RegisterExtensionToolsParams" + "RegisterExtensionToolsResult" + "SessionsRegisterExtensionToolsOnSessionOptions"}}} + :event-interface-fields + {"AssistantMessageData" + {:stable-public #{"originatingMessageId"}} + "ToolExecutionStartData" + {:stable-public #{"mcpConfigServerName" "mcpConfigSource"}} + "ToolExecutionCompleteData" + {:experimental #{"shellExecution"}} + "SubagentStartedData" + {:stable-public #{"modelSelectionSource"}} + "McpServersLoadedServer" + {:stable-public #{"displayName"}}} + :changed-type-values + {"McpHeadersRefreshCompletedOutcome" + {:stable-public #{"error"}} + "McpServerSource" + {:stable-public #{"managed"}}} + :changed-declarations + {"nodejs/src/factory.ts" + {:experimental + #{"FactoryContext" "ResumeOptions" "RunOptions" "SessionFactoryApi"}} + "nodejs/src/generated/session-events.ts" + {:stable-public + #{"AssistantMessageData" + "McpHeadersRefreshCompletedOutcome" + "McpServerSource" + "McpServersLoadedServer" + "SubagentStartedData" + "ToolExecutionStartData"} + :experimental #{"ToolExecutionCompleteData"}} + "nodejs/src/generated/rpc.ts" + {:generated-only + #{"AdaptiveThinkingSupport" + "DebugCollectLogsRedaction" + "InstalledPluginInfo" + "McpHeadersHandlePendingHeadersRefreshRequest" + "McpServer" + "PermissionModeSource" + "ProviderModelConfig" + "SandboxConfigUserPolicyNetwork" + "SendMessagesRequest" + "SendRequest" + "SessionOpenOptions" + "ShutdownRequest" + "SlashCommandTextResult"}}}} + + :stable-delta-ids + #{:events/assistant-originating-message + :events/mcp-headers-refresh-error + :events/mcp-managed-source + :events/mcp-server-display-name + :events/mcp-tool-provenance + :events/skill-output-elision + :events/subagent-model-selection-source + :runtime/schema-1.0.84-8} + + :stable-deltas + [{:id :runtime/schema-1.0.84-8 + :classification :stable-public + :status :regenerated + :upstream-pr 2662 + :evidence [:runtime-version] + :inventory-items [] + :clojure-paths + [".copilot-schema-version" + "schemas/README.md" + "schemas/api.schema.json" + "schemas/session-events.schema.json" + "src/github/copilot_sdk/generated/event_specs.clj"]} + {:id :events/assistant-originating-message + :classification :stable-public + :status :ported + :upstream-pr 2658 + :evidence [:assistant-originating-message] + :inventory-items + [[:event-interface-field "AssistantMessageData" "originatingMessageId"] + [:changed-declaration + "nodejs/src/generated/session-events.ts" + "AssistantMessageData"]] + :contract + {:node-type "AssistantMessageData" + :wire-key "originatingMessageId" + :clojure-key :originating-message-id + :omission :preserved + :explicit-nil :rejected} + :clojure-paths + ["src/github/copilot_sdk/specs.clj" + "test/github/copilot_sdk/integration/stable_sync_e9df3938_test.clj" + "doc/reference/API.md"]} + {:id :events/mcp-tool-provenance + :classification :stable-public + :status :ported + :upstream-pr 2658 + :evidence [:mcp-tool-provenance] + :inventory-items + [[:event-interface-field + "ToolExecutionStartData" + "mcpConfigServerName"] + [:event-interface-field "ToolExecutionStartData" "mcpConfigSource"] + [:changed-declaration + "nodejs/src/generated/session-events.ts" + "ToolExecutionStartData"]] + :contract + {:node-type "ToolExecutionStartData" + :wire-keys #{"mcpConfigServerName" "mcpConfigSource"} + :clojure-keys #{:mcp-config-server-name :mcp-config-source} + :omission :preserved + :explicit-nil :rejected} + :clojure-paths + ["src/github/copilot_sdk/specs.clj" + "test/github/copilot_sdk/integration/stable_sync_e9df3938_test.clj" + "doc/reference/API.md"]} + {:id :events/mcp-managed-source + :classification :stable-public + :status :ported + :upstream-pr 2658 + :evidence [:mcp-managed-source] + :inventory-items + [[:changed-type-value "McpServerSource" "managed"] + [:changed-declaration + "nodejs/src/generated/session-events.ts" + "McpServerSource"]] + :contract + {:node-type "McpServerSource" + :values #{"user" "workspace" "plugin" "builtin" "managed"} + :explicit-nil :rejected} + :clojure-paths + ["src/github/copilot_sdk/specs.clj" + "test/github/copilot_sdk/integration/stable_sync_e9df3938_test.clj" + "doc/reference/API.md"]} + {:id :events/mcp-headers-refresh-error + :classification :stable-public + :status :ported + :upstream-pr 2658 + :evidence [:mcp-refresh-error] + :inventory-items + [[:changed-type-value "McpHeadersRefreshCompletedOutcome" "error"] + [:changed-declaration + "nodejs/src/generated/session-events.ts" + "McpHeadersRefreshCompletedOutcome"]] + :contract + {:node-type "McpHeadersRefreshCompletedOutcome" + :values #{"headers" "none" "error" "timeout"} + :explicit-nil :rejected} + :clojure-paths + ["src/github/copilot_sdk.clj" + "src/github/copilot_sdk/specs.clj" + "test/github/copilot_sdk/integration/stable_sync_e9df3938_test.clj" + "doc/reference/API.md"]} + {:id :events/subagent-model-selection-source + :classification :stable-public + :status :ported + :upstream-pr 2658 + :evidence [:subagent-model-selection] + :inventory-items + [[:event-interface-field "SubagentStartedData" "modelSelectionSource"] + [:changed-declaration + "nodejs/src/generated/session-events.ts" + "SubagentStartedData"]] + :contract + {:node-type "SubagentStartedData" + :wire-key "modelSelectionSource" + :clojure-key :model-selection-source + :omission :preserved + :explicit-nil :rejected} + :clojure-paths + ["src/github/copilot_sdk/specs.clj" + "test/github/copilot_sdk/integration/stable_sync_e9df3938_test.clj" + "doc/reference/API.md"]} + {:id :events/mcp-server-display-name + :classification :stable-public + :status :ported + :upstream-pr 2658 + :evidence [:mcp-server-display-name] + :inventory-items + [[:event-interface-field "McpServersLoadedServer" "displayName"] + [:changed-declaration + "nodejs/src/generated/session-events.ts" + "McpServersLoadedServer"]] + :contract + {:node-type "McpServersLoadedServer" + :wire-key "displayName" + :clojure-key :display-name + :omission :preserved + :explicit-nil :rejected} + :clojure-paths + ["src/github/copilot_sdk/specs.clj" + "test/github/copilot_sdk/integration/stable_sync_e9df3938_test.clj" + "doc/reference/API.md"]} + {:id :events/skill-output-elision + :classification :stable-public + :status :documented + :upstream-pr 2658 + :evidence [:skill-output-elision] + :inventory-items [] + :contract + {:node-type "ToolExecutionCompleteResult" + :behavior + "Successful skill invocations may retain concise model-facing detailedContent while the corresponding skill event owns the authoritative body."} + :clojure-paths ["doc/reference/API.md"]}] + + :compatibility-deltas + [{:id :factory/execution-token-abort + :classification :experimental + :status :ported + :upstream-pr 2537 + :evidence [:factory-execution-token-abort] + :reason + "The existing experimental Clojure factory runtime already accepts reverse factory.abort requests, so it must honor the required executionToken and isolate concurrent attempts sharing a durable run ID. Factory execute registration and abort cancellation are prepared inline in wire order before bounded worker dispatch, matching Node's synchronous pre-await registration without blocking arbitrary factory work on the protocol reader." + :clojure-paths + ["src/github/copilot_sdk/client.clj" + "src/github/copilot_sdk/session.clj" + "test/github/copilot_sdk/integration/factory_test.clj" + "doc/guides/agent-factories.md" + "doc/reference/API.md"]}] + + :intentional-exclusions + [{:id :factory/pause-checkpoints + :classification :experimental + :decision :exclude + :status :approved + :authority :upstream-experimental-subsystem + :reason + "FactoryLimitOverrides, FactoryContext.pause, SessionFactoryApi.pause, paused run status, and checkpoint behavior are explicitly experimental." + :evidence [:factory-limit-overrides :factory-pause] + :inventory-groups + [[:added-exported-symbols "nodejs/src/index.ts" :experimental] + [:added-exported-symbols "nodejs/src/factory.ts" :experimental] + [:changed-declarations "nodejs/src/factory.ts" :experimental]]} + {:id :events/tool-shell-execution + :classification :experimental + :decision :exclude + :status :approved + :authority :upstream-experimental-subsystem + :reason + "ToolExecutionCompleteData.shellExecution and its exported shape are explicitly experimental." + :evidence [:tool-shell-execution] + :inventory-groups + [[:added-exported-symbols + "nodejs/src/generated/session-events.ts" + :experimental] + [:event-interface-fields "ToolExecutionCompleteData" :experimental] + [:changed-declarations + "nodejs/src/generated/session-events.ts" + :experimental]]} + {:id :events/skill-reference-events + :classification :experimental + :decision :exclude + :status :approved + :authority :upstream-experimental-subsystem + :reason + "Skill invocation and context reference events are explicitly experimental wire events." + :evidence [:skill-reference-events] + :inventory-groups []} + {:id :rpc/generated-additions + :classification :generated-only + :decision :exclude + :status :approved + :authority :generated-wire-is-not-independent-api + :reason + "Response formats, managed MCP configuration, system-message protocol customization, shutdown hook detachment, sandbox changes, and workspace mutation RPCs are not exposed by the stable Node SDK at this pin." + :evidence [:generated-rpc] + :inventory-groups + [[:added-exported-symbols "nodejs/src/generated/rpc.ts" :generated-only] + [:removed-exported-symbols + "nodejs/src/generated/rpc.ts" + :generated-only] + [:changed-declarations "nodejs/src/generated/rpc.ts" :generated-only]]} + {:id :sandbox/network-host + :classification :internal + :decision :exclude + :status :approved + :authority :upstream-internal-implementation + :reason + "The network_host denial class belongs to an internal sandbox event." + :evidence [:sandbox-network-host] + :inventory-groups []} + {:id :other-sdks/language-specific + :classification :language-specific + :decision :exclude + :status :approved + :authority :language-scope + :reason + "Java, .NET, Go, Python, and Rust implementation, release, generated, and test changes do not create Clojure parity obligations." + :evidence [:language-specific-changes] + :inventory-groups []}] + + :decision-authorities + #{:generated-wire-is-not-independent-api + :language-scope + :upstream-experimental-subsystem + :upstream-internal-implementation} + + :source-evidence + {:runtime-version + {:path "nodejs/package.json" :contains "1.0.84-8"} + :factory-limit-overrides + {:path "nodejs/src/index.ts" :contains "FactoryLimitOverrides"} + :factory-pause + {:path "nodejs/src/factory.ts" :contains "pause(runId"} + :factory-execution-token-abort + {:path "nodejs/src/session.ts" :contains "executionToken"} + :assistant-originating-message + {:path "nodejs/src/generated/session-events.ts" + :contains "originatingMessageId"} + :mcp-tool-provenance + {:path "nodejs/src/generated/session-events.ts" + :contains "mcpConfigServerName"} + :mcp-managed-source + {:path "nodejs/src/generated/session-events.ts" :contains "\"managed\""} + :mcp-refresh-error + {:path "nodejs/src/generated/session-events.ts" + :contains "\"error\""} + :tool-shell-execution + {:path "nodejs/src/generated/session-events.ts" + :contains "shellExecution"} + :skill-output-elision + {:path "nodejs/src/generated/session-events.ts" + :contains "authoritative skill body"} + :subagent-model-selection + {:path "nodejs/src/generated/session-events.ts" + :contains "modelSelectionSource"} + :mcp-server-display-name + {:path "nodejs/src/generated/session-events.ts" + :contains "Human-readable display name"} + :skill-reference-events + {:path "schemas/session-events.schema.json" + :contains "\"SkillInvokedRefEvent\"" + :local true} + :sandbox-network-host + {:path "schemas/session-events.schema.json" + :contains "\"network_host\"" + :local true} + :generated-rpc + {:path "nodejs/src/generated/rpc.ts" :contains "ResponseFormat"} + :language-specific-changes + {:path ".github/workflows/publish.yml" :contains "dotnet"}} + + :local-artifacts + {".copilot-schema-version" + "fccb6f593bdc8422342c25c4adc3a6d223ff55c420a2a2a8fd54aa92dee4f24d" + "schemas/README.md" + "e6b15a843d36401d1c071ac4677691e860c3f996d6c4d59169c0c7156ca89d8d" + "schemas/api.schema.json" + "c8e04f9ad4ebed1e568c95243fce6f11ba7e5e92b934184632ded13ece4787f8" + "schemas/session-events.schema.json" + "0300faefeb301498acd29d00d504820fa7aabb903ce1e3ad56532ce0528cd7d0" + "src/github/copilot_sdk/generated/coerce.clj" + "4daf61b23897a37453aec06f9e778a5ed0114e484702389cfc3c33e7b62f938e" + "src/github/copilot_sdk/generated/event_metadata.clj" + "28da2c970c8cd1d758e8d89e8442ed41c9afec6ed28624b31bac81bb2a18dc1e" + "src/github/copilot_sdk/generated/event_specs.clj" + "7fd6b5ba0d635ef098407a310a1638d7d2d42dc1e087c5282b725359daf0f8a4" + "resources/github/copilot_sdk/api_surface.edn" + "d2e53642f706ecc71ea29d2501198e3a1e66bc0fb295543658e384fb962d3bf6"}}