diff --git a/.copilot-schema-version b/.copilot-schema-version index 8e17bf1..9f1a864 100644 --- a/.copilot-schema-version +++ b/.copilot-schema-version @@ -1 +1 @@ -1.0.56-1 +1.0.57 diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ea7fa4..ee53e7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,34 @@ All notable changes to this project will be documented in this file. This change ## [Unreleased] +### Added (v1.0.0-beta.12 sync) +- **`:context-tier` and `:reasoning-summary` on `switch-model!` / `set-model!`** + (upstream PR #1522). `:context-tier` accepts `:default` or `:long-context` + (wire-encoded as `contextTier` → `"default"` / `"long_context"`); + `:reasoning-summary` accepts `"none"` / `"concise"` / `"detailed"` + (wire-encoded as `reasoningSummary`). Mirrors the existing create/resume + session-config options. +- **`:model` field on `:copilot/tool.execution_start` event data** + (upstream npm `@github/copilot` 1.0.57). Added to `::tool.execution_start-data` + as an optional key, mirroring `::tool.execution_complete-data`. +- **`session.extensions.attachments_pushed` event + `extension_context` + attachment branch** — regenerated wire specs from the bumped schema + (upstream PR #1517). The new ephemeral event is not promoted to the curated + public `event-types` / `session-events` sets, consistent with the existing + canvas/extension surface. + +### Fixed (v1.0.0-beta.12 sync) +- **Preserve opaque `extension_context` attachment payloads** — `extension_context` + attachments (reachable on `user.message` events via `session.getMessages` and on + `session.extensions.attachments_pushed` events) carry an opaque `:payload` whose + keys must not be kebab-cased by `wire->clj`. The protocol layer now restores the + raw payload for these attachments on both the live notification and historical + response paths. + +### Changed (v1.0.0-beta.12 sync) +- Pinned schema bumped `1.0.56-1` → `1.0.57`; version synced to upstream release + `v1.0.0-beta.12` (`1.0.0-beta.12.0`). + ### Added (Client Mode Empty — upstream PR #1428) - **`:mode` client option** — `#{:copilot-cli :empty}`, default `:copilot-cli`. Selects between historical CLI behavior and a hardened diff --git a/README.md b/README.md index dc7fae1..6df3183 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Add to your `deps.edn`: ```clojure ;; From Maven Central -io.github.copilot-community-sdk/copilot-sdk-clojure {:mvn/version "1.0.0-beta.4.1"} +io.github.copilot-community-sdk/copilot-sdk-clojure {:mvn/version "1.0.0-beta.12.0"} ;; Or git dependency io.github.copilot-community-sdk/copilot-sdk-clojure {:git/url "https://github.com/copilot-community-sdk/copilot-sdk-clojure.git" diff --git a/build.clj b/build.clj index 4cca854..5bd374f 100644 --- a/build.clj +++ b/build.clj @@ -7,7 +7,7 @@ (:import [java.io File])) (def lib 'io.github.copilot-community-sdk/copilot-sdk-clojure) -(def version "1.0.0-beta.4.1") +(def version "1.0.0-beta.12.0") (def class-dir "target/classes") (defn- try-sh diff --git a/doc/reference/API.md b/doc/reference/API.md index 2d32f5f..a51ed9e 100644 --- a/doc/reference/API.md +++ b/doc/reference/API.md @@ -927,6 +927,8 @@ Switch the model for this session mid-conversation. Returns the new model ID str Optional opts map: - `:reasoning-effort` — Reasoning effort level ("low", "medium", "high", "xhigh") +- `:reasoning-summary` — Reasoning summary mode ("none", "concise", "detailed"). Wire-encoded as `reasoningSummary`. +- `:context-tier` — Context window tier for models that support it: `:default` or `:long-context` (upstream PR #1522). Wire-encoded as `contextTier` with values `"default"` / `"long_context"`. - `:model-capabilities` — Model capabilities override map, e.g. `{:model-supports {:supports-vision true}}` #### `set-model!` @@ -1416,7 +1418,7 @@ Convert an unqualified event keyword to a namespace-qualified `:copilot/` keywor | `:copilot/assistant.usage` | Token usage for this turn | | `: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`, `:parent-tool-call-id`, `:mcp-server-name`, `:mcp-tool-name` | +| `:copilot/tool.execution_start` | Tool execution started; data includes `:tool-call-id`, `:tool-name`, optional `:arguments`, `:parent-tool-call-id`, `:mcp-server-name`, `:mcp-tool-name`, `:model` | | `:copilot/tool.execution_progress` | Tool execution progress update | | `:copilot/tool.execution_partial_result` | Tool execution partial result | | `:copilot/tool.execution_complete` | Tool execution completed | diff --git a/schemas/README.md b/schemas/README.md index f904ea6..80979c8 100644 --- a/schemas/README.md +++ b/schemas/README.md @@ -4,4 +4,4 @@ These files are fetched verbatim from the `@github/copilot` npm package at the v **Do not edit by hand.** To update, run `bb schemas:fetch` after bumping `.copilot-schema-version`. -Currently pinned version: `1.0.56-1` +Currently pinned version: `1.0.57` diff --git a/schemas/api.schema.json b/schemas/api.schema.json index d4d59e2..bbd2e75 100644 --- a/schemas/api.schema.json +++ b/schemas/api.schema.json @@ -211,6 +211,16 @@ } } }, + "runtime": { + "shutdown": { + "rpcMethod": "runtime.shutdown", + "description": "Gracefully shuts down an SDK-owned runtime. The response is sent only after cleanup completes; callers may then terminate the owned runtime process.", + "params": null, + "result": { + "type": "null" + } + } + }, "sessionFs": { "setProvider": { "rpcMethod": "sessionFs.setProvider", @@ -531,8 +541,8 @@ "attachments": { "type": "array", "items": { - "$ref": "#/definitions/SendAttachment", - "description": "A user message attachment — a file, directory, code selection, blob, or GitHub reference" + "$ref": "#/definitions/Attachment", + "description": "A user message attachment — a file, directory, code selection, blob, GitHub reference, or extension-supplied context payload" }, "description": "Optional attachments (files, directories, selections, blobs, GitHub references) to include with the message" }, @@ -885,7 +895,7 @@ }, "result": { "$ref": "#/definitions/CurrentModel", - "description": "The currently selected model and reasoning effort for the session." + "description": "The currently selected model, reasoning effort, and context tier for the session. The context tier reflects `Session.getContextTier()`, restored from the session journal on resume." }, "stability": "experimental" }, @@ -916,23 +926,8 @@ "description": "Override individual model capabilities resolved by the runtime" }, "contextTier": { - "anyOf": [ - { - "type": "string", - "enum": [ - "default", - "long_context" - ], - "x-enumDescriptions": { - "default": "Use the model's default context window.", - "long_context": "Pin the session to the long-context tier when supported." - } - }, - { - "type": "null" - } - ], - "description": "Explicit context tier for the selected model. `\"default\"` / `\"long_context\"` pin the tier; `null` clears any previous explicit choice; `undefined` leaves the existing tier untouched." + "$ref": "#/definitions/ContextTier", + "description": "Explicit context tier for the selected model. `\"default\"` / `\"long_context\"` apply the requested tier; omit this field to use normal model behavior with no explicit tier." } }, "required": [ @@ -2736,6 +2731,10 @@ "type": "boolean", "description": "Whether to default custom agents to local-only execution." }, + "suppressCustomAgentPrompt": { + "type": "boolean", + "description": "When true, the selected custom agent's prompt is not injected into the user message (skill context is still injected). Used by automation triggers where the agent prompt is already in the problem statement." + }, "skipCustomInstructions": { "type": "boolean", "description": "Whether to skip loading custom instruction sources." @@ -2976,6 +2975,41 @@ "type": "null" }, "stability": "experimental" + }, + "sendAttachmentsToMessage": { + "rpcMethod": "session.extensions.sendAttachmentsToMessage", + "description": "Push attachments into the next user-message turn from an extension. The host should surface them as composer pills and forward them via the next session.send call. Callable only by extension-owned connections.", + "params": { + "type": "object", + "properties": { + "sessionId": { + "type": "string", + "description": "Target session identifier" + }, + "instanceId": { + "type": "string", + "description": "Optional canvas instance binding the push for provenance. When supplied, the runtime resolves the canvas, verifies it is owned by the calling extension, and stamps canvasId/instanceId onto each extension_context entry. When omitted, no resolution runs and those fields stay unset on the attachment." + }, + "attachments": { + "type": "array", + "items": { + "$ref": "#/definitions/PushAttachment" + }, + "description": "Attachments to push into the next user-message turn. extension_context entries take the slim shape; standard variants take their full AttachmentSchema shape." + } + }, + "required": [ + "sessionId", + "attachments" + ], + "additionalProperties": false, + "description": "Parameters for session.extensions.sendAttachmentsToMessage.", + "title": "SendAttachmentsToMessageParams" + }, + "result": { + "type": "null" + }, + "stability": "experimental" } }, "tools": { @@ -4744,8 +4778,7 @@ }, "max": { "type": "integer", - "exclusiveMinimum": true, - "minimum": 0, + "exclusiveMinimum": 0, "maximum": 1000, "description": "Maximum number of events to return in this batch (1–1000, default 200)." }, @@ -5002,8 +5035,7 @@ }, "id": { "type": "integer", - "exclusiveMinimum": true, - "minimum": 0, + "exclusiveMinimum": 0, "description": "Id of the scheduled prompt to remove." } }, @@ -6419,165 +6451,506 @@ "title": "ApiKeyAuthInfo", "description": "Schema for the `ApiKeyAuthInfo` type." }, - "AuthInfo": { + "Attachment": { "anyOf": [ { - "$ref": "#/definitions/HMACAuthInfo" - }, - { - "$ref": "#/definitions/EnvAuthInfo" + "$ref": "#/definitions/AttachmentFile", + "description": "File attachment" }, { - "$ref": "#/definitions/TokenAuthInfo" + "$ref": "#/definitions/AttachmentDirectory", + "description": "Directory attachment" }, { - "$ref": "#/definitions/CopilotApiTokenAuthInfo" + "$ref": "#/definitions/AttachmentSelection", + "description": "Code selection attachment from an editor" }, { - "$ref": "#/definitions/UserAuthInfo" + "$ref": "#/definitions/AttachmentGitHubReference", + "description": "GitHub issue, pull request, or discussion reference" }, { - "$ref": "#/definitions/GhCliAuthInfo" + "$ref": "#/definitions/AttachmentBlob", + "description": "Blob attachment with inline base64-encoded data" }, { - "$ref": "#/definitions/ApiKeyAuthInfo" + "$ref": "#/definitions/AttachmentExtensionContext", + "description": "Structured context contributed by an extension. Composer pills displayed in the host are forwarded back through session.send.attachments, then rendered into the model prompt as an XML block." } ], - "description": "The new auth credentials to install on the session. When omitted or `undefined`, the call is a no-op and the session's existing credentials are preserved. The runtime stores the value verbatim and uses it for outbound model/API requests; it does NOT re-validate or re-fetch the associated Copilot user response. Several variants carry secret material; treat this method's params as containing secrets at rest and in transit.", - "title": "AuthInfo" - }, - "AuthInfoType": { - "type": "string", - "enum": [ - "hmac", - "env", - "user", - "gh-cli", - "api-key", - "token", - "copilot-api-token" - ], - "description": "Authentication type", - "title": "AuthInfoType", - "x-enumDescriptions": { - "hmac": "Authentication provided by a GitHub App HMAC credential.", - "env": "Authentication resolved from environment-provided credentials.", - "user": "Authentication from an interactive user sign-in.", - "gh-cli": "Authentication delegated to the GitHub CLI.", - "api-key": "Authentication from an API key credential.", - "token": "Authentication from a GitHub token.", - "copilot-api-token": "Authentication from a Copilot API token." - } + "description": "A user message attachment — a file, directory, code selection, blob, GitHub reference, or extension-supplied context payload", + "title": "Attachment" }, - "CanvasAction": { + "AttachmentBlob": { "type": "object", "properties": { - "name": { + "type": { "type": "string", - "description": "Action name exposed by the canvas provider" + "const": "blob", + "description": "Attachment type discriminator" }, - "description": { + "data": { "type": "string", - "description": "Description of the action" + "description": "Base64-encoded content", + "contentEncoding": "base64" }, - "inputSchema": { - "$ref": "#/definitions/CanvasJsonSchema", - "description": "JSON Schema for the action input" + "mimeType": { + "type": "string", + "description": "MIME type of the inline data" + }, + "displayName": { + "type": "string", + "description": "User-facing display name for the attachment" } }, "required": [ - "name" + "type", + "data", + "mimeType" ], "additionalProperties": false, - "description": "Canvas action that the agent or host can invoke. To discover the input schema for a particular action, call the list_canvas_capabilities tool.", - "title": "CanvasAction" + "description": "Blob attachment with inline base64-encoded data", + "title": "AttachmentBlob" }, - "CanvasActionInvokeRequest": { + "AttachmentDirectory": { "type": "object", "properties": { - "instanceId": { + "type": { "type": "string", - "description": "Open canvas instance identifier" + "const": "directory", + "description": "Attachment type discriminator" }, - "actionName": { + "path": { "type": "string", - "description": "Action name to invoke" + "description": "Absolute directory path" }, - "input": { - "description": "Action input", - "x-opaque-json": true + "displayName": { + "type": "string", + "description": "User-facing display name for the attachment" } }, "required": [ - "instanceId", - "actionName" + "type", + "path", + "displayName" ], "additionalProperties": false, - "description": "Canvas action invocation parameters.", - "title": "CanvasActionInvokeRequest" + "description": "Directory attachment", + "title": "AttachmentDirectory" }, - "CanvasActionInvokeResult": { + "AttachmentExtensionContext": { "type": "object", "properties": { - "result": { - "description": "Provider-supplied action result", + "type": { + "type": "string", + "const": "extension_context", + "description": "Attachment type discriminator" + }, + "extensionId": { + "type": "string", + "description": "Owning extension identifier. Runtime-derived from the caller's connection when produced via session.extensions.sendAttachmentsToMessage; preserved verbatim on subsequent transports." + }, + "canvasId": { + "type": "string", + "description": "Provider-local canvas identifier when the push was bound to a canvas instance" + }, + "instanceId": { + "type": "string", + "description": "Open canvas instance identifier when the push was bound to a canvas instance" + }, + "title": { + "type": "string", + "minLength": 1, + "description": "Human-readable composer pill label" + }, + "payload": { + "description": "Caller-supplied JSON payload", "x-opaque-json": true + }, + "capturedAt": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 timestamp captured by the runtime when the push was accepted" } }, + "required": [ + "type", + "extensionId", + "title", + "capturedAt" + ], "additionalProperties": false, - "description": "Canvas action invocation result.", - "title": "CanvasActionInvokeResult" + "description": "Structured context contributed by an extension. Composer pills displayed in the host are forwarded back through session.send.attachments, then rendered into the model prompt as an XML block.", + "title": "AttachmentExtensionContext" }, - "CanvasCloseRequest": { + "AttachmentFile": { "type": "object", "properties": { - "instanceId": { + "type": { "type": "string", - "description": "Open canvas instance identifier" + "const": "file", + "description": "Attachment type discriminator" + }, + "path": { + "type": "string", + "description": "Absolute file path" + }, + "displayName": { + "type": "string", + "description": "User-facing display name for the attachment" + }, + "lineRange": { + "$ref": "#/definitions/AttachmentFileLineRange", + "description": "Optional line range to scope the attachment to a specific section of the file" } }, "required": [ - "instanceId" + "type", + "path", + "displayName" ], "additionalProperties": false, - "description": "Canvas close parameters.", - "title": "CanvasCloseRequest" + "description": "File attachment", + "title": "AttachmentFile" }, - "CanvasHostContext": { + "AttachmentFileLineRange": { "type": "object", "properties": { - "capabilities": { - "$ref": "#/definitions/CanvasHostContextCapabilities", - "description": "Host capabilities" + "start": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Start line number (1-based)" + }, + "end": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "End line number (1-based, inclusive)" } }, + "required": [ + "start", + "end" + ], "additionalProperties": false, - "description": "Host context supplied by the runtime.", - "title": "CanvasHostContext" + "description": "Optional line range to scope the attachment to a specific section of the file", + "title": "AttachmentFileLineRange" }, - "CanvasHostContextCapabilities": { + "AttachmentGitHubReference": { "type": "object", "properties": { - "canvases": { - "type": "boolean", - "description": "Whether canvas rendering is supported" + "type": { + "type": "string", + "const": "github_reference", + "description": "Attachment type discriminator" + }, + "number": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Issue, pull request, or discussion number" + }, + "title": { + "type": "string", + "description": "Title of the referenced item" + }, + "referenceType": { + "$ref": "#/definitions/AttachmentGitHubReferenceType", + "description": "Type of GitHub reference" + }, + "state": { + "type": "string", + "description": "Current state of the referenced item (e.g., open, closed, merged)" + }, + "url": { + "type": "string", + "description": "URL to the referenced item on GitHub" } }, + "required": [ + "type", + "number", + "title", + "referenceType", + "state", + "url" + ], "additionalProperties": false, - "description": "Host capabilities", - "title": "CanvasHostContextCapabilities" + "description": "GitHub issue, pull request, or discussion reference", + "title": "AttachmentGitHubReference" }, - "CanvasInstanceAvailability": { + "AttachmentGitHubReferenceType": { "type": "string", "enum": [ - "ready", - "stale" + "issue", + "pr", + "discussion" ], - "description": "Runtime-controlled routing state for an open canvas instance.", - "title": "CanvasInstanceAvailability", + "description": "Type of GitHub reference", + "title": "AttachmentGitHubReferenceType", "x-enumDescriptions": { - "ready": "The owning provider is currently connected and routing calls will be dispatched normally.", - "stale": "The owning provider is not currently connected. Routing calls fail with canvas_provider_unavailable until the agent re-issues open_canvas (which rehydrates via a fresh canvas.open) or the provider reconnects." + "issue": "GitHub issue reference.", + "pr": "GitHub pull request reference.", + "discussion": "GitHub discussion reference." + } + }, + "AttachmentSelection": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "selection", + "description": "Attachment type discriminator" + }, + "filePath": { + "type": "string", + "description": "Absolute path to the file containing the selection" + }, + "displayName": { + "type": "string", + "description": "User-facing display name for the selection" + }, + "text": { + "type": "string", + "description": "The selected text content" + }, + "selection": { + "$ref": "#/definitions/AttachmentSelectionDetails", + "description": "Position range of the selection within the file" + } + }, + "required": [ + "type", + "filePath", + "displayName", + "text", + "selection" + ], + "additionalProperties": false, + "description": "Code selection attachment from an editor", + "title": "AttachmentSelection" + }, + "AttachmentSelectionDetails": { + "type": "object", + "properties": { + "start": { + "$ref": "#/definitions/AttachmentSelectionDetailsStart", + "description": "Start position of the selection" + }, + "end": { + "$ref": "#/definitions/AttachmentSelectionDetailsEnd", + "description": "End position of the selection" + } + }, + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "description": "Position range of the selection within the file", + "title": "AttachmentSelectionDetails" + }, + "AttachmentSelectionDetailsEnd": { + "type": "object", + "properties": { + "line": { + "type": "integer", + "minimum": 0, + "description": "End line number (0-based)" + }, + "character": { + "type": "integer", + "minimum": 0, + "description": "End character offset within the line (0-based)" + } + }, + "required": [ + "line", + "character" + ], + "additionalProperties": false, + "description": "End position of the selection", + "title": "AttachmentSelectionDetailsEnd" + }, + "AttachmentSelectionDetailsStart": { + "type": "object", + "properties": { + "line": { + "type": "integer", + "minimum": 0, + "description": "Start line number (0-based)" + }, + "character": { + "type": "integer", + "minimum": 0, + "description": "Start character offset within the line (0-based)" + } + }, + "required": [ + "line", + "character" + ], + "additionalProperties": false, + "description": "Start position of the selection", + "title": "AttachmentSelectionDetailsStart" + }, + "AuthInfo": { + "anyOf": [ + { + "$ref": "#/definitions/HMACAuthInfo" + }, + { + "$ref": "#/definitions/EnvAuthInfo" + }, + { + "$ref": "#/definitions/TokenAuthInfo" + }, + { + "$ref": "#/definitions/CopilotApiTokenAuthInfo" + }, + { + "$ref": "#/definitions/UserAuthInfo" + }, + { + "$ref": "#/definitions/GhCliAuthInfo" + }, + { + "$ref": "#/definitions/ApiKeyAuthInfo" + } + ], + "description": "The new auth credentials to install on the session. When omitted or `undefined`, the call is a no-op and the session's existing credentials are preserved. The runtime stores the value verbatim and uses it for outbound model/API requests; it does NOT re-validate or re-fetch the associated Copilot user response. Several variants carry secret material; treat this method's params as containing secrets at rest and in transit.", + "title": "AuthInfo" + }, + "AuthInfoType": { + "type": "string", + "enum": [ + "hmac", + "env", + "user", + "gh-cli", + "api-key", + "token", + "copilot-api-token" + ], + "description": "Authentication type", + "title": "AuthInfoType", + "x-enumDescriptions": { + "hmac": "Authentication provided by a GitHub App HMAC credential.", + "env": "Authentication resolved from environment-provided credentials.", + "user": "Authentication from an interactive user sign-in.", + "gh-cli": "Authentication delegated to the GitHub CLI.", + "api-key": "Authentication from an API key credential.", + "token": "Authentication from a GitHub token.", + "copilot-api-token": "Authentication from a Copilot API token." + } + }, + "CanvasAction": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Action name exposed by the canvas provider" + }, + "description": { + "type": "string", + "description": "Description of the action" + }, + "inputSchema": { + "$ref": "#/definitions/CanvasJsonSchema", + "description": "JSON Schema for the action input" + } + }, + "required": [ + "name" + ], + "additionalProperties": false, + "description": "Canvas action that the agent or host can invoke. To discover the input schema for a particular action, call the list_canvas_capabilities tool.", + "title": "CanvasAction" + }, + "CanvasActionInvokeRequest": { + "type": "object", + "properties": { + "instanceId": { + "type": "string", + "description": "Open canvas instance identifier" + }, + "actionName": { + "type": "string", + "description": "Action name to invoke" + }, + "input": { + "description": "Action input", + "x-opaque-json": true + } + }, + "required": [ + "instanceId", + "actionName" + ], + "additionalProperties": false, + "description": "Canvas action invocation parameters.", + "title": "CanvasActionInvokeRequest" + }, + "CanvasActionInvokeResult": { + "type": "object", + "properties": { + "result": { + "description": "Provider-supplied action result", + "x-opaque-json": true + } + }, + "additionalProperties": false, + "description": "Canvas action invocation result.", + "title": "CanvasActionInvokeResult" + }, + "CanvasCloseRequest": { + "type": "object", + "properties": { + "instanceId": { + "type": "string", + "description": "Open canvas instance identifier" + } + }, + "required": [ + "instanceId" + ], + "additionalProperties": false, + "description": "Canvas close parameters.", + "title": "CanvasCloseRequest" + }, + "CanvasHostContext": { + "type": "object", + "properties": { + "capabilities": { + "$ref": "#/definitions/CanvasHostContextCapabilities", + "description": "Host capabilities" + } + }, + "additionalProperties": false, + "description": "Host context supplied by the runtime.", + "title": "CanvasHostContext" + }, + "CanvasHostContextCapabilities": { + "type": "object", + "properties": { + "canvases": { + "type": "boolean", + "description": "Whether canvas rendering is supported" + } + }, + "additionalProperties": false, + "description": "Host capabilities", + "title": "CanvasHostContextCapabilities" + }, + "CanvasInstanceAvailability": { + "type": "string", + "enum": [ + "ready", + "stale" + ], + "description": "Runtime-controlled routing state for an open canvas instance.", + "title": "CanvasInstanceAvailability", + "x-enumDescriptions": { + "ready": "The owning provider is currently connected and routing calls will be dispatched normally.", + "stale": "The owning provider is not currently connected. Routing calls fail with canvas_provider_unavailable until the agent re-issues open_canvas (which rehydrates via a fresh canvas.open) or the provider reconnects." } }, "CanvasJsonSchema": { @@ -6959,8 +7332,7 @@ }, "pullRequestNumber": { "type": "integer", - "exclusiveMinimum": true, - "minimum": 0, + "exclusiveMinimum": 0, "description": "Pull request number associated with the session." }, "resourceId": { @@ -7068,8 +7440,7 @@ }, "protocolVersion": { "type": "integer", - "exclusiveMinimum": true, - "minimum": 0, + "exclusiveMinimum": 0, "description": "Server protocol version number" }, "version": { @@ -7102,7 +7473,20 @@ "hidden_characters": "Remove characters that can hide directives." } }, - "CopilotApiTokenAuthInfo": { + "ContextTier": { + "type": "string", + "enum": [ + "default", + "long_context" + ], + "description": "Context tier for models that support multiple context-window sizes.", + "title": "ContextTier", + "x-enumDescriptions": { + "default": "Use the model's default context window.", + "long_context": "Pin the session to the long-context tier when supported." + } + }, + "CopilotApiTokenAuthInfo": { "type": "object", "properties": { "type": { @@ -7536,10 +7920,14 @@ "reasoningEffort": { "type": "string", "description": "Reasoning effort level currently applied to the active model, when one is set. Reads `Session.getReasoningEffort()` synchronously after `getSelectedModel()` resolves so the two values are reported as a snapshot." + }, + "contextTier": { + "$ref": "#/definitions/ContextTier", + "description": "Context tier for models that support multiple context-window sizes." } }, "additionalProperties": false, - "description": "The currently selected model and reasoning effort for the session.", + "description": "The currently selected model, reasoning effort, and context tier for the session. The context tier reflects `Session.getContextTier()`, restored from the session journal on resume.", "title": "CurrentModel" }, "CurrentToolMetadata": { @@ -7807,8 +8195,7 @@ }, "max": { "type": "integer", - "exclusiveMinimum": true, - "minimum": 0, + "exclusiveMinimum": 0, "maximum": 1000, "description": "Maximum number of events to return in this batch (1–1000, default 200)." }, @@ -7992,8 +8379,7 @@ }, "pid": { "type": "integer", - "exclusiveMinimum": true, - "minimum": 0, + "exclusiveMinimum": 0, "maximum": 4294967295, "description": "Process ID if the extension is running" } @@ -8008,6 +8394,33 @@ "title": "Extension", "description": "Schema for the `Extension` type." }, + "ExtensionContextPushInput": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "extension_context", + "description": "Attachment type discriminator" + }, + "title": { + "type": "string", + "minLength": 1, + "description": "Human-readable composer pill label" + }, + "payload": { + "description": "Caller-supplied JSON payload (required, may be null but not undefined)", + "x-opaque-json": true + } + }, + "required": [ + "type", + "title", + "payload" + ], + "additionalProperties": false, + "description": "Slim input shape for extension_context attachments; identity fields are runtime-derived.", + "title": "ExtensionContextPushInput" + }, "ExtensionList": { "type": "object", "properties": { @@ -8879,7 +9292,7 @@ "pattern": "^[^/]+\\/[^/]+$" }, { - "$ref": "#/definitions/InstalledPluginSourceGithub" + "$ref": "#/definitions/InstalledPluginSourceGitHub" }, { "$ref": "#/definitions/InstalledPluginSourceUrl" @@ -8893,7 +9306,7 @@ "x-opaque-json": true, "stability": "experimental" }, - "InstalledPluginSourceGithub": { + "InstalledPluginSourceGitHub": { "type": "object", "properties": { "source": { @@ -8917,8 +9330,8 @@ "repo" ], "additionalProperties": false, - "title": "InstalledPluginSourceGithub", - "description": "Schema for the `InstalledPluginSourceGithub` type." + "title": "InstalledPluginSourceGitHub", + "description": "Schema for the `InstalledPluginSourceGitHub` type." }, "InstalledPluginSourceLocal": { "type": "object", @@ -10089,8 +10502,7 @@ }, "timeout": { "type": "integer", - "exclusiveMinimum": true, - "minimum": 0, + "exclusiveMinimum": 0, "description": "Timeout in milliseconds for tool calls to this server.", "format": "duration" }, @@ -10181,8 +10593,7 @@ }, "timeout": { "type": "integer", - "exclusiveMinimum": true, - "minimum": 0, + "exclusiveMinimum": 0, "description": "Timeout in milliseconds for tool calls to this server.", "format": "duration" }, @@ -10503,8 +10914,7 @@ }, "pullRequestNumber": { "type": "integer", - "exclusiveMinimum": true, - "minimum": 0, + "exclusiveMinimum": 0, "description": "The pull request number the remote session is associated with, if any." }, "taskType": { @@ -10645,13 +11055,12 @@ }, "batchSize": { "type": "integer", - "exclusiveMinimum": true, - "minimum": 0, + "exclusiveMinimum": 0, "description": "Number of tokens per standard billing batch" }, "contextMax": { "type": "integer", - "description": "Maximum context window tokens for the default tier" + "description": "Prompt token budget (max_prompt_tokens) for the default tier. The total context window is this value plus the model's max_output_tokens." }, "longContext": { "$ref": "#/definitions/ModelBillingTokenPricesLongContext", @@ -10682,7 +11091,7 @@ }, "contextMax": { "type": "integer", - "description": "Maximum context window tokens for the long context tier" + "description": "Prompt token budget (max_prompt_tokens) for the long context tier. The total context window is this value plus the model's max_output_tokens." } }, "additionalProperties": false, @@ -11034,23 +11443,8 @@ "description": "Override individual model capabilities resolved by the runtime" }, "contextTier": { - "anyOf": [ - { - "type": "string", - "enum": [ - "default", - "long_context" - ], - "x-enumDescriptions": { - "default": "Use the model's default context window.", - "long_context": "Pin the session to the long-context tier when supported." - } - }, - { - "type": "null" - } - ], - "description": "Explicit context tier for the selected model. `\"default\"` / `\"long_context\"` pin the tier; `null` clears any previous explicit choice; `undefined` leaves the existing tier untouched." + "$ref": "#/definitions/ContextTier", + "description": "Explicit context tier for the selected model. `\"default\"` / `\"long_context\"` apply the requested tier; omit this field to use normal model behavior with no explicit tier." } }, "required": [ @@ -13176,8 +13570,7 @@ }, "protocolVersion": { "type": "integer", - "exclusiveMinimum": true, - "minimum": 0, + "exclusiveMinimum": 0, "description": "Server protocol version number" } }, @@ -13283,714 +13676,734 @@ "description": "Plugins installed for the session, with their enabled state and version metadata.", "title": "PluginList" }, - "QueuedCommandHandled": { + "PushAttachment": { + "anyOf": [ + { + "$ref": "#/definitions/PushAttachmentFile", + "description": "File attachment" + }, + { + "$ref": "#/definitions/PushAttachmentDirectory", + "description": "Directory attachment" + }, + { + "$ref": "#/definitions/PushAttachmentSelection", + "description": "Code selection attachment from an editor" + }, + { + "$ref": "#/definitions/PushAttachmentGitHubReference", + "description": "GitHub issue, pull request, or discussion reference" + }, + { + "$ref": "#/definitions/PushAttachmentBlob", + "description": "Blob attachment with inline base64-encoded data" + }, + { + "$ref": "#/definitions/ExtensionContextPushInput", + "description": "Slim input shape for extension_context attachments; identity fields are runtime-derived." + } + ], + "title": "PushAttachment", + "description": "Schema for the `PushAttachment` type." + }, + "PushAttachmentBlob": { "type": "object", "properties": { - "handled": { - "type": "boolean", - "const": true, - "description": "The host actually executed the queued command." + "type": { + "type": "string", + "const": "blob", + "description": "Attachment type discriminator" }, - "stopProcessingQueue": { - "type": "boolean", - "description": "When true, the runtime will not process subsequent queued commands until a new request comes in." + "data": { + "type": "string", + "description": "Base64-encoded content", + "contentEncoding": "base64" + }, + "mimeType": { + "type": "string", + "description": "MIME type of the inline data" + }, + "displayName": { + "type": "string", + "description": "User-facing display name for the attachment" } }, "required": [ - "handled" + "type", + "data", + "mimeType" ], "additionalProperties": false, - "title": "QueuedCommandHandled", - "description": "Schema for the `QueuedCommandHandled` type." + "description": "Blob attachment with inline base64-encoded data", + "title": "PushAttachmentBlob" }, - "QueuedCommandNotHandled": { + "PushAttachmentDirectory": { "type": "object", "properties": { - "handled": { - "type": "boolean", - "const": false, - "description": "The host did not execute the queued command. Unblocks the queue without claiming the command was processed (e.g. when the handler threw before completing)." + "type": { + "type": "string", + "const": "directory", + "description": "Attachment type discriminator" + }, + "path": { + "type": "string", + "description": "Absolute directory path" + }, + "displayName": { + "type": "string", + "description": "User-facing display name for the attachment" } }, "required": [ - "handled" + "type", + "path", + "displayName" ], "additionalProperties": false, - "title": "QueuedCommandNotHandled", - "description": "Schema for the `QueuedCommandNotHandled` type." - }, - "QueuedCommandResult": { - "anyOf": [ - { - "$ref": "#/definitions/QueuedCommandHandled" - }, - { - "$ref": "#/definitions/QueuedCommandNotHandled" - } - ], - "description": "Result of the queued command execution.", - "title": "QueuedCommandResult" + "description": "Directory attachment", + "title": "PushAttachmentDirectory" }, - "QueuePendingItems": { + "PushAttachmentFile": { "type": "object", "properties": { - "kind": { - "$ref": "#/definitions/QueuePendingItemsKind", - "description": "Whether this item is a queued user message or a queued slash command / model change" + "type": { + "type": "string", + "const": "file", + "description": "Attachment type discriminator" }, - "displayText": { + "path": { "type": "string", - "description": "Human-readable text to display for this queue entry in the UI" + "description": "Absolute file path" + }, + "displayName": { + "type": "string", + "description": "User-facing display name for the attachment" + }, + "lineRange": { + "$ref": "#/definitions/PushAttachmentFileLineRange", + "description": "Optional line range to scope the attachment to a specific section of the file" } }, "required": [ - "kind", - "displayText" + "type", + "path", + "displayName" ], "additionalProperties": false, - "title": "QueuePendingItems", - "description": "Schema for the `QueuePendingItems` type." - }, - "QueuePendingItemsKind": { - "type": "string", - "enum": [ - "message", - "command" - ], - "description": "Whether this item is a queued user message or a queued slash command / model change", - "title": "QueuePendingItemsKind", - "x-enumDescriptions": { - "message": "A queued user message.", - "command": "A queued slash command or model-change command." - } + "description": "File attachment", + "title": "PushAttachmentFile" }, - "QueuePendingItemsResult": { + "PushAttachmentFileLineRange": { "type": "object", "properties": { - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/QueuePendingItems" - }, - "description": "Pending queued items in submission order. Includes user messages, queued slash commands, and queued model changes; omits internal system items." + "start": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Start line number (1-based)" }, - "steeringMessages": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Display text for messages currently in the immediate steering queue (interjections sent during a running turn)." + "end": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "End line number (1-based, inclusive)" } }, "required": [ - "items", - "steeringMessages" + "start", + "end" ], "additionalProperties": false, - "description": "Snapshot of the session's pending queued items and immediate-steering messages.", - "title": "QueuePendingItemsResult" + "description": "Optional line range to scope the attachment to a specific section of the file", + "title": "PushAttachmentFileLineRange" }, - "QueueRemoveMostRecentResult": { + "PushAttachmentGitHubReference": { "type": "object", "properties": { - "removed": { - "type": "boolean", - "description": "True if a user-facing pending item was removed (LIFO across both queues); false when no removable items remained." + "type": { + "type": "string", + "const": "github_reference", + "description": "Attachment type discriminator" + }, + "number": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Issue, pull request, or discussion number" + }, + "title": { + "type": "string", + "description": "Title of the referenced item" + }, + "referenceType": { + "$ref": "#/definitions/PushAttachmentGitHubReferenceType", + "description": "Type of GitHub reference" + }, + "state": { + "type": "string", + "description": "Current state of the referenced item (e.g., open, closed, merged)" + }, + "url": { + "type": "string", + "description": "URL to the referenced item on GitHub" } }, "required": [ - "removed" + "type", + "number", + "title", + "referenceType", + "state", + "url" ], "additionalProperties": false, - "description": "Indicates whether a user-facing pending item was removed.", - "title": "QueueRemoveMostRecentResult" + "description": "GitHub issue, pull request, or discussion reference", + "title": "PushAttachmentGitHubReference" }, - "ReasoningSummary": { + "PushAttachmentGitHubReferenceType": { "type": "string", "enum": [ - "none", - "concise", - "detailed" + "issue", + "pr", + "discussion" ], - "description": "Reasoning summary mode to request for supported model clients", - "title": "ReasoningSummary", + "description": "Type of GitHub reference", + "title": "PushAttachmentGitHubReferenceType", "x-enumDescriptions": { - "none": "Do not request reasoning summaries from the model.", - "concise": "Request a concise summary of the model's reasoning.", - "detailed": "Request a detailed summary of the model's reasoning." + "issue": "GitHub issue reference.", + "pr": "GitHub pull request reference.", + "discussion": "GitHub discussion reference." } }, - "RegisterEventInterestParams": { + "PushAttachmentSelection": { "type": "object", "properties": { - "eventType": { + "type": { "type": "string", - "description": "The event type the consumer wants the runtime to treat as 'observed' for behavior-switching gating. Some runtime code paths inspect whether any consumer is interested in a specific event type and choose a different implementation accordingly (e.g. `mcp.oauth_required`: when interest is registered the runtime delegates the full interactive OAuth flow to the consumer; when no interest is registered the runtime installs a browserless fallback that silently reuses cached tokens). SDK clients that long-poll events do NOT automatically appear as listeners to these gating checks — they must explicitly call `registerInterest` for each event type they want the runtime to count as having a consumer. Multiple registrations for the same event type from the same or different consumers are tracked independently and must each be released. See: `mcp.oauth_required`, `sampling.requested`, `auto_mode_switch.requested`, `user_input.requested`, `elicitation.requested`, `command.queued`, `exit_plan_mode.requested`." + "const": "selection", + "description": "Attachment type discriminator" + }, + "filePath": { + "type": "string", + "description": "Absolute path to the file containing the selection" + }, + "displayName": { + "type": "string", + "description": "User-facing display name for the selection" + }, + "text": { + "type": "string", + "description": "The selected text content" + }, + "selection": { + "$ref": "#/definitions/PushAttachmentSelectionDetails", + "description": "Position range of the selection within the file" } }, "required": [ - "eventType" + "type", + "filePath", + "displayName", + "text", + "selection" ], "additionalProperties": false, - "description": "Event type to register consumer interest for, used by runtime gating logic.", - "title": "RegisterEventInterestParams" + "description": "Code selection attachment from an editor", + "title": "PushAttachmentSelection" }, - "RegisterEventInterestResult": { + "PushAttachmentSelectionDetails": { "type": "object", "properties": { - "handle": { - "type": "string", - "description": "Opaque handle for this registration. Pass to releaseInterest to release. Each call to registerInterest produces a fresh handle, even when the same eventType is registered multiple times." + "start": { + "$ref": "#/definitions/PushAttachmentSelectionDetailsStart", + "description": "Start position of the selection" + }, + "end": { + "$ref": "#/definitions/PushAttachmentSelectionDetailsEnd", + "description": "End position of the selection" } }, "required": [ - "handle" + "start", + "end" ], "additionalProperties": false, - "description": "Opaque handle representing an event-type interest registration.", - "title": "RegisterEventInterestResult" + "description": "Position range of the selection within the file", + "title": "PushAttachmentSelectionDetails" }, - "ReleaseEventInterestParams": { + "PushAttachmentSelectionDetailsEnd": { "type": "object", "properties": { - "handle": { - "type": "string", - "description": "Handle returned by a previous `registerInterest` call. Idempotent: releasing an unknown or already-released handle is a no-op (returns success). When the last outstanding handle for an event type is released, the runtime reverts to its 'no consumer' code path for that event type." + "line": { + "type": "integer", + "minimum": 0, + "description": "End line number (0-based)" + }, + "character": { + "type": "integer", + "minimum": 0, + "description": "End character offset within the line (0-based)" } }, "required": [ - "handle" + "line", + "character" ], "additionalProperties": false, - "description": "Opaque handle previously returned by `registerInterest` to release.", - "title": "ReleaseEventInterestParams" + "description": "End position of the selection", + "title": "PushAttachmentSelectionDetailsEnd" }, - "RemoteEnableRequest": { + "PushAttachmentSelectionDetailsStart": { "type": "object", "properties": { - "mode": { - "$ref": "#/definitions/RemoteSessionMode", - "description": "Per-session remote mode. \"off\" disables remote, \"export\" exports session events to GitHub without enabling remote steering, \"on\" enables both export and remote steering." + "line": { + "type": "integer", + "minimum": 0, + "description": "Start line number (0-based)" + }, + "character": { + "type": "integer", + "minimum": 0, + "description": "Start character offset within the line (0-based)" } }, + "required": [ + "line", + "character" + ], "additionalProperties": false, - "description": "Optional remote session mode (\"off\", \"export\", or \"on\"); defaults to enabling both export and remote steering.", - "title": "RemoteEnableRequest" + "description": "Start position of the selection", + "title": "PushAttachmentSelectionDetailsStart" }, - "RemoteEnableResult": { + "QueuedCommandHandled": { "type": "object", "properties": { - "url": { - "type": "string", - "format": "uri", - "description": "GitHub frontend URL for this session" + "handled": { + "type": "boolean", + "const": true, + "description": "The host actually executed the queued command." }, - "remoteSteerable": { + "stopProcessingQueue": { "type": "boolean", - "description": "Whether remote steering is enabled" + "description": "When true, the runtime will not process subsequent queued commands until a new request comes in." } }, "required": [ - "remoteSteerable" + "handled" ], "additionalProperties": false, - "description": "GitHub URL for the session and a flag indicating whether remote steering is enabled.", - "title": "RemoteEnableResult" + "title": "QueuedCommandHandled", + "description": "Schema for the `QueuedCommandHandled` type." }, - "RemoteNotifySteerableChangedRequest": { + "QueuedCommandNotHandled": { "type": "object", "properties": { - "remoteSteerable": { + "handled": { "type": "boolean", - "description": "Whether the session now supports remote steering via GitHub. The runtime persists this as a `session.remote_steerable_changed` event so resume/replay sees the up-to-date capability." + "const": false, + "description": "The host did not execute the queued command. Unblocks the queue without claiming the command was processed (e.g. when the handler threw before completing)." } }, "required": [ - "remoteSteerable" + "handled" ], "additionalProperties": false, - "description": "New remote-steerability state to persist as a `session.remote_steerable_changed` event.", - "title": "RemoteNotifySteerableChangedRequest" + "title": "QueuedCommandNotHandled", + "description": "Schema for the `QueuedCommandNotHandled` type." }, - "RemoteNotifySteerableChangedResult": { - "type": "object", - "properties": {}, - "additionalProperties": false, - "description": "Persist a steerability change as a `session.remote_steerable_changed` event. Used by the host (CLI / SDK consumer) when it has just finished enabling or disabling steering on a remote exporter that the runtime does not directly own.", - "title": "RemoteNotifySteerableChangedResult" + "QueuedCommandResult": { + "anyOf": [ + { + "$ref": "#/definitions/QueuedCommandHandled" + }, + { + "$ref": "#/definitions/QueuedCommandNotHandled" + } + ], + "description": "Result of the queued command execution.", + "title": "QueuedCommandResult" }, - "RemoteSessionConnectionResult": { + "QueuePendingItems": { "type": "object", "properties": { - "sessionId": { - "type": "string", - "description": "SDK session ID for the connected remote session." + "kind": { + "$ref": "#/definitions/QueuePendingItemsKind", + "description": "Whether this item is a queued user message or a queued slash command / model change" }, - "metadata": { - "$ref": "#/definitions/ConnectedRemoteSessionMetadata", - "description": "Metadata for a connected remote session." + "displayText": { + "type": "string", + "description": "Human-readable text to display for this queue entry in the UI" } }, "required": [ - "sessionId", - "metadata" + "kind", + "displayText" ], "additionalProperties": false, - "description": "Remote session connection result.", - "title": "RemoteSessionConnectionResult" + "title": "QueuePendingItems", + "description": "Schema for the `QueuePendingItems` type." }, - "RemoteSessionMode": { + "QueuePendingItemsKind": { "type": "string", "enum": [ - "off", - "export", - "on" + "message", + "command" ], - "description": "Per-session remote mode. \"off\" disables remote, \"export\" exports session events to GitHub without enabling remote steering, \"on\" enables both export and remote steering.", - "title": "RemoteSessionMode", + "description": "Whether this item is a queued user message or a queued slash command / model change", + "title": "QueuePendingItemsKind", "x-enumDescriptions": { - "off": "Disable remote session export and steering.", - "export": "Export session events to GitHub without enabling remote steering.", - "on": "Enable both remote session export and remote steering." + "message": "A queued user message.", + "command": "A queued slash command or model-change command." } }, - "ScheduleEntry": { + "QueuePendingItemsResult": { "type": "object", "properties": { - "id": { - "type": "integer", - "exclusiveMinimum": true, - "minimum": 0, - "description": "Sequential id assigned by the runtime within the session. Stable across resumes (rebuilt from the event log)." - }, - "intervalMs": { - "type": "integer", - "exclusiveMinimum": true, - "minimum": 0, - "description": "Interval between scheduled ticks, in milliseconds.", - "format": "duration" - }, - "prompt": { - "type": "string", - "description": "Prompt text that gets enqueued on every tick." - }, - "recurring": { - "type": "boolean", - "description": "Whether the schedule re-arms after each tick (`/every`) or fires once (`/after`)." - }, - "displayPrompt": { - "type": "string", - "description": "Display-only label for the prompt as shown in the UI (e.g. `/skill-name` for a skill-invocation schedule). The actual enqueued prompt is `prompt`." + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/QueuePendingItems" + }, + "description": "Pending queued items in submission order. Includes user messages, queued slash commands, and queued model changes; omits internal system items." }, - "nextRunAt": { - "type": "string", - "format": "date-time", - "description": "ISO 8601 timestamp when the next tick is scheduled to fire." - } - }, - "required": [ - "id", - "intervalMs", - "prompt", - "recurring", - "nextRunAt" - ], - "additionalProperties": false, - "title": "ScheduleEntry", - "description": "Schema for the `ScheduleEntry` type." - }, - "ScheduleList": { - "type": "object", - "properties": { - "entries": { + "steeringMessages": { "type": "array", "items": { - "$ref": "#/definitions/ScheduleEntry" + "type": "string" }, - "description": "Active scheduled prompts, ordered by id." + "description": "Display text for messages currently in the immediate steering queue (interjections sent during a running turn)." } }, "required": [ - "entries" + "items", + "steeringMessages" ], "additionalProperties": false, - "description": "Snapshot of the currently active recurring prompts for this session.", - "title": "ScheduleList" + "description": "Snapshot of the session's pending queued items and immediate-steering messages.", + "title": "QueuePendingItemsResult" }, - "ScheduleStopRequest": { + "QueueRemoveMostRecentResult": { "type": "object", "properties": { - "id": { - "type": "integer", - "exclusiveMinimum": true, - "minimum": 0, - "description": "Id of the scheduled prompt to remove." + "removed": { + "type": "boolean", + "description": "True if a user-facing pending item was removed (LIFO across both queues); false when no removable items remained." } }, "required": [ - "id" + "removed" ], "additionalProperties": false, - "description": "Identifier of the scheduled prompt to remove.", - "title": "ScheduleStopRequest" + "description": "Indicates whether a user-facing pending item was removed.", + "title": "QueueRemoveMostRecentResult" }, - "ScheduleStopResult": { - "type": "object", - "properties": { - "entry": { - "$ref": "#/definitions/ScheduleEntry", - "description": "The removed entry, or omitted if no entry matched." - } - }, - "additionalProperties": false, - "description": "Remove a scheduled prompt by id. The result entry is omitted if the id was unknown.", - "title": "ScheduleStopResult" + "ReasoningSummary": { + "type": "string", + "enum": [ + "none", + "concise", + "detailed" + ], + "description": "Reasoning summary mode to request for supported model clients", + "title": "ReasoningSummary", + "x-enumDescriptions": { + "none": "Do not request reasoning summaries from the model.", + "concise": "Request a concise summary of the model's reasoning.", + "detailed": "Request a detailed summary of the model's reasoning." + } }, - "SecretsAddFilterValuesRequest": { + "RegisterEventInterestParams": { "type": "object", "properties": { - "values": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Raw secret values to register for redaction" + "eventType": { + "type": "string", + "description": "The event type the consumer wants the runtime to treat as 'observed' for behavior-switching gating. Some runtime code paths inspect whether any consumer is interested in a specific event type and choose a different implementation accordingly (e.g. `mcp.oauth_required`: when interest is registered the runtime delegates the full interactive OAuth flow to the consumer; when no interest is registered the runtime installs a browserless fallback that silently reuses cached tokens). SDK clients that long-poll events do NOT automatically appear as listeners to these gating checks — they must explicitly call `registerInterest` for each event type they want the runtime to count as having a consumer. Multiple registrations for the same event type from the same or different consumers are tracked independently and must each be released. See: `mcp.oauth_required`, `sampling.requested`, `auto_mode_switch.requested`, `user_input.requested`, `elicitation.requested`, `command.queued`, `exit_plan_mode.requested`." } }, "required": [ - "values" + "eventType" ], "additionalProperties": false, - "description": "Secret values to add to the redaction filter.", - "title": "SecretsAddFilterValuesRequest" + "description": "Event type to register consumer interest for, used by runtime gating logic.", + "title": "RegisterEventInterestParams" }, - "SecretsAddFilterValuesResult": { + "RegisterEventInterestResult": { "type": "object", "properties": { - "ok": { - "type": "boolean", - "const": true, - "description": "Whether the values were successfully registered" + "handle": { + "type": "string", + "description": "Opaque handle for this registration. Pass to releaseInterest to release. Each call to registerInterest produces a fresh handle, even when the same eventType is registered multiple times." } }, "required": [ - "ok" + "handle" ], "additionalProperties": false, - "description": "Confirmation that the secret values were registered.", - "title": "SecretsAddFilterValuesResult" - }, - "SendAgentMode": { - "type": "string", - "enum": [ - "interactive", - "plan", - "autopilot", - "shell" - ], - "description": "The UI mode the agent was in when this message was sent. Defaults to the session's current mode.", - "title": "SendAgentMode", - "x-enumDescriptions": { - "interactive": "The agent is responding interactively to the user.", - "plan": "The agent is preparing a plan before making changes.", - "autopilot": "The agent is working autonomously toward task completion.", - "shell": "The agent is in shell-focused UI mode." - } - }, - "SendAttachment": { - "anyOf": [ - { - "$ref": "#/definitions/SendAttachmentFile", - "description": "File attachment" - }, - { - "$ref": "#/definitions/SendAttachmentDirectory", - "description": "Directory attachment" - }, - { - "$ref": "#/definitions/SendAttachmentSelection", - "description": "Code selection attachment from an editor" - }, - { - "$ref": "#/definitions/SendAttachmentGithubReference", - "description": "GitHub issue, pull request, or discussion reference" - }, - { - "$ref": "#/definitions/SendAttachmentBlob", - "description": "Blob attachment with inline base64-encoded data" - } - ], - "description": "A user message attachment — a file, directory, code selection, blob, or GitHub reference", - "title": "SendAttachment" + "description": "Opaque handle representing an event-type interest registration.", + "title": "RegisterEventInterestResult" }, - "SendAttachmentBlob": { + "ReleaseEventInterestParams": { "type": "object", "properties": { - "type": { - "type": "string", - "const": "blob", - "description": "Attachment type discriminator" - }, - "data": { - "type": "string", - "description": "Base64-encoded content", - "contentEncoding": "base64" - }, - "mimeType": { - "type": "string", - "description": "MIME type of the inline data" - }, - "displayName": { + "handle": { "type": "string", - "description": "User-facing display name for the attachment" + "description": "Handle returned by a previous `registerInterest` call. Idempotent: releasing an unknown or already-released handle is a no-op (returns success). When the last outstanding handle for an event type is released, the runtime reverts to its 'no consumer' code path for that event type." } }, "required": [ - "type", - "data", - "mimeType" + "handle" ], "additionalProperties": false, - "description": "Blob attachment with inline base64-encoded data", - "title": "SendAttachmentBlob" + "description": "Opaque handle previously returned by `registerInterest` to release.", + "title": "ReleaseEventInterestParams" }, - "SendAttachmentDirectory": { + "RemoteEnableRequest": { "type": "object", "properties": { - "type": { - "type": "string", - "const": "directory", - "description": "Attachment type discriminator" - }, - "path": { - "type": "string", - "description": "Absolute directory path" - }, - "displayName": { - "type": "string", - "description": "User-facing display name for the attachment" + "mode": { + "$ref": "#/definitions/RemoteSessionMode", + "description": "Per-session remote mode. \"off\" disables remote, \"export\" exports session events to GitHub without enabling remote steering, \"on\" enables both export and remote steering." } }, - "required": [ - "type", - "path", - "displayName" - ], "additionalProperties": false, - "description": "Directory attachment", - "title": "SendAttachmentDirectory" + "description": "Optional remote session mode (\"off\", \"export\", or \"on\"); defaults to enabling both export and remote steering.", + "title": "RemoteEnableRequest" }, - "SendAttachmentFile": { + "RemoteEnableResult": { "type": "object", "properties": { - "type": { - "type": "string", - "const": "file", - "description": "Attachment type discriminator" - }, - "path": { - "type": "string", - "description": "Absolute file path" - }, - "displayName": { + "url": { "type": "string", - "description": "User-facing display name for the attachment" + "format": "uri", + "description": "GitHub frontend URL for this session" }, - "lineRange": { - "$ref": "#/definitions/SendAttachmentFileLineRange", - "description": "Optional line range to scope the attachment to a specific section of the file" + "remoteSteerable": { + "type": "boolean", + "description": "Whether remote steering is enabled" } }, "required": [ - "type", - "path", - "displayName" + "remoteSteerable" ], "additionalProperties": false, - "description": "File attachment", - "title": "SendAttachmentFile" + "description": "GitHub URL for the session and a flag indicating whether remote steering is enabled.", + "title": "RemoteEnableResult" }, - "SendAttachmentFileLineRange": { + "RemoteNotifySteerableChangedRequest": { "type": "object", "properties": { - "start": { - "type": "integer", - "exclusiveMinimum": true, - "minimum": 0, - "description": "Start line number (1-based)" - }, - "end": { - "type": "integer", - "exclusiveMinimum": true, - "minimum": 0, - "description": "End line number (1-based, inclusive)" + "remoteSteerable": { + "type": "boolean", + "description": "Whether the session now supports remote steering via GitHub. The runtime persists this as a `session.remote_steerable_changed` event so resume/replay sees the up-to-date capability." } }, "required": [ - "start", - "end" + "remoteSteerable" ], "additionalProperties": false, - "description": "Optional line range to scope the attachment to a specific section of the file", - "title": "SendAttachmentFileLineRange" + "description": "New remote-steerability state to persist as a `session.remote_steerable_changed` event.", + "title": "RemoteNotifySteerableChangedRequest" + }, + "RemoteNotifySteerableChangedResult": { + "type": "object", + "properties": {}, + "additionalProperties": false, + "description": "Persist a steerability change as a `session.remote_steerable_changed` event. Used by the host (CLI / SDK consumer) when it has just finished enabling or disabling steering on a remote exporter that the runtime does not directly own.", + "title": "RemoteNotifySteerableChangedResult" }, - "SendAttachmentGithubReference": { + "RemoteSessionConnectionResult": { "type": "object", "properties": { - "type": { - "type": "string", - "const": "github_reference", - "description": "Attachment type discriminator" - }, - "number": { - "type": "integer", - "exclusiveMinimum": true, - "minimum": 0, - "description": "Issue, pull request, or discussion number" - }, - "title": { - "type": "string", - "description": "Title of the referenced item" - }, - "referenceType": { - "$ref": "#/definitions/SendAttachmentGithubReferenceType", - "description": "Type of GitHub reference" - }, - "state": { + "sessionId": { "type": "string", - "description": "Current state of the referenced item (e.g., open, closed, merged)" + "description": "SDK session ID for the connected remote session." }, - "url": { - "type": "string", - "description": "URL to the referenced item on GitHub" + "metadata": { + "$ref": "#/definitions/ConnectedRemoteSessionMetadata", + "description": "Metadata for a connected remote session." } }, "required": [ - "type", - "number", - "title", - "referenceType", - "state", - "url" + "sessionId", + "metadata" ], "additionalProperties": false, - "description": "GitHub issue, pull request, or discussion reference", - "title": "SendAttachmentGithubReference" + "description": "Remote session connection result.", + "title": "RemoteSessionConnectionResult" }, - "SendAttachmentGithubReferenceType": { + "RemoteSessionMode": { "type": "string", "enum": [ - "issue", - "pr", - "discussion" + "off", + "export", + "on" ], - "description": "Type of GitHub reference", - "title": "SendAttachmentGithubReferenceType", + "description": "Per-session remote mode. \"off\" disables remote, \"export\" exports session events to GitHub without enabling remote steering, \"on\" enables both export and remote steering.", + "title": "RemoteSessionMode", "x-enumDescriptions": { - "issue": "GitHub issue reference.", - "pr": "GitHub pull request reference.", - "discussion": "GitHub discussion reference." + "off": "Disable remote session export and steering.", + "export": "Export session events to GitHub without enabling remote steering.", + "on": "Enable both remote session export and remote steering." } }, - "SendAttachmentSelection": { + "ScheduleEntry": { "type": "object", "properties": { - "type": { - "type": "string", - "const": "selection", - "description": "Attachment type discriminator" + "id": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Sequential id assigned by the runtime within the session. Stable across resumes (rebuilt from the event log)." }, - "filePath": { - "type": "string", - "description": "Absolute path to the file containing the selection" + "intervalMs": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Interval between scheduled ticks, in milliseconds.", + "format": "duration" }, - "displayName": { + "prompt": { "type": "string", - "description": "User-facing display name for the selection" + "description": "Prompt text that gets enqueued on every tick." }, - "text": { + "recurring": { + "type": "boolean", + "description": "Whether the schedule re-arms after each tick (`/every`) or fires once (`/after`)." + }, + "displayPrompt": { "type": "string", - "description": "The selected text content" + "description": "Display-only label for the prompt as shown in the UI (e.g. `/skill-name` for a skill-invocation schedule). The actual enqueued prompt is `prompt`." }, - "selection": { - "$ref": "#/definitions/SendAttachmentSelectionDetails", - "description": "Position range of the selection within the file" + "nextRunAt": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 timestamp when the next tick is scheduled to fire." } }, "required": [ - "type", - "filePath", - "displayName", - "text", - "selection" + "id", + "intervalMs", + "prompt", + "recurring", + "nextRunAt" ], "additionalProperties": false, - "description": "Code selection attachment from an editor", - "title": "SendAttachmentSelection" + "title": "ScheduleEntry", + "description": "Schema for the `ScheduleEntry` type." }, - "SendAttachmentSelectionDetails": { + "ScheduleList": { "type": "object", "properties": { - "start": { - "$ref": "#/definitions/SendAttachmentSelectionDetailsStart", - "description": "Start position of the selection" - }, - "end": { - "$ref": "#/definitions/SendAttachmentSelectionDetailsEnd", - "description": "End position of the selection" + "entries": { + "type": "array", + "items": { + "$ref": "#/definitions/ScheduleEntry" + }, + "description": "Active scheduled prompts, ordered by id." } }, "required": [ - "start", - "end" + "entries" ], "additionalProperties": false, - "description": "Position range of the selection within the file", - "title": "SendAttachmentSelectionDetails" + "description": "Snapshot of the currently active recurring prompts for this session.", + "title": "ScheduleList" }, - "SendAttachmentSelectionDetailsEnd": { + "ScheduleStopRequest": { "type": "object", "properties": { - "line": { - "type": "integer", - "minimum": 0, - "description": "End line number (0-based)" - }, - "character": { + "id": { "type": "integer", - "minimum": 0, - "description": "End character offset within the line (0-based)" + "exclusiveMinimum": 0, + "description": "Id of the scheduled prompt to remove." } }, "required": [ - "line", - "character" + "id" ], "additionalProperties": false, - "description": "End position of the selection", - "title": "SendAttachmentSelectionDetailsEnd" + "description": "Identifier of the scheduled prompt to remove.", + "title": "ScheduleStopRequest" }, - "SendAttachmentSelectionDetailsStart": { + "ScheduleStopResult": { "type": "object", "properties": { - "line": { - "type": "integer", - "minimum": 0, - "description": "Start line number (0-based)" + "entry": { + "$ref": "#/definitions/ScheduleEntry", + "description": "The removed entry, or omitted if no entry matched." + } + }, + "additionalProperties": false, + "description": "Remove a scheduled prompt by id. The result entry is omitted if the id was unknown.", + "title": "ScheduleStopResult" + }, + "SecretsAddFilterValuesRequest": { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Raw secret values to register for redaction" + } + }, + "required": [ + "values" + ], + "additionalProperties": false, + "description": "Secret values to add to the redaction filter.", + "title": "SecretsAddFilterValuesRequest" + }, + "SecretsAddFilterValuesResult": { + "type": "object", + "properties": { + "ok": { + "type": "boolean", + "const": true, + "description": "Whether the values were successfully registered" + } + }, + "required": [ + "ok" + ], + "additionalProperties": false, + "description": "Confirmation that the secret values were registered.", + "title": "SecretsAddFilterValuesResult" + }, + "SendAgentMode": { + "type": "string", + "enum": [ + "interactive", + "plan", + "autopilot", + "shell" + ], + "description": "The UI mode the agent was in when this message was sent. Defaults to the session's current mode.", + "title": "SendAgentMode", + "x-enumDescriptions": { + "interactive": "The agent is responding interactively to the user.", + "plan": "The agent is preparing a plan before making changes.", + "autopilot": "The agent is working autonomously toward task completion.", + "shell": "The agent is in shell-focused UI mode." + } + }, + "SendAttachmentsToMessageParams": { + "type": "object", + "properties": { + "instanceId": { + "type": "string", + "description": "Optional canvas instance binding the push for provenance. When supplied, the runtime resolves the canvas, verifies it is owned by the calling extension, and stamps canvasId/instanceId onto each extension_context entry. When omitted, no resolution runs and those fields stay unset on the attachment." }, - "character": { - "type": "integer", - "minimum": 0, - "description": "Start character offset within the line (0-based)" + "attachments": { + "type": "array", + "items": { + "$ref": "#/definitions/PushAttachment" + }, + "description": "Attachments to push into the next user-message turn. extension_context entries take the slim shape; standard variants take their full AttachmentSchema shape." } }, "required": [ - "line", - "character" + "attachments" ], "additionalProperties": false, - "description": "Start position of the selection", - "title": "SendAttachmentSelectionDetailsStart" + "description": "Parameters for session.extensions.sendAttachmentsToMessage.", + "title": "SendAttachmentsToMessageParams" }, "SendMode": { "type": "string", @@ -14019,8 +14432,8 @@ "attachments": { "type": "array", "items": { - "$ref": "#/definitions/SendAttachment", - "description": "A user message attachment — a file, directory, code selection, blob, or GitHub reference" + "$ref": "#/definitions/Attachment", + "description": "A user message attachment — a file, directory, code selection, blob, GitHub reference, or extension-supplied context payload" }, "description": "Optional attachments (files, directories, selections, blobs, GitHub references) to include with the message" }, @@ -14298,7 +14711,7 @@ "limit": { "type": "integer", "minimum": 0, - "description": "Total context limit for /context display. promptTokenLimit + min(32k or 64k, outputTokenLimit) depending on model." + "description": "Total context limit for /context display: promptTokenLimit + outputTokenLimit (the model's full max_output_tokens reserved on top of the prompt budget)." }, "bufferTokens": { "type": "integer", @@ -14946,7 +15359,7 @@ "pattern": "^[^/]+\\/[^/]+$" }, { - "$ref": "#/definitions/SessionInstalledPluginSourceGithub" + "$ref": "#/definitions/SessionInstalledPluginSourceGitHub" }, { "$ref": "#/definitions/SessionInstalledPluginSourceUrl" @@ -14960,7 +15373,7 @@ "x-opaque-json": true, "stability": "experimental" }, - "SessionInstalledPluginSourceGithub": { + "SessionInstalledPluginSourceGitHub": { "type": "object", "properties": { "source": { @@ -14984,8 +15397,8 @@ "repo" ], "additionalProperties": false, - "title": "SessionInstalledPluginSourceGithub", - "description": "Schema for the `SessionInstalledPluginSourceGithub` type." + "title": "SessionInstalledPluginSourceGitHub", + "description": "Schema for the `SessionInstalledPluginSourceGitHub` type." }, "SessionInstalledPluginSourceLocal": { "type": "object", @@ -15939,6 +16352,10 @@ "type": "boolean", "description": "Whether to default custom agents to local-only execution." }, + "suppressCustomAgentPrompt": { + "type": "boolean", + "description": "When true, the selected custom agent's prompt is not injected into the user message (skill context is still injected). Used by automation triggers where the agent prompt is already in the problem statement." + }, "skipCustomInstructions": { "type": "boolean", "description": "Whether to skip loading custom instruction sources." @@ -17057,8 +17474,7 @@ }, "pid": { "type": "integer", - "exclusiveMinimum": true, - "minimum": 0, + "exclusiveMinimum": 0, "maximum": 4294967295, "description": "Process ID when available" } @@ -17103,8 +17519,7 @@ }, "pid": { "type": "integer", - "exclusiveMinimum": true, - "minimum": 0, + "exclusiveMinimum": 0, "maximum": 4294967295, "description": "Process ID when available" } diff --git a/schemas/session-events.schema.json b/schemas/session-events.schema.json index 7e74166..5202b7a 100644 --- a/schemas/session-events.schema.json +++ b/schemas/session-events.schema.json @@ -1177,296 +1177,368 @@ "visibility": "internal", "description": "Schema for the `AssistantUsageQuotaSnapshot` type." }, - "AutoModeSwitchCompletedData": { - "type": "object", - "properties": { - "requestId": { - "type": "string", - "description": "Request ID of the resolved request; clients should dismiss any UI for this request" + "Attachment": { + "anyOf": [ + { + "$ref": "#/definitions/AttachmentFile", + "description": "File attachment" }, - "response": { - "$ref": "#/definitions/AutoModeSwitchResponse", - "description": "The user's auto-mode-switch choice" + { + "$ref": "#/definitions/AttachmentDirectory", + "description": "Directory attachment" + }, + { + "$ref": "#/definitions/AttachmentSelection", + "description": "Code selection attachment from an editor" + }, + { + "$ref": "#/definitions/AttachmentGitHubReference", + "description": "GitHub issue, pull request, or discussion reference" + }, + { + "$ref": "#/definitions/AttachmentBlob", + "description": "Blob attachment with inline base64-encoded data" + }, + { + "$ref": "#/definitions/AttachmentExtensionContext", + "description": "Structured context contributed by an extension. Composer pills displayed in the host are forwarded back through session.send.attachments, then rendered into the model prompt as an XML block." } - }, - "required": [ - "requestId", - "response" ], - "additionalProperties": false, - "description": "Auto mode switch completion notification", - "title": "AutoModeSwitchCompletedData" + "description": "A user message attachment — a file, directory, code selection, blob, GitHub reference, or extension-supplied context payload", + "title": "Attachment" }, - "AutoModeSwitchCompletedEvent": { + "AttachmentBlob": { "type": "object", "properties": { - "id": { + "type": { "type": "string", - "format": "uuid", - "description": "Unique event identifier (UUID v4), generated when the event is emitted" + "const": "blob", + "description": "Attachment type discriminator" }, - "timestamp": { + "data": { "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", - "const": true, - "description": "Always true for events that are transient and not persisted to the session event log on disk." + "description": "Base64-encoded content", + "contentEncoding": "base64" }, - "agentId": { + "mimeType": { "type": "string", - "description": "Sub-agent instance identifier. Absent for events from the root/main agent and session-level events." + "description": "MIME type of the inline data" }, - "type": { + "displayName": { "type": "string", - "const": "auto_mode_switch.completed", - "description": "Type discriminator. Always \"auto_mode_switch.completed\"." - }, - "data": { - "$ref": "#/definitions/AutoModeSwitchCompletedData", - "description": "Auto mode switch completion notification" + "description": "User-facing display name for the attachment" } }, "required": [ - "id", - "timestamp", - "parentId", - "ephemeral", "type", - "data" + "data", + "mimeType" ], "additionalProperties": false, - "description": "Session event \"auto_mode_switch.completed\". Auto mode switch completion notification", - "title": "AutoModeSwitchCompletedEvent" + "description": "Blob attachment with inline base64-encoded data", + "title": "AttachmentBlob" }, - "AutoModeSwitchRequestedData": { + "AttachmentDirectory": { "type": "object", "properties": { - "requestId": { + "type": { "type": "string", - "description": "Unique identifier for this request; used to respond via session.respondToAutoModeSwitch()" + "const": "directory", + "description": "Attachment type discriminator" }, - "errorCode": { + "path": { "type": "string", - "description": "The rate limit error code that triggered this request" + "description": "Absolute directory path" }, - "retryAfterSeconds": { - "type": "integer", - "minimum": 0, - "description": "Seconds until the rate limit resets, when known. Lets clients render a humanized reset time alongside the prompt.", - "format": "duration" + "displayName": { + "type": "string", + "description": "User-facing display name for the attachment" } }, "required": [ - "requestId" + "type", + "path", + "displayName" ], "additionalProperties": false, - "description": "Auto mode switch request notification requiring user approval", - "title": "AutoModeSwitchRequestedData" + "description": "Directory attachment", + "title": "AttachmentDirectory" }, - "AutoModeSwitchRequestedEvent": { + "AttachmentExtensionContext": { "type": "object", "properties": { - "id": { + "type": { "type": "string", - "format": "uuid", - "description": "Unique event identifier (UUID v4), generated when the event is emitted" + "const": "extension_context", + "description": "Attachment type discriminator" }, - "timestamp": { + "extensionId": { "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." + "description": "Owning extension identifier. Runtime-derived from the caller's connection when produced via session.extensions.sendAttachmentsToMessage; preserved verbatim on subsequent transports." }, - "ephemeral": { - "type": "boolean", - "const": true, - "description": "Always true for events that are transient and not persisted to the session event log on disk." + "canvasId": { + "type": "string", + "description": "Provider-local canvas identifier when the push was bound to a canvas instance" }, - "agentId": { + "instanceId": { "type": "string", - "description": "Sub-agent instance identifier. Absent for events from the root/main agent and session-level events." + "description": "Open canvas instance identifier when the push was bound to a canvas instance" }, - "type": { + "title": { "type": "string", - "const": "auto_mode_switch.requested", - "description": "Type discriminator. Always \"auto_mode_switch.requested\"." + "minLength": 1, + "description": "Human-readable composer pill label" }, - "data": { - "$ref": "#/definitions/AutoModeSwitchRequestedData", - "description": "Auto mode switch request notification requiring user approval" + "payload": { + "description": "Caller-supplied JSON payload", + "x-opaque-json": true + }, + "capturedAt": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 timestamp captured by the runtime when the push was accepted" } }, "required": [ - "id", - "timestamp", - "parentId", - "ephemeral", "type", - "data" + "extensionId", + "title", + "capturedAt" ], "additionalProperties": false, - "description": "Session event \"auto_mode_switch.requested\". Auto mode switch request notification requiring user approval", - "title": "AutoModeSwitchRequestedEvent" + "description": "Structured context contributed by an extension. Composer pills displayed in the host are forwarded back through session.send.attachments, then rendered into the model prompt as an XML block.", + "title": "AttachmentExtensionContext" }, - "AutoModeSwitchResponse": { - "type": "string", - "enum": [ - "yes", - "yes_always", - "no" + "AttachmentFile": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "file", + "description": "Attachment type discriminator" + }, + "path": { + "type": "string", + "description": "Absolute file path" + }, + "displayName": { + "type": "string", + "description": "User-facing display name for the attachment" + }, + "lineRange": { + "$ref": "#/definitions/AttachmentFileLineRange", + "description": "Optional line range to scope the attachment to a specific section of the file" + } + }, + "required": [ + "type", + "path", + "displayName" ], - "description": "The user's auto-mode-switch choice", - "title": "AutoModeSwitchResponse", - "x-enumDescriptions": { - "yes": "Switch models for this request.", - "yes_always": "Switch models now and keep using the replacement automatically.", - "no": "Do not switch models." - } + "additionalProperties": false, + "description": "File attachment", + "title": "AttachmentFile" }, - "AutopilotObjectiveChangedData": { + "AttachmentFileLineRange": { "type": "object", "properties": { - "operation": { - "$ref": "#/definitions/AutopilotObjectiveChangedOperation", - "description": "The type of operation performed on the autopilot objective state file" - }, - "id": { + "start": { "type": "integer", "exclusiveMinimum": 0, - "description": "Current autopilot objective id, if one exists" + "description": "Start line number (1-based)" }, - "status": { - "$ref": "#/definitions/AutopilotObjectiveChangedStatus", - "description": "Current autopilot objective status, if one exists" + "end": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "End line number (1-based, inclusive)" } }, "required": [ - "operation" + "start", + "end" ], "additionalProperties": false, - "description": "Autopilot objective state file operation details indicating what changed", - "title": "AutopilotObjectiveChangedData" + "description": "Optional line range to scope the attachment to a specific section of the file", + "title": "AttachmentFileLineRange" }, - "AutopilotObjectiveChangedEvent": { + "AttachmentGitHubReference": { "type": "object", "properties": { - "id": { + "type": { "type": "string", - "format": "uuid", - "description": "Unique event identifier (UUID v4), generated when the event is emitted" + "const": "github_reference", + "description": "Attachment type discriminator" }, - "timestamp": { + "number": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Issue, pull request, or discussion number" + }, + "title": { "type": "string", - "format": "date-time", - "description": "ISO 8601 timestamp when the event was created" + "description": "Title of the referenced item" }, - "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." + "referenceType": { + "$ref": "#/definitions/AttachmentGitHubReferenceType", + "description": "Type of GitHub reference" }, - "ephemeral": { - "type": "boolean", - "description": "When true, the event is transient and not persisted to the session event log on disk" - }, - "agentId": { + "state": { "type": "string", - "description": "Sub-agent instance identifier. Absent for events from the root/main agent and session-level events." + "description": "Current state of the referenced item (e.g., open, closed, merged)" }, - "type": { + "url": { "type": "string", - "const": "session.autopilot_objective_changed", - "description": "Type discriminator. Always \"session.autopilot_objective_changed\"." - }, - "data": { - "$ref": "#/definitions/AutopilotObjectiveChangedData", - "description": "Autopilot objective state file operation details indicating what changed" + "description": "URL to the referenced item on GitHub" } }, "required": [ - "id", - "timestamp", - "parentId", "type", - "data" + "number", + "title", + "referenceType", + "state", + "url" ], "additionalProperties": false, - "description": "Session event \"session.autopilot_objective_changed\". Autopilot objective state file operation details indicating what changed", - "title": "AutopilotObjectiveChangedEvent" + "description": "GitHub issue, pull request, or discussion reference", + "title": "AttachmentGitHubReference" }, - "AutopilotObjectiveChangedOperation": { + "AttachmentGitHubReferenceType": { "type": "string", "enum": [ - "create", - "update", - "delete" + "issue", + "pr", + "discussion" ], - "description": "The type of operation performed on the autopilot objective state file", - "title": "AutopilotObjectiveChangedOperation", + "description": "Type of GitHub reference", + "title": "AttachmentGitHubReferenceType", "x-enumDescriptions": { - "create": "Autopilot objective state file was created for a new objective.", - "update": "Autopilot objective state file was updated for an existing objective.", - "delete": "Autopilot objective state file was deleted or cleared." + "issue": "GitHub issue reference.", + "pr": "GitHub pull request reference.", + "discussion": "GitHub discussion reference." } }, - "AutopilotObjectiveChangedStatus": { - "type": "string", - "enum": [ - "active", - "paused", - "cap_reached", - "completed" + "AttachmentSelection": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "selection", + "description": "Attachment type discriminator" + }, + "filePath": { + "type": "string", + "description": "Absolute path to the file containing the selection" + }, + "displayName": { + "type": "string", + "description": "User-facing display name for the selection" + }, + "text": { + "type": "string", + "description": "The selected text content" + }, + "selection": { + "$ref": "#/definitions/AttachmentSelectionDetails", + "description": "Position range of the selection within the file" + } + }, + "required": [ + "type", + "filePath", + "displayName", + "text", + "selection" ], - "description": "Current autopilot objective status, if one exists", - "title": "AutopilotObjectiveChangedStatus", - "x-enumDescriptions": { - "active": "Objective is active and can drive autopilot continuations.", - "paused": "Objective is paused and will not drive autopilot continuations.", - "cap_reached": "Legacy objective state indicating the previous continuation cap was reached.", - "completed": "Objective was completed by the agent." - } + "additionalProperties": false, + "description": "Code selection attachment from an editor", + "title": "AttachmentSelection" }, - "BackgroundTasksChangedData": { + "AttachmentSelectionDetails": { "type": "object", - "properties": {}, + "properties": { + "start": { + "$ref": "#/definitions/AttachmentSelectionDetailsStart", + "description": "Start position of the selection" + }, + "end": { + "$ref": "#/definitions/AttachmentSelectionDetailsEnd", + "description": "End position of the selection" + } + }, + "required": [ + "start", + "end" + ], "additionalProperties": false, - "title": "BackgroundTasksChangedData", - "description": "Schema for the `BackgroundTasksChangedData` type." + "description": "Position range of the selection within the file", + "title": "AttachmentSelectionDetails" }, - "BackgroundTasksChangedEvent": { + "AttachmentSelectionDetailsEnd": { + "type": "object", + "properties": { + "line": { + "type": "integer", + "minimum": 0, + "description": "End line number (0-based)" + }, + "character": { + "type": "integer", + "minimum": 0, + "description": "End character offset within the line (0-based)" + } + }, + "required": [ + "line", + "character" + ], + "additionalProperties": false, + "description": "End position of the selection", + "title": "AttachmentSelectionDetailsEnd" + }, + "AttachmentSelectionDetailsStart": { + "type": "object", + "properties": { + "line": { + "type": "integer", + "minimum": 0, + "description": "Start line number (0-based)" + }, + "character": { + "type": "integer", + "minimum": 0, + "description": "Start character offset within the line (0-based)" + } + }, + "required": [ + "line", + "character" + ], + "additionalProperties": false, + "description": "Start position of the selection", + "title": "AttachmentSelectionDetailsStart" + }, + "AutoModeSwitchCompletedData": { + "type": "object", + "properties": { + "requestId": { + "type": "string", + "description": "Request ID of the resolved request; clients should dismiss any UI for this request" + }, + "response": { + "$ref": "#/definitions/AutoModeSwitchResponse", + "description": "The user's auto-mode-switch choice" + } + }, + "required": [ + "requestId", + "response" + ], + "additionalProperties": false, + "description": "Auto mode switch completion notification", + "title": "AutoModeSwitchCompletedData" + }, + "AutoModeSwitchCompletedEvent": { "type": "object", "properties": { "id": { @@ -1502,12 +1574,12 @@ }, "type": { "type": "string", - "const": "session.background_tasks_changed", - "description": "Type discriminator. Always \"session.background_tasks_changed\"." + "const": "auto_mode_switch.completed", + "description": "Type discriminator. Always \"auto_mode_switch.completed\"." }, "data": { - "$ref": "#/definitions/BackgroundTasksChangedData", - "description": "Schema for the `BackgroundTasksChangedData` type." + "$ref": "#/definitions/AutoModeSwitchCompletedData", + "description": "Auto mode switch completion notification" } }, "required": [ @@ -1519,78 +1591,35 @@ "data" ], "additionalProperties": false, - "description": "Session event \"session.background_tasks_changed\".", - "title": "BackgroundTasksChangedEvent" - }, - "CanvasOpenedAvailability": { - "type": "string", - "enum": [ - "ready", - "stale" - ], - "description": "Runtime-controlled routing state for the instance. \"ready\" when the provider connection is live; \"stale\" when the provider has gone away and the instance is awaiting rebinding.", - "title": "CanvasOpenedAvailability", - "x-enumDescriptions": { - "ready": "Provider connection is live; actions can be invoked.", - "stale": "Provider has gone away; the instance is awaiting rebinding." - } + "description": "Session event \"auto_mode_switch.completed\". Auto mode switch completion notification", + "title": "AutoModeSwitchCompletedEvent" }, - "CanvasOpenedData": { + "AutoModeSwitchRequestedData": { "type": "object", "properties": { - "instanceId": { - "type": "string", - "description": "Stable caller-supplied canvas instance identifier" - }, - "extensionId": { - "type": "string", - "description": "Owning provider identifier" - }, - "extensionName": { - "type": "string", - "description": "Owning extension display name, when available" - }, - "canvasId": { - "type": "string", - "description": "Provider-local canvas identifier" - }, - "title": { - "type": "string", - "description": "Rendered title" - }, - "status": { + "requestId": { "type": "string", - "description": "Provider-supplied status text" + "description": "Unique identifier for this request; used to respond via session.respondToAutoModeSwitch()" }, - "url": { + "errorCode": { "type": "string", - "description": "URL for web-rendered canvases" - }, - "input": { - "description": "Input supplied when the instance was opened", - "x-opaque-json": true - }, - "reopen": { - "type": "boolean", - "description": "Whether this notification represents an idempotent reopen" + "description": "The rate limit error code that triggered this request" }, - "availability": { - "$ref": "#/definitions/CanvasOpenedAvailability", - "description": "Runtime-controlled routing state for the instance. \"ready\" when the provider connection is live; \"stale\" when the provider has gone away and the instance is awaiting rebinding." + "retryAfterSeconds": { + "type": "integer", + "minimum": 0, + "description": "Seconds until the rate limit resets, when known. Lets clients render a humanized reset time alongside the prompt.", + "format": "duration" } }, "required": [ - "instanceId", - "extensionId", - "canvasId", - "reopen", - "availability" + "requestId" ], "additionalProperties": false, - "title": "CanvasOpenedData", - "description": "Schema for the `CanvasOpenedData` type." + "description": "Auto mode switch request notification requiring user approval", + "title": "AutoModeSwitchRequestedData" }, - "CanvasOpenedEvent": { + "AutoModeSwitchRequestedEvent": { "type": "object", "properties": { "id": { @@ -1626,12 +1655,12 @@ }, "type": { "type": "string", - "const": "session.canvas.opened", - "description": "Type discriminator. Always \"session.canvas.opened\"." + "const": "auto_mode_switch.requested", + "description": "Type discriminator. Always \"auto_mode_switch.requested\"." }, "data": { - "$ref": "#/definitions/CanvasOpenedData", - "description": "Schema for the `CanvasOpenedData` type." + "$ref": "#/definitions/AutoModeSwitchRequestedData", + "description": "Auto mode switch request notification requiring user approval" } }, "required": [ @@ -1643,101 +1672,49 @@ "data" ], "additionalProperties": false, - "description": "Session event \"session.canvas.opened\".", - "title": "CanvasOpenedEvent" + "description": "Session event \"auto_mode_switch.requested\". Auto mode switch request notification requiring user approval", + "title": "AutoModeSwitchRequestedEvent" }, - "CanvasRegistryChangedCanvas": { - "type": "object", - "properties": { - "extensionId": { - "type": "string", - "description": "Owning provider identifier" - }, - "extensionName": { - "type": "string", - "description": "Owning extension display name, when available" - }, - "canvasId": { - "type": "string", - "description": "Provider-local canvas identifier" - }, - "displayName": { - "type": "string", - "description": "Human-readable canvas name" - }, - "description": { - "type": "string", - "minLength": 1, - "description": "Short, single-sentence description shown to the agent in canvas catalogs." - }, - "inputSchema": { - "type": "object", - "additionalProperties": {}, - "description": "JSON Schema for canvas open input", - "x-opaque-json": true - }, - "actions": { - "type": "array", - "items": { - "$ref": "#/definitions/CanvasRegistryChangedCanvasAction" - }, - "description": "Actions the agent or host may invoke" - } - }, - "required": [ - "extensionId", - "canvasId", - "displayName", - "description" + "AutoModeSwitchResponse": { + "type": "string", + "enum": [ + "yes", + "yes_always", + "no" ], - "additionalProperties": false, - "title": "CanvasRegistryChangedCanvas", - "description": "Schema for the `CanvasRegistryChangedCanvas` type." + "description": "The user's auto-mode-switch choice", + "title": "AutoModeSwitchResponse", + "x-enumDescriptions": { + "yes": "Switch models for this request.", + "yes_always": "Switch models now and keep using the replacement automatically.", + "no": "Do not switch models." + } }, - "CanvasRegistryChangedCanvasAction": { + "AutopilotObjectiveChangedData": { "type": "object", "properties": { - "name": { - "type": "string", - "description": "Action name" + "operation": { + "$ref": "#/definitions/AutopilotObjectiveChangedOperation", + "description": "The type of operation performed on the autopilot objective state file" }, - "description": { - "type": "string", - "description": "Action description" + "id": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Current autopilot objective id, if one exists" }, - "inputSchema": { - "type": "object", - "additionalProperties": {}, - "description": "JSON Schema for action input", - "x-opaque-json": true - } - }, - "required": [ - "name" - ], - "additionalProperties": false, - "title": "CanvasRegistryChangedCanvasAction", - "description": "Schema for the `CanvasRegistryChangedCanvasAction` type." - }, - "CanvasRegistryChangedData": { - "type": "object", - "properties": { - "canvases": { - "type": "array", - "items": { - "$ref": "#/definitions/CanvasRegistryChangedCanvas" - }, - "description": "Canvas declarations currently available" + "status": { + "$ref": "#/definitions/AutopilotObjectiveChangedStatus", + "description": "Current autopilot objective status, if one exists" } }, "required": [ - "canvases" + "operation" ], "additionalProperties": false, - "title": "CanvasRegistryChangedData", - "description": "Schema for the `CanvasRegistryChangedData` type." + "description": "Autopilot objective state file operation details indicating what changed", + "title": "AutopilotObjectiveChangedData" }, - "CanvasRegistryChangedEvent": { + "AutopilotObjectiveChangedEvent": { "type": "object", "properties": { "id": { @@ -1764,8 +1741,7 @@ }, "ephemeral": { "type": "boolean", - "const": true, - "description": "Always true for events that are transient and not persisted to the session event log on disk." + "description": "When true, the event is transient and not persisted to the session event log on disk" }, "agentId": { "type": "string", @@ -1773,39 +1749,65 @@ }, "type": { "type": "string", - "const": "session.canvas.registry_changed", - "description": "Type discriminator. Always \"session.canvas.registry_changed\"." + "const": "session.autopilot_objective_changed", + "description": "Type discriminator. Always \"session.autopilot_objective_changed\"." }, "data": { - "$ref": "#/definitions/CanvasRegistryChangedData", - "description": "Schema for the `CanvasRegistryChangedData` type." + "$ref": "#/definitions/AutopilotObjectiveChangedData", + "description": "Autopilot objective state file operation details indicating what changed" } }, "required": [ "id", "timestamp", "parentId", - "ephemeral", "type", "data" ], "additionalProperties": false, - "description": "Session event \"session.canvas.registry_changed\".", - "title": "CanvasRegistryChangedEvent" + "description": "Session event \"session.autopilot_objective_changed\". Autopilot objective state file operation details indicating what changed", + "title": "AutopilotObjectiveChangedEvent" }, - "CapabilitiesChangedData": { + "AutopilotObjectiveChangedOperation": { + "type": "string", + "enum": [ + "create", + "update", + "delete" + ], + "description": "The type of operation performed on the autopilot objective state file", + "title": "AutopilotObjectiveChangedOperation", + "x-enumDescriptions": { + "create": "Autopilot objective state file was created for a new objective.", + "update": "Autopilot objective state file was updated for an existing objective.", + "delete": "Autopilot objective state file was deleted or cleared." + } + }, + "AutopilotObjectiveChangedStatus": { + "type": "string", + "enum": [ + "active", + "paused", + "cap_reached", + "completed" + ], + "description": "Current autopilot objective status, if one exists", + "title": "AutopilotObjectiveChangedStatus", + "x-enumDescriptions": { + "active": "Objective is active and can drive autopilot continuations.", + "paused": "Objective is paused and will not drive autopilot continuations.", + "cap_reached": "Legacy objective state indicating the previous continuation cap was reached.", + "completed": "Objective was completed by the agent." + } + }, + "BackgroundTasksChangedData": { "type": "object", - "properties": { - "ui": { - "$ref": "#/definitions/CapabilitiesChangedUI", - "description": "UI capability changes" - } - }, + "properties": {}, "additionalProperties": false, - "description": "Session capability change notification", - "title": "CapabilitiesChangedData" + "title": "BackgroundTasksChangedData", + "description": "Schema for the `BackgroundTasksChangedData` type." }, - "CapabilitiesChangedEvent": { + "BackgroundTasksChangedEvent": { "type": "object", "properties": { "id": { @@ -1841,12 +1843,12 @@ }, "type": { "type": "string", - "const": "capabilities.changed", - "description": "Type discriminator. Always \"capabilities.changed\"." + "const": "session.background_tasks_changed", + "description": "Type discriminator. Always \"session.background_tasks_changed\"." }, "data": { - "$ref": "#/definitions/CapabilitiesChangedData", - "description": "Session capability change notification" + "$ref": "#/definitions/BackgroundTasksChangedData", + "description": "Schema for the `BackgroundTasksChangedData` type." } }, "required": [ @@ -1858,58 +1860,91 @@ "data" ], "additionalProperties": false, - "description": "Session event \"capabilities.changed\". Session capability change notification", - "title": "CapabilitiesChangedEvent" + "description": "Session event \"session.background_tasks_changed\".", + "title": "BackgroundTasksChangedEvent" }, - "CapabilitiesChangedUI": { + "CanvasOpenedAvailability": { + "type": "string", + "enum": [ + "ready", + "stale" + ], + "description": "Runtime-controlled routing state for the instance. \"ready\" when the provider connection is live; \"stale\" when the provider has gone away and the instance is awaiting rebinding.", + "title": "CanvasOpenedAvailability", + "x-enumDescriptions": { + "ready": "Provider connection is live; actions can be invoked.", + "stale": "Provider has gone away; the instance is awaiting rebinding." + } + }, + "CanvasOpenedData": { "type": "object", "properties": { - "elicitation": { - "type": "boolean", - "description": "Whether elicitation is now supported" + "instanceId": { + "type": "string", + "description": "Stable caller-supplied canvas instance identifier" }, - "mcpApps": { - "type": "boolean", - "description": "Whether MCP Apps (SEP-1865) UI passthrough is now supported" + "extensionId": { + "type": "string", + "description": "Owning provider identifier" }, - "canvases": { - "type": "boolean", - "description": "Whether canvas rendering is now supported" - } - }, - "additionalProperties": false, - "description": "UI capability changes", - "title": "CapabilitiesChangedUI" - }, - "CommandCompletedData": { - "type": "object", - "properties": { - "requestId": { + "extensionName": { "type": "string", - "description": "Request ID of the resolved command request; clients should dismiss any UI for this request" - } - }, - "required": [ - "requestId" - ], - "additionalProperties": false, - "description": "Queued command completion notification signaling UI dismissal", - "title": "CommandCompletedData" - }, - "CommandCompletedEvent": { - "type": "object", - "properties": { - "id": { + "description": "Owning extension display name, when available" + }, + "canvasId": { "type": "string", - "format": "uuid", - "description": "Unique event identifier (UUID v4), generated when the event is emitted" + "description": "Provider-local canvas identifier" }, - "timestamp": { + "title": { "type": "string", - "format": "date-time", - "description": "ISO 8601 timestamp when the event was created" + "description": "Rendered title" }, - "parentId": { + "status": { + "type": "string", + "description": "Provider-supplied status text" + }, + "url": { + "type": "string", + "description": "URL for web-rendered canvases" + }, + "input": { + "description": "Input supplied when the instance was opened", + "x-opaque-json": true + }, + "reopen": { + "type": "boolean", + "description": "Whether this notification represents an idempotent reopen" + }, + "availability": { + "$ref": "#/definitions/CanvasOpenedAvailability", + "description": "Runtime-controlled routing state for the instance. \"ready\" when the provider connection is live; \"stale\" when the provider has gone away and the instance is awaiting rebinding." + } + }, + "required": [ + "instanceId", + "extensionId", + "canvasId", + "reopen", + "availability" + ], + "additionalProperties": false, + "title": "CanvasOpenedData", + "description": "Schema for the `CanvasOpenedData` type." + }, + "CanvasOpenedEvent": { + "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", @@ -1932,12 +1967,12 @@ }, "type": { "type": "string", - "const": "command.completed", - "description": "Type discriminator. Always \"command.completed\"." + "const": "session.canvas.opened", + "description": "Type discriminator. Always \"session.canvas.opened\"." }, "data": { - "$ref": "#/definitions/CommandCompletedData", - "description": "Queued command completion notification signaling UI dismissal" + "$ref": "#/definitions/CanvasOpenedData", + "description": "Schema for the `CanvasOpenedData` type." } }, "required": [ @@ -1949,40 +1984,101 @@ "data" ], "additionalProperties": false, - "description": "Session event \"command.completed\". Queued command completion notification signaling UI dismissal", - "title": "CommandCompletedEvent" + "description": "Session event \"session.canvas.opened\".", + "title": "CanvasOpenedEvent" }, - "CommandExecuteData": { + "CanvasRegistryChangedCanvas": { "type": "object", "properties": { - "requestId": { + "extensionId": { "type": "string", - "description": "Unique identifier; used to respond via session.commands.handlePendingCommand()" + "description": "Owning provider identifier" }, - "command": { + "extensionName": { "type": "string", - "description": "The full command text (e.g., /deploy production)" + "description": "Owning extension display name, when available" }, - "commandName": { + "canvasId": { "type": "string", - "description": "Command name without leading /" + "description": "Provider-local canvas identifier" }, - "args": { + "displayName": { "type": "string", - "description": "Raw argument string after the command name" + "description": "Human-readable canvas name" + }, + "description": { + "type": "string", + "minLength": 1, + "description": "Short, single-sentence description shown to the agent in canvas catalogs." + }, + "inputSchema": { + "type": "object", + "additionalProperties": {}, + "description": "JSON Schema for canvas open input", + "x-opaque-json": true + }, + "actions": { + "type": "array", + "items": { + "$ref": "#/definitions/CanvasRegistryChangedCanvasAction" + }, + "description": "Actions the agent or host may invoke" } }, "required": [ - "requestId", - "command", - "commandName", - "args" + "extensionId", + "canvasId", + "displayName", + "description" ], "additionalProperties": false, - "description": "Registered command dispatch request routed to the owning client", - "title": "CommandExecuteData" + "title": "CanvasRegistryChangedCanvas", + "description": "Schema for the `CanvasRegistryChangedCanvas` type." }, - "CommandExecuteEvent": { + "CanvasRegistryChangedCanvasAction": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Action name" + }, + "description": { + "type": "string", + "description": "Action description" + }, + "inputSchema": { + "type": "object", + "additionalProperties": {}, + "description": "JSON Schema for action input", + "x-opaque-json": true + } + }, + "required": [ + "name" + ], + "additionalProperties": false, + "title": "CanvasRegistryChangedCanvasAction", + "description": "Schema for the `CanvasRegistryChangedCanvasAction` type." + }, + "CanvasRegistryChangedData": { + "type": "object", + "properties": { + "canvases": { + "type": "array", + "items": { + "$ref": "#/definitions/CanvasRegistryChangedCanvas" + }, + "description": "Canvas declarations currently available" + } + }, + "required": [ + "canvases" + ], + "additionalProperties": false, + "title": "CanvasRegistryChangedData", + "description": "Schema for the `CanvasRegistryChangedData` type." + }, + "CanvasRegistryChangedEvent": { "type": "object", "properties": { "id": { @@ -2018,12 +2114,12 @@ }, "type": { "type": "string", - "const": "command.execute", - "description": "Type discriminator. Always \"command.execute\"." + "const": "session.canvas.registry_changed", + "description": "Type discriminator. Always \"session.canvas.registry_changed\"." }, "data": { - "$ref": "#/definitions/CommandExecuteData", - "description": "Registered command dispatch request routed to the owning client" + "$ref": "#/definitions/CanvasRegistryChangedData", + "description": "Schema for the `CanvasRegistryChangedData` type." } }, "required": [ @@ -2035,30 +2131,22 @@ "data" ], "additionalProperties": false, - "description": "Session event \"command.execute\". Registered command dispatch request routed to the owning client", - "title": "CommandExecuteEvent" + "description": "Session event \"session.canvas.registry_changed\".", + "title": "CanvasRegistryChangedEvent" }, - "CommandQueuedData": { + "CapabilitiesChangedData": { "type": "object", "properties": { - "requestId": { - "type": "string", - "description": "Unique identifier for this request; used to respond via session.respondToQueuedCommand()" - }, - "command": { - "type": "string", - "description": "The slash command text to be executed (e.g., /help, /clear)" + "ui": { + "$ref": "#/definitions/CapabilitiesChangedUI", + "description": "UI capability changes" } }, - "required": [ - "requestId", - "command" - ], "additionalProperties": false, - "description": "Queued slash command dispatch request for client execution", - "title": "CommandQueuedData" + "description": "Session capability change notification", + "title": "CapabilitiesChangedData" }, - "CommandQueuedEvent": { + "CapabilitiesChangedEvent": { "type": "object", "properties": { "id": { @@ -2094,12 +2182,12 @@ }, "type": { "type": "string", - "const": "command.queued", - "description": "Type discriminator. Always \"command.queued\"." + "const": "capabilities.changed", + "description": "Type discriminator. Always \"capabilities.changed\"." }, "data": { - "$ref": "#/definitions/CommandQueuedData", - "description": "Queued slash command dispatch request for client execution" + "$ref": "#/definitions/CapabilitiesChangedData", + "description": "Session capability change notification" } }, "required": [ @@ -2111,47 +2199,45 @@ "data" ], "additionalProperties": false, - "description": "Session event \"command.queued\". Queued slash command dispatch request for client execution", - "title": "CommandQueuedEvent" + "description": "Session event \"capabilities.changed\". Session capability change notification", + "title": "CapabilitiesChangedEvent" }, - "CommandsChangedCommand": { + "CapabilitiesChangedUI": { "type": "object", "properties": { - "name": { - "type": "string", - "description": "Slash command name without the leading slash." + "elicitation": { + "type": "boolean", + "description": "Whether elicitation is now supported" }, - "description": { - "type": "string", - "description": "Optional human-readable command description." + "mcpApps": { + "type": "boolean", + "description": "Whether MCP Apps (SEP-1865) UI passthrough is now supported" + }, + "canvases": { + "type": "boolean", + "description": "Whether canvas rendering is now supported" } }, - "required": [ - "name" - ], "additionalProperties": false, - "title": "CommandsChangedCommand", - "description": "Schema for the `CommandsChangedCommand` type." + "description": "UI capability changes", + "title": "CapabilitiesChangedUI" }, - "CommandsChangedData": { + "CommandCompletedData": { "type": "object", "properties": { - "commands": { - "type": "array", - "items": { - "$ref": "#/definitions/CommandsChangedCommand" - }, - "description": "Current list of registered SDK commands" + "requestId": { + "type": "string", + "description": "Request ID of the resolved command request; clients should dismiss any UI for this request" } }, "required": [ - "commands" + "requestId" ], "additionalProperties": false, - "description": "SDK command registration change notification", - "title": "CommandsChangedData" + "description": "Queued command completion notification signaling UI dismissal", + "title": "CommandCompletedData" }, - "CommandsChangedEvent": { + "CommandCompletedEvent": { "type": "object", "properties": { "id": { @@ -2187,12 +2273,12 @@ }, "type": { "type": "string", - "const": "commands.changed", - "description": "Type discriminator. Always \"commands.changed\"." + "const": "command.completed", + "description": "Type discriminator. Always \"command.completed\"." }, "data": { - "$ref": "#/definitions/CommandsChangedData", - "description": "SDK command registration change notification" + "$ref": "#/definitions/CommandCompletedData", + "description": "Queued command completion notification signaling UI dismissal" } }, "required": [ @@ -2204,13 +2290,268 @@ "data" ], "additionalProperties": false, - "description": "Session event \"commands.changed\". SDK command registration change notification", - "title": "CommandsChangedEvent" + "description": "Session event \"command.completed\". Queued command completion notification signaling UI dismissal", + "title": "CommandCompletedEvent" }, - "CompactionCompleteCompactionTokensUsed": { + "CommandExecuteData": { "type": "object", "properties": { - "inputTokens": { + "requestId": { + "type": "string", + "description": "Unique identifier; used to respond via session.commands.handlePendingCommand()" + }, + "command": { + "type": "string", + "description": "The full command text (e.g., /deploy production)" + }, + "commandName": { + "type": "string", + "description": "Command name without leading /" + }, + "args": { + "type": "string", + "description": "Raw argument string after the command name" + } + }, + "required": [ + "requestId", + "command", + "commandName", + "args" + ], + "additionalProperties": false, + "description": "Registered command dispatch request routed to the owning client", + "title": "CommandExecuteData" + }, + "CommandExecuteEvent": { + "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", + "const": true, + "description": "Always true for events that are 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": "command.execute", + "description": "Type discriminator. Always \"command.execute\"." + }, + "data": { + "$ref": "#/definitions/CommandExecuteData", + "description": "Registered command dispatch request routed to the owning client" + } + }, + "required": [ + "id", + "timestamp", + "parentId", + "ephemeral", + "type", + "data" + ], + "additionalProperties": false, + "description": "Session event \"command.execute\". Registered command dispatch request routed to the owning client", + "title": "CommandExecuteEvent" + }, + "CommandQueuedData": { + "type": "object", + "properties": { + "requestId": { + "type": "string", + "description": "Unique identifier for this request; used to respond via session.respondToQueuedCommand()" + }, + "command": { + "type": "string", + "description": "The slash command text to be executed (e.g., /help, /clear)" + } + }, + "required": [ + "requestId", + "command" + ], + "additionalProperties": false, + "description": "Queued slash command dispatch request for client execution", + "title": "CommandQueuedData" + }, + "CommandQueuedEvent": { + "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", + "const": true, + "description": "Always true for events that are 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": "command.queued", + "description": "Type discriminator. Always \"command.queued\"." + }, + "data": { + "$ref": "#/definitions/CommandQueuedData", + "description": "Queued slash command dispatch request for client execution" + } + }, + "required": [ + "id", + "timestamp", + "parentId", + "ephemeral", + "type", + "data" + ], + "additionalProperties": false, + "description": "Session event \"command.queued\". Queued slash command dispatch request for client execution", + "title": "CommandQueuedEvent" + }, + "CommandsChangedCommand": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Slash command name without the leading slash." + }, + "description": { + "type": "string", + "description": "Optional human-readable command description." + } + }, + "required": [ + "name" + ], + "additionalProperties": false, + "title": "CommandsChangedCommand", + "description": "Schema for the `CommandsChangedCommand` type." + }, + "CommandsChangedData": { + "type": "object", + "properties": { + "commands": { + "type": "array", + "items": { + "$ref": "#/definitions/CommandsChangedCommand" + }, + "description": "Current list of registered SDK commands" + } + }, + "required": [ + "commands" + ], + "additionalProperties": false, + "description": "SDK command registration change notification", + "title": "CommandsChangedData" + }, + "CommandsChangedEvent": { + "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", + "const": true, + "description": "Always true for events that are 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": "commands.changed", + "description": "Type discriminator. Always \"commands.changed\"." + }, + "data": { + "$ref": "#/definitions/CommandsChangedData", + "description": "SDK command registration change notification" + } + }, + "required": [ + "id", + "timestamp", + "parentId", + "ephemeral", + "type", + "data" + ], + "additionalProperties": false, + "description": "Session event \"commands.changed\". SDK command registration change notification", + "title": "CommandsChangedEvent" + }, + "CompactionCompleteCompactionTokensUsed": { + "type": "object", + "properties": { + "inputTokens": { "type": "integer", "minimum": 0, "description": "Input tokens consumed by the compaction LLM call" @@ -2581,6 +2922,19 @@ "description": "Session event \"session.context_changed\". Updated working directory and git context after the change", "title": "ContextChangedEvent" }, + "ContextTier": { + "type": "string", + "enum": [ + "default", + "long_context" + ], + "title": "ContextTier", + "x-enumDescriptions": { + "default": "Default context tier with standard context window size.", + "long_context": "Extended context tier with a larger context window." + }, + "description": "Allowed values for the `ContextTier` enumeration." + }, "CustomAgentsUpdatedAgent": { "type": "object", "properties": { @@ -3468,6 +3822,81 @@ "description": "Session event \"exit_plan_mode.requested\". Plan approval request with plan content and available user actions", "title": "ExitPlanModeRequestedEvent" }, + "ExtensionsAttachmentsPushedData": { + "type": "object", + "properties": { + "attachments": { + "type": "array", + "items": { + "$ref": "#/definitions/Attachment", + "description": "A user message attachment — a file, directory, code selection, blob, GitHub reference, or extension-supplied context payload" + }, + "description": "Attachments contributed by an extension; the host should surface these as composer pills and forward them via the next session.send call." + } + }, + "required": [ + "attachments" + ], + "additionalProperties": false, + "title": "ExtensionsAttachmentsPushedData", + "description": "Schema for the `ExtensionsAttachmentsPushedData` type." + }, + "ExtensionsAttachmentsPushedEvent": { + "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", + "const": true, + "description": "Always true for events that are 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": "session.extensions.attachments_pushed", + "description": "Type discriminator. Always \"session.extensions.attachments_pushed\"." + }, + "data": { + "$ref": "#/definitions/ExtensionsAttachmentsPushedData", + "description": "Schema for the `ExtensionsAttachmentsPushedData` type." + } + }, + "required": [ + "id", + "timestamp", + "parentId", + "ephemeral", + "type", + "data" + ], + "additionalProperties": false, + "description": "Session event \"session.extensions.attachments_pushed\".", + "title": "ExtensionsAttachmentsPushedEvent" + }, "ExtensionsLoadedData": { "type": "object", "properties": { @@ -5121,15 +5550,7 @@ "contextTier": { "anyOf": [ { - "type": "string", - "enum": [ - "default", - "long_context" - ], - "x-enumDescriptions": { - "default": "Default context tier with standard context window size.", - "long_context": "Extended context tier with a larger context window." - } + "$ref": "#/definitions/ContextTier" }, { "type": "null" @@ -6915,15 +7336,7 @@ "contextTier": { "anyOf": [ { - "type": "string", - "enum": [ - "default", - "long_context" - ], - "x-enumDescriptions": { - "default": "Default context tier with standard context window size.", - "long_context": "Extended context tier with a larger context window." - } + "$ref": "#/definitions/ContextTier" }, { "type": "null" @@ -7676,6 +8089,10 @@ "$ref": "#/definitions/CanvasRegistryChangedEvent", "description": "Session event \"session.canvas.registry_changed\"." }, + { + "$ref": "#/definitions/ExtensionsAttachmentsPushedEvent", + "description": "Session event \"session.extensions.attachments_pushed\"." + }, { "$ref": "#/definitions/McpAppToolCallCompleteEvent", "description": "Session event \"mcp_app.tool_call_complete\". MCP App view called a tool on a connected MCP server (SEP-1865)" @@ -8380,15 +8797,7 @@ "contextTier": { "anyOf": [ { - "type": "string", - "enum": [ - "default", - "long_context" - ], - "x-enumDescriptions": { - "default": "Default context tier with standard context window size.", - "long_context": "Extended context tier with a larger context window." - } + "$ref": "#/definitions/ContextTier" }, { "type": "null" @@ -10293,6 +10702,10 @@ "description": "Arguments passed to the tool", "x-opaque-json": true }, + "model": { + "type": "string", + "description": "Model identifier that generated this tool call" + }, "mcpServerName": { "type": "string", "description": "Name of the MCP server hosting this tool, when the tool is an MCP tool" @@ -10927,298 +11340,6 @@ "shell": "The agent is in shell-focused UI mode." } }, - "UserMessageAttachment": { - "anyOf": [ - { - "$ref": "#/definitions/UserMessageAttachmentFile", - "description": "File attachment" - }, - { - "$ref": "#/definitions/UserMessageAttachmentDirectory", - "description": "Directory attachment" - }, - { - "$ref": "#/definitions/UserMessageAttachmentSelection", - "description": "Code selection attachment from an editor" - }, - { - "$ref": "#/definitions/UserMessageAttachmentGithubReference", - "description": "GitHub issue, pull request, or discussion reference" - }, - { - "$ref": "#/definitions/UserMessageAttachmentBlob", - "description": "Blob attachment with inline base64-encoded data" - } - ], - "description": "A user message attachment — a file, directory, code selection, blob, or GitHub reference", - "title": "UserMessageAttachment" - }, - "UserMessageAttachmentBlob": { - "type": "object", - "properties": { - "type": { - "type": "string", - "const": "blob", - "description": "Attachment type discriminator" - }, - "data": { - "type": "string", - "description": "Base64-encoded content", - "contentEncoding": "base64" - }, - "mimeType": { - "type": "string", - "description": "MIME type of the inline data" - }, - "displayName": { - "type": "string", - "description": "User-facing display name for the attachment" - } - }, - "required": [ - "type", - "data", - "mimeType" - ], - "additionalProperties": false, - "description": "Blob attachment with inline base64-encoded data", - "title": "UserMessageAttachmentBlob" - }, - "UserMessageAttachmentDirectory": { - "type": "object", - "properties": { - "type": { - "type": "string", - "const": "directory", - "description": "Attachment type discriminator" - }, - "path": { - "type": "string", - "description": "Absolute directory path" - }, - "displayName": { - "type": "string", - "description": "User-facing display name for the attachment" - } - }, - "required": [ - "type", - "path", - "displayName" - ], - "additionalProperties": false, - "description": "Directory attachment", - "title": "UserMessageAttachmentDirectory" - }, - "UserMessageAttachmentFile": { - "type": "object", - "properties": { - "type": { - "type": "string", - "const": "file", - "description": "Attachment type discriminator" - }, - "path": { - "type": "string", - "description": "Absolute file path" - }, - "displayName": { - "type": "string", - "description": "User-facing display name for the attachment" - }, - "lineRange": { - "$ref": "#/definitions/UserMessageAttachmentFileLineRange", - "description": "Optional line range to scope the attachment to a specific section of the file" - } - }, - "required": [ - "type", - "path", - "displayName" - ], - "additionalProperties": false, - "description": "File attachment", - "title": "UserMessageAttachmentFile" - }, - "UserMessageAttachmentFileLineRange": { - "type": "object", - "properties": { - "start": { - "type": "integer", - "exclusiveMinimum": 0, - "description": "Start line number (1-based)" - }, - "end": { - "type": "integer", - "exclusiveMinimum": 0, - "description": "End line number (1-based, inclusive)" - } - }, - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "description": "Optional line range to scope the attachment to a specific section of the file", - "title": "UserMessageAttachmentFileLineRange" - }, - "UserMessageAttachmentGithubReference": { - "type": "object", - "properties": { - "type": { - "type": "string", - "const": "github_reference", - "description": "Attachment type discriminator" - }, - "number": { - "type": "integer", - "exclusiveMinimum": 0, - "description": "Issue, pull request, or discussion number" - }, - "title": { - "type": "string", - "description": "Title of the referenced item" - }, - "referenceType": { - "$ref": "#/definitions/UserMessageAttachmentGithubReferenceType", - "description": "Type of GitHub reference" - }, - "state": { - "type": "string", - "description": "Current state of the referenced item (e.g., open, closed, merged)" - }, - "url": { - "type": "string", - "description": "URL to the referenced item on GitHub" - } - }, - "required": [ - "type", - "number", - "title", - "referenceType", - "state", - "url" - ], - "additionalProperties": false, - "description": "GitHub issue, pull request, or discussion reference", - "title": "UserMessageAttachmentGithubReference" - }, - "UserMessageAttachmentGithubReferenceType": { - "type": "string", - "enum": [ - "issue", - "pr", - "discussion" - ], - "description": "Type of GitHub reference", - "title": "UserMessageAttachmentGithubReferenceType", - "x-enumDescriptions": { - "issue": "GitHub issue reference.", - "pr": "GitHub pull request reference.", - "discussion": "GitHub discussion reference." - } - }, - "UserMessageAttachmentSelection": { - "type": "object", - "properties": { - "type": { - "type": "string", - "const": "selection", - "description": "Attachment type discriminator" - }, - "filePath": { - "type": "string", - "description": "Absolute path to the file containing the selection" - }, - "displayName": { - "type": "string", - "description": "User-facing display name for the selection" - }, - "text": { - "type": "string", - "description": "The selected text content" - }, - "selection": { - "$ref": "#/definitions/UserMessageAttachmentSelectionDetails", - "description": "Position range of the selection within the file" - } - }, - "required": [ - "type", - "filePath", - "displayName", - "text", - "selection" - ], - "additionalProperties": false, - "description": "Code selection attachment from an editor", - "title": "UserMessageAttachmentSelection" - }, - "UserMessageAttachmentSelectionDetails": { - "type": "object", - "properties": { - "start": { - "$ref": "#/definitions/UserMessageAttachmentSelectionDetailsStart", - "description": "Start position of the selection" - }, - "end": { - "$ref": "#/definitions/UserMessageAttachmentSelectionDetailsEnd", - "description": "End position of the selection" - } - }, - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "description": "Position range of the selection within the file", - "title": "UserMessageAttachmentSelectionDetails" - }, - "UserMessageAttachmentSelectionDetailsEnd": { - "type": "object", - "properties": { - "line": { - "type": "integer", - "minimum": 0, - "description": "End line number (0-based)" - }, - "character": { - "type": "integer", - "minimum": 0, - "description": "End character offset within the line (0-based)" - } - }, - "required": [ - "line", - "character" - ], - "additionalProperties": false, - "description": "End position of the selection", - "title": "UserMessageAttachmentSelectionDetailsEnd" - }, - "UserMessageAttachmentSelectionDetailsStart": { - "type": "object", - "properties": { - "line": { - "type": "integer", - "minimum": 0, - "description": "Start line number (0-based)" - }, - "character": { - "type": "integer", - "minimum": 0, - "description": "Start character offset within the line (0-based)" - } - }, - "required": [ - "line", - "character" - ], - "additionalProperties": false, - "description": "Start position of the selection", - "title": "UserMessageAttachmentSelectionDetailsStart" - }, "UserMessageData": { "type": "object", "properties": { @@ -11233,8 +11354,8 @@ "attachments": { "type": "array", "items": { - "$ref": "#/definitions/UserMessageAttachment", - "description": "A user message attachment — a file, directory, code selection, blob, or GitHub reference" + "$ref": "#/definitions/Attachment", + "description": "A user message attachment — a file, directory, code selection, blob, GitHub reference, or extension-supplied context payload" }, "description": "Files, selections, or GitHub references attached to the message" }, diff --git a/src/github/copilot_sdk/client.clj b/src/github/copilot_sdk/client.clj index 992cd79..f2918f5 100644 --- a/src/github/copilot_sdk/client.clj +++ b/src/github/copilot_sdk/client.clj @@ -1534,18 +1534,6 @@ (assoc :maxPromptTokens (:maxInputTokens wire))) wire))) -(defn- context-tier->wire - "Convert a Clojure :context-tier keyword to the wire string value. - The CLI expects \"default\" / \"long_context\" (underscore), so csk - camelCasing would produce the wrong value — we map explicitly." - [tier] - (case tier - :default "default" - :long-context "long_context" - nil nil - (throw (ex-info "Invalid :context-tier value (expected :default or :long-context)" - {:context-tier tier})))) - (defn- large-output->wire "Convert a :large-output config map to wire shape, accepting both the pre-existing `:output-dir` key and the upstream-aligned alias @@ -1743,7 +1731,7 @@ (:reasoning-effort config) (assoc :reasoning-effort (:reasoning-effort config)) (:reasoning-summary config) (assoc :reasoning-summary (:reasoning-summary config)) (contains? config :context-tier) - (assoc :context-tier (context-tier->wire (:context-tier config))) + (assoc :context-tier (util/context-tier->wire (:context-tier config))) (:agent config) (assoc :agent (:agent config)) true (assoc :request-user-input (boolean (:on-user-input-request config))) true (assoc :request-elicitation (boolean (:on-elicitation-request config))) @@ -1857,7 +1845,7 @@ (:reasoning-effort config) (assoc :reasoning-effort (:reasoning-effort config)) (:reasoning-summary config) (assoc :reasoning-summary (:reasoning-summary config)) (contains? config :context-tier) - (assoc :context-tier (context-tier->wire (:context-tier config))) + (assoc :context-tier (util/context-tier->wire (:context-tier config))) (:agent config) (assoc :agent (:agent config)) true (assoc :request-user-input (boolean (:on-user-input-request config))) true (assoc :request-elicitation (boolean (:on-elicitation-request config))) diff --git a/src/github/copilot_sdk/generated/event_specs.clj b/src/github/copilot_sdk/generated/event_specs.clj index 4878499..631154d 100644 --- a/src/github/copilot_sdk/generated/event_specs.clj +++ b/src/github/copilot_sdk/generated/event_specs.clj @@ -45,7 +45,7 @@ (s/def :github.copilot-sdk.generated.event-specs/arguments (s/spec (fn [v950] (or (s/valid? clojure.core/any? v950) (s/valid? clojure.core/map? v950))))) -(s/def :github.copilot-sdk.generated.event-specs/attachments (s/coll-of (s/or :branch-0 clojure.core/map? :branch-1 clojure.core/map? :branch-2 clojure.core/map? :branch-3 clojure.core/map? :branch-4 clojure.core/map?))) +(s/def :github.copilot-sdk.generated.event-specs/attachments (s/coll-of (s/or :branch-0 clojure.core/map? :branch-1 clojure.core/map? :branch-2 clojure.core/map? :branch-3 clojure.core/map? :branch-4 clojure.core/map? :branch-5 clojure.core/map?))) (s/def :github.copilot-sdk.generated.event-specs/auto-approve-edits clojure.core/boolean?) @@ -443,7 +443,7 @@ (s/def :github.copilot-sdk.generated.event-specs/turn-id clojure.core/string?) -(s/def :github.copilot-sdk.generated.event-specs/type (s/spec (fn [v949] (or (s/valid? #{"session.start"} v949) (s/valid? #{"session.resume"} v949) (s/valid? #{"session.remote_steerable_changed"} v949) (s/valid? #{"session.error"} v949) (s/valid? #{"session.idle"} v949) (s/valid? #{"session.title_changed"} v949) (s/valid? #{"session.schedule_created"} v949) (s/valid? #{"session.schedule_cancelled"} v949) (s/valid? #{"session.autopilot_objective_changed"} v949) (s/valid? #{"session.info"} v949) (s/valid? #{"session.warning"} v949) (s/valid? #{"session.model_change"} v949) (s/valid? #{"session.mode_changed"} v949) (s/valid? #{"session.permissions_changed"} v949) (s/valid? #{"session.plan_changed"} v949) (s/valid? #{"session.workspace_file_changed"} v949) (s/valid? #{"session.handoff"} v949) (s/valid? #{"session.truncation"} v949) (s/valid? #{"session.snapshot_rewind"} v949) (s/valid? #{"session.shutdown"} v949) (s/valid? #{"session.context_changed"} v949) (s/valid? #{"session.usage_info"} v949) (s/valid? #{"session.compaction_start"} v949) (s/valid? #{"session.compaction_complete"} v949) (s/valid? #{"session.task_complete"} v949) (s/valid? #{"user.message"} v949) (s/valid? #{"pending_messages.modified"} v949) (s/valid? #{"assistant.turn_start"} v949) (s/valid? #{"assistant.intent"} v949) (s/valid? #{"assistant.reasoning"} v949) (s/valid? #{"assistant.reasoning_delta"} v949) (s/valid? #{"assistant.streaming_delta"} v949) (s/valid? #{"assistant.message"} v949) (s/valid? #{"assistant.message_start"} v949) (s/valid? #{"assistant.message_delta"} v949) (s/valid? #{"assistant.turn_end"} v949) (s/valid? #{"assistant.usage"} v949) (s/valid? #{"model.call_failure"} v949) (s/valid? #{"abort"} v949) (s/valid? #{"tool.user_requested"} v949) (s/valid? #{"tool.execution_start"} v949) (s/valid? #{"tool.execution_partial_result"} v949) (s/valid? #{"tool.execution_progress"} v949) (s/valid? #{"tool.execution_complete"} v949) (s/valid? #{"skill.invoked"} v949) (s/valid? #{"subagent.started"} v949) (s/valid? #{"subagent.completed"} v949) (s/valid? #{"subagent.failed"} v949) (s/valid? #{"subagent.selected"} v949) (s/valid? #{"subagent.deselected"} v949) (s/valid? #{"hook.start"} v949) (s/valid? #{"hook.end"} v949) (s/valid? #{"hook.progress"} v949) (s/valid? #{"system.message"} v949) (s/valid? #{"system.notification"} v949) (s/valid? #{"permission.requested"} v949) (s/valid? #{"permission.completed"} v949) (s/valid? #{"user_input.requested"} v949) (s/valid? #{"user_input.completed"} v949) (s/valid? #{"elicitation.requested"} v949) (s/valid? #{"elicitation.completed"} v949) (s/valid? #{"sampling.requested"} v949) (s/valid? #{"sampling.completed"} v949) (s/valid? #{"mcp.oauth_required"} v949) (s/valid? #{"mcp.oauth_completed"} v949) (s/valid? #{"session.custom_notification"} v949) (s/valid? #{"external_tool.requested"} v949) (s/valid? #{"external_tool.completed"} v949) (s/valid? #{"command.queued"} v949) (s/valid? #{"command.execute"} v949) (s/valid? #{"command.completed"} v949) (s/valid? #{"auto_mode_switch.requested"} v949) (s/valid? #{"auto_mode_switch.completed"} v949) (s/valid? #{"commands.changed"} v949) (s/valid? #{"capabilities.changed"} v949) (s/valid? #{"exit_plan_mode.requested"} v949) (s/valid? #{"exit_plan_mode.completed"} v949) (s/valid? #{"session.tools_updated"} v949) (s/valid? #{"session.background_tasks_changed"} v949) (s/valid? #{"session.skills_loaded"} v949) (s/valid? #{"session.custom_agents_updated"} v949) (s/valid? #{"session.mcp_servers_loaded"} v949) (s/valid? #{"session.mcp_server_status_changed"} v949) (s/valid? #{"session.extensions_loaded"} v949) (s/valid? #{"session.canvas.opened"} v949) (s/valid? #{"session.canvas.registry_changed"} v949) (s/valid? #{"mcp_app.tool_call_complete"} v949))))) +(s/def :github.copilot-sdk.generated.event-specs/type (s/spec (fn [v949] (or (s/valid? #{"session.start"} v949) (s/valid? #{"session.resume"} v949) (s/valid? #{"session.remote_steerable_changed"} v949) (s/valid? #{"session.error"} v949) (s/valid? #{"session.idle"} v949) (s/valid? #{"session.title_changed"} v949) (s/valid? #{"session.schedule_created"} v949) (s/valid? #{"session.schedule_cancelled"} v949) (s/valid? #{"session.autopilot_objective_changed"} v949) (s/valid? #{"session.info"} v949) (s/valid? #{"session.warning"} v949) (s/valid? #{"session.model_change"} v949) (s/valid? #{"session.mode_changed"} v949) (s/valid? #{"session.permissions_changed"} v949) (s/valid? #{"session.plan_changed"} v949) (s/valid? #{"session.workspace_file_changed"} v949) (s/valid? #{"session.handoff"} v949) (s/valid? #{"session.truncation"} v949) (s/valid? #{"session.snapshot_rewind"} v949) (s/valid? #{"session.shutdown"} v949) (s/valid? #{"session.context_changed"} v949) (s/valid? #{"session.usage_info"} v949) (s/valid? #{"session.compaction_start"} v949) (s/valid? #{"session.compaction_complete"} v949) (s/valid? #{"session.task_complete"} v949) (s/valid? #{"user.message"} v949) (s/valid? #{"pending_messages.modified"} v949) (s/valid? #{"assistant.turn_start"} v949) (s/valid? #{"assistant.intent"} v949) (s/valid? #{"assistant.reasoning"} v949) (s/valid? #{"assistant.reasoning_delta"} v949) (s/valid? #{"assistant.streaming_delta"} v949) (s/valid? #{"assistant.message"} v949) (s/valid? #{"assistant.message_start"} v949) (s/valid? #{"assistant.message_delta"} v949) (s/valid? #{"assistant.turn_end"} v949) (s/valid? #{"assistant.usage"} v949) (s/valid? #{"model.call_failure"} v949) (s/valid? #{"abort"} v949) (s/valid? #{"tool.user_requested"} v949) (s/valid? #{"tool.execution_start"} v949) (s/valid? #{"tool.execution_partial_result"} v949) (s/valid? #{"tool.execution_progress"} v949) (s/valid? #{"tool.execution_complete"} v949) (s/valid? #{"skill.invoked"} v949) (s/valid? #{"subagent.started"} v949) (s/valid? #{"subagent.completed"} v949) (s/valid? #{"subagent.failed"} v949) (s/valid? #{"subagent.selected"} v949) (s/valid? #{"subagent.deselected"} v949) (s/valid? #{"hook.start"} v949) (s/valid? #{"hook.end"} v949) (s/valid? #{"hook.progress"} v949) (s/valid? #{"system.message"} v949) (s/valid? #{"system.notification"} v949) (s/valid? #{"permission.requested"} v949) (s/valid? #{"permission.completed"} v949) (s/valid? #{"user_input.requested"} v949) (s/valid? #{"user_input.completed"} v949) (s/valid? #{"elicitation.requested"} v949) (s/valid? #{"elicitation.completed"} v949) (s/valid? #{"sampling.requested"} v949) (s/valid? #{"sampling.completed"} v949) (s/valid? #{"mcp.oauth_required"} v949) (s/valid? #{"mcp.oauth_completed"} v949) (s/valid? #{"session.custom_notification"} v949) (s/valid? #{"external_tool.requested"} v949) (s/valid? #{"external_tool.completed"} v949) (s/valid? #{"command.queued"} v949) (s/valid? #{"command.execute"} v949) (s/valid? #{"command.completed"} v949) (s/valid? #{"auto_mode_switch.requested"} v949) (s/valid? #{"auto_mode_switch.completed"} v949) (s/valid? #{"commands.changed"} v949) (s/valid? #{"capabilities.changed"} v949) (s/valid? #{"exit_plan_mode.requested"} v949) (s/valid? #{"exit_plan_mode.completed"} v949) (s/valid? #{"session.tools_updated"} v949) (s/valid? #{"session.background_tasks_changed"} v949) (s/valid? #{"session.skills_loaded"} v949) (s/valid? #{"session.custom_agents_updated"} v949) (s/valid? #{"session.mcp_servers_loaded"} v949) (s/valid? #{"session.mcp_server_status_changed"} v949) (s/valid? #{"session.extensions_loaded"} v949) (s/valid? #{"session.canvas.opened"} v949) (s/valid? #{"session.canvas.registry_changed"} v949) (s/valid? #{"session.extensions.attachments_pushed"} v949) (s/valid? #{"mcp_app.tool_call_complete"} v949))))) (s/def :github.copilot-sdk.generated.event-specs/ui clojure.core/map?) @@ -553,6 +553,8 @@ (s/def :github.copilot-sdk.generated.event-specs/session.error-data (s/keys :req-un [:github.copilot-sdk.generated.event-specs/error-type :github.copilot-sdk.generated.event-specs/message] :opt-un [:github.copilot-sdk.generated.event-specs/eligible-for-auto-switch :github.copilot-sdk.generated.event-specs/error-code :github.copilot-sdk.generated.event-specs/provider-call-id :github.copilot-sdk.generated.event-specs/service-request-id :github.copilot-sdk.generated.event-specs/stack :github.copilot-sdk.generated.event-specs/status-code :github.copilot-sdk.generated.event-specs/url])) +(s/def :github.copilot-sdk.generated.event-specs/session.extensions.attachments_pushed-data (s/keys :req-un [:github.copilot-sdk.generated.event-specs/attachments])) + (s/def :github.copilot-sdk.generated.event-specs/session.extensions_loaded-data (s/keys :req-un [:github.copilot-sdk.generated.event-specs/extensions])) (s/def :github.copilot-sdk.generated.event-specs/session.handoff-data (s/keys :req-un [:github.copilot-sdk.generated.event-specs/handoff-time :github.copilot-sdk.generated.event-specs/source-type] :opt-un [:github.copilot-sdk.generated.event-specs/context :github.copilot-sdk.generated.event-specs/host :github.copilot-sdk.generated.event-specs/remote-session-id :github.copilot-sdk.generated.event-specs/repository :github.copilot-sdk.generated.event-specs/summary])) @@ -625,7 +627,7 @@ (s/def :github.copilot-sdk.generated.event-specs/tool.execution_progress-data (s/keys :req-un [:github.copilot-sdk.generated.event-specs/progress-message :github.copilot-sdk.generated.event-specs/tool-call-id])) -(s/def :github.copilot-sdk.generated.event-specs/tool.execution_start-data (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/mcp-server-name :github.copilot-sdk.generated.event-specs/mcp-tool-name :github.copilot-sdk.generated.event-specs/parent-tool-call-id :github.copilot-sdk.generated.event-specs/turn-id])) +(s/def :github.copilot-sdk.generated.event-specs/tool.execution_start-data (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/mcp-server-name :github.copilot-sdk.generated.event-specs/mcp-tool-name :github.copilot-sdk.generated.event-specs/model :github.copilot-sdk.generated.event-specs/parent-tool-call-id :github.copilot-sdk.generated.event-specs/turn-id])) (s/def :github.copilot-sdk.generated.event-specs/tool.user_requested-data (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])) @@ -727,6 +729,8 @@ (s/def :github.copilot-sdk.generated.event-specs/session.error (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/= "session.error" (:type event))) (fn [event] (s/valid? clojure.core/string? (:id event))) (fn [event] (s/valid? :github.copilot-sdk.generated.event-specs/session.error-data (:data event))))) +(s/def :github.copilot-sdk.generated.event-specs/session.extensions.attachments_pushed (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.extensions.attachments_pushed" (:type event))) (fn [event] (s/valid? clojure.core/string? (:id event))) (fn [event] (s/valid? :github.copilot-sdk.generated.event-specs/session.extensions.attachments_pushed-data (:data event))))) + (s/def :github.copilot-sdk.generated.event-specs/session.extensions_loaded (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.extensions_loaded" (:type event))) (fn [event] (s/valid? clojure.core/string? (:id event))) (fn [event] (s/valid? :github.copilot-sdk.generated.event-specs/session.extensions_loaded-data (:data event))))) (s/def :github.copilot-sdk.generated.event-specs/session.handoff (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/= "session.handoff" (:type event))) (fn [event] (s/valid? clojure.core/string? (:id event))) (fn [event] (s/valid? :github.copilot-sdk.generated.event-specs/session.handoff-data (:data event))))) @@ -809,7 +813,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? :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" "assistant.intent" "assistant.message" "assistant.message_delta" "assistant.message_start" "assistant.reasoning" "assistant.reasoning_delta" "assistant.streaming_delta" "assistant.turn_end" "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" "hook.end" "hook.progress" "hook.start" "mcp.oauth_completed" "mcp.oauth_required" "mcp_app.tool_call_complete" "model.call_failure" "pending_messages.modified" "permission.completed" "permission.requested" "sampling.completed" "sampling.requested" "session.autopilot_objective_changed" "session.background_tasks_changed" "session.canvas.opened" "session.canvas.registry_changed" "session.compaction_complete" "session.compaction_start" "session.context_changed" "session.custom_agents_updated" "session.custom_notification" "session.error" "session.extensions_loaded" "session.handoff" "session.idle" "session.info" "session.mcp_server_status_changed" "session.mcp_servers_loaded" "session.mode_changed" "session.model_change" "session.permissions_changed" "session.plan_changed" "session.remote_steerable_changed" "session.resume" "session.schedule_cancelled" "session.schedule_created" "session.shutdown" "session.skills_loaded" "session.snapshot_rewind" "session.start" "session.task_complete" "session.title_changed" "session.tools_updated" "session.truncation" "session.usage_info" "session.warning" "session.workspace_file_changed" "skill.invoked" "subagent.completed" "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" "user.message" "user_input.completed" "user_input.requested"}) +(def event-types "Set of all event-type strings known to the schema." #{"abort" "assistant.intent" "assistant.message" "assistant.message_delta" "assistant.message_start" "assistant.reasoning" "assistant.reasoning_delta" "assistant.streaming_delta" "assistant.turn_end" "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" "hook.end" "hook.progress" "hook.start" "mcp.oauth_completed" "mcp.oauth_required" "mcp_app.tool_call_complete" "model.call_failure" "pending_messages.modified" "permission.completed" "permission.requested" "sampling.completed" "sampling.requested" "session.autopilot_objective_changed" "session.background_tasks_changed" "session.canvas.opened" "session.canvas.registry_changed" "session.compaction_complete" "session.compaction_start" "session.context_changed" "session.custom_agents_updated" "session.custom_notification" "session.error" "session.extensions.attachments_pushed" "session.extensions_loaded" "session.handoff" "session.idle" "session.info" "session.mcp_server_status_changed" "session.mcp_servers_loaded" "session.mode_changed" "session.model_change" "session.permissions_changed" "session.plan_changed" "session.remote_steerable_changed" "session.resume" "session.schedule_cancelled" "session.schedule_created" "session.shutdown" "session.skills_loaded" "session.snapshot_rewind" "session.start" "session.task_complete" "session.title_changed" "session.tools_updated" "session.truncation" "session.usage_info" "session.warning" "session.workspace_file_changed" "skill.invoked" "subagent.completed" "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" "user.message" "user_input.completed" "user_input.requested"}) (defmulti event-mm :type) @@ -905,6 +909,8 @@ (defmethod event-mm "session.error" [_] (s/get-spec :github.copilot-sdk.generated.event-specs/session.error)) +(defmethod event-mm "session.extensions.attachments_pushed" [_] (s/get-spec :github.copilot-sdk.generated.event-specs/session.extensions.attachments_pushed)) + (defmethod event-mm "session.extensions_loaded" [_] (s/get-spec :github.copilot-sdk.generated.event-specs/session.extensions_loaded)) (defmethod event-mm "session.handoff" [_] (s/get-spec :github.copilot-sdk.generated.event-specs/session.handoff)) diff --git a/src/github/copilot_sdk/instrument.clj b/src/github/copilot_sdk/instrument.clj index 8822f6b..69f4abc 100644 --- a/src/github/copilot_sdk/instrument.clj +++ b/src/github/copilot_sdk/instrument.clj @@ -295,6 +295,8 @@ :args (s/cat :session ::specs/session :model-id string? :opts (s/? (s/nilable (s/keys :opt-un [::specs/reasoning-effort + ::specs/reasoning-summary + ::specs/context-tier ::specs/model-capabilities])))) :ret (s/nilable ::specs/model-id)) @@ -302,6 +304,8 @@ :args (s/cat :session ::specs/session :model-id string? :opts (s/? (s/nilable (s/keys :opt-un [::specs/reasoning-effort + ::specs/reasoning-summary + ::specs/context-tier ::specs/model-capabilities])))) :ret (s/nilable ::specs/model-id)) diff --git a/src/github/copilot_sdk/protocol.clj b/src/github/copilot_sdk/protocol.clj index a9747f8..9feef25 100644 --- a/src/github/copilot_sdk/protocol.clj +++ b/src/github/copilot_sdk/protocol.clj @@ -220,6 +220,21 @@ :error {:code -32603 :message (str "Internal error: " (ex-message e))}})))))) +(defn- restore-extension-context-payloads + "Restore the opaque `:payload` of each `extension_context` attachment from + the raw (pre-`wire->clj`) attachments onto the converted attachments, so + source-defined payload keys aren't kebab-cased. Raw and converted + attachment vectors are positionally aligned (conversion preserves order + and count)." + [raw-atts conv-atts] + (mapv (fn [raw conv] + (if (and (= "extension_context" (:type raw)) + (contains? raw :payload)) + (assoc conv :payload (:payload raw)) + conv)) + raw-atts + conv-atts)) + (defn- preserve-event-opaque-fields "Given a raw wire event (pre-`wire->clj`) and a converted event, restore source-defined / opaque fields verbatim onto the converted shape so @@ -251,6 +266,19 @@ (contains? (:data raw-event) :result) (assoc-in [:data :result] (get-in raw-event [:data :result]))) + ;; Upstream schema 1.0.57: `extension_context` attachments carry an opaque + ;; caller-supplied `:payload`. These appear on `user.message` attachments + ;; (reachable via `session.getMessages`) and on the ephemeral + ;; `session.extensions.attachments_pushed` event. Restore each payload so + ;; its source-defined keys aren't kebab-cased. + ("user.message" "session.extensions.attachments_pushed") + (cond-> converted-event + (seq (get-in raw-event [:data :attachments])) + (assoc-in [:data :attachments] + (restore-extension-context-payloads + (get-in raw-event [:data :attachments]) + (get-in converted-event [:data :attachments])))) + converted-event)) (defn- normalize-incoming diff --git a/src/github/copilot_sdk/session.clj b/src/github/copilot_sdk/session.clj index c350529..330394e 100644 --- a/src/github/copilot_sdk/session.clj +++ b/src/github/copilot_sdk/session.clj @@ -1568,6 +1568,9 @@ Optional opts map: - :reasoning-effort - Reasoning effort level for the new model (\"low\", \"medium\", \"high\", \"xhigh\") + - :reasoning-summary - Reasoning summary mode for the new model (\"none\", \"concise\", \"detailed\") + - :context-tier - Context window tier for models that support it + (:default or :long-context, upstream PR #1522) - :model-capabilities - Model capabilities override map (upstream PR #1029) e.g. {:model-supports {:supports-vision true}} @@ -1576,9 +1579,12 @@ ([session model-id opts] (let [{:keys [session-id client]} session conn (connection-io client) + context-tier (util/context-tier->wire (:context-tier opts)) params (cond-> {:sessionId session-id :modelId model-id} (:reasoning-effort opts) (assoc :reasoningEffort (:reasoning-effort opts)) + (:reasoning-summary opts) (assoc :reasoningSummary (:reasoning-summary opts)) + (some? context-tier) (assoc :contextTier context-tier) (:model-capabilities opts) (assoc :modelCapabilities (util/clj->wire (:model-capabilities opts)))) result (proto/send-request! conn "session.model.switchTo" params)] diff --git a/src/github/copilot_sdk/specs.clj b/src/github/copilot_sdk/specs.clj index c1c510e..20ca71c 100644 --- a/src/github/copilot_sdk/specs.clj +++ b/src/github/copilot_sdk/specs.clj @@ -1269,7 +1269,7 @@ (s/def ::tool.execution_start-data (s/keys :req-un [::tool-call-id ::tool-name] - :opt-un [::arguments ::parent-tool-call-id ::mcp-server-name ::mcp-tool-name])) + :opt-un [::arguments ::parent-tool-call-id ::mcp-server-name ::mcp-tool-name ::model])) (s/def ::progress-message string?) (s/def ::success boolean?) diff --git a/src/github/copilot_sdk/util.clj b/src/github/copilot_sdk/util.clj index 6e943c9..be9a747 100644 --- a/src/github/copilot_sdk/util.clj +++ b/src/github/copilot_sdk/util.clj @@ -162,6 +162,18 @@ [attachments] (mapv attachment->wire attachments)) +(defn context-tier->wire + "Convert a Clojure :context-tier keyword to the wire string value. + The CLI expects \"default\" / \"long_context\" (underscore), so csk + camelCasing would produce the wrong value — we map explicitly." + [tier] + (case tier + :default "default" + :long-context "long_context" + nil nil + (throw (ex-info "Invalid :context-tier value (expected :default or :long-context)" + {:context-tier tier})))) + ;; ----------------------------------------------------------------------------- ;; Event type normalization ;; ----------------------------------------------------------------------------- diff --git a/test/github/copilot_sdk/integration_test.clj b/test/github/copilot_sdk/integration_test.clj index d9fc55c..5fed910 100644 --- a/test/github/copilot_sdk/integration_test.clj +++ b/test/github/copilot_sdk/integration_test.clj @@ -1624,7 +1624,85 @@ :tool-name "mcp__server__tool" :mcp-server-name "my-mcp-server" :mcp-tool-name "original-tool"}) - "tool.execution_start-data should accept mcp-server-name and mcp-tool-name"))) + "tool.execution_start-data should accept mcp-server-name and mcp-tool-name")) + (testing "tool.execution_start event data allows the :model field (upstream 1.0.57)" + (is (s/valid? :github.copilot-sdk.specs/tool.execution_start-data + {:tool-call-id "tc-1" + :tool-name "shell" + :model "claude-sonnet-4.6"}) + "tool.execution_start-data should accept the model identifier"))) + +(deftest test-extensions-attachments-pushed-event-generated + (testing "session.extensions.attachments_pushed is a known generated event type (upstream schema 1.0.57)" + (is (contains? github.copilot-sdk.generated.event-specs/event-types "session.extensions.attachments_pushed")) + (is (some? (s/get-spec :github.copilot-sdk.generated.event-specs/session.extensions.attachments_pushed)) + "generated envelope spec should be registered")) + (testing "the generated event envelope validates a well-formed ephemeral payload" + (is (s/valid? :github.copilot-sdk.generated.event-specs/session.extensions.attachments_pushed + {:type "session.extensions.attachments_pushed" + :id "evt-1" + :parent-id nil + :timestamp "2026-06-02T00:00:00Z" + :ephemeral true + :data {:attachments [{:type "extension_context" + :title "pill" + :extension-id "ext" + :captured-at "2026-06-02T00:00:00Z"}]}})))) + +(deftest test-extension-context-attachment-payload-preserved + (testing "extension_context attachment :payload survives normalize-incoming on user.message" + (let [normalize @#'protocol/normalize-incoming + raw-msg {:jsonrpc "2.0" + :method "session.event" + :params {:sessionId "abc" + :event {:type "user.message" + :data {:content "hi" + :attachments [{:type "file" + :displayName "a.txt" + :path "/tmp/a.txt"} + {:type "extension_context" + :title "My Pill" + :extensionId "my-ext" + :payload {:firstName "Foo" + :nested {:userId 42}}}]}}}} + normalized (normalize raw-msg) + atts (get-in normalized [:params :event :data :attachments]) + ext (nth atts 1)] + (is (= "extension_context" (:type ext))) + (is (= "Foo" (get-in ext [:payload :firstName])) + "payload top-level keys must not be kebab-cased") + (is (= 42 (get-in ext [:payload :nested :userId])) + "payload nested keys must not be kebab-cased") + (is (= "My Pill" (:title ext)) + "non-opaque attachment fields are still converted/preserved"))) + (testing "payload is preserved in historical events from session.getMessages responses" + (let [normalize @#'protocol/normalize-incoming + raw-response {:jsonrpc "2.0" + :id 7 + :result {:events [{:type "user.message" + :data {:content "hi" + :attachments [{:type "extension_context" + :title "P" + :extensionId "e" + :payload {:nestedKey {:userId 7}}}]}}]}} + normalized (normalize raw-response) + ext (get-in normalized [:result :events 0 :data :attachments 0])] + (is (= 7 (get-in ext [:payload :nestedKey :userId])) + "historical payload keys must not be kebab-cased"))) + (testing "extension_context payload survives on session.extensions.attachments_pushed events" + (let [normalize @#'protocol/normalize-incoming + raw-msg {:jsonrpc "2.0" + :method "session.event" + :params {:sessionId "abc" + :event {:type "session.extensions.attachments_pushed" + :data {:attachments [{:type "extension_context" + :title "P" + :extensionId "e" + :payload {:camelKey 1}}]}}}} + normalized (normalize raw-msg) + ext (get-in normalized [:params :event :data :attachments 0])] + (is (= 1 (get-in ext [:payload :camelKey])) + "payload keys must not be kebab-cased on attachments_pushed events")))) (deftest test-upstream-event-data-field-specs (testing "assistant reasoning and message fields from generated events are explicitly spec'd" @@ -3793,6 +3871,49 @@ {:model-capabilities {:model-supports {:supports-vision true}}})] (is (= true (get-in @captured-params [:modelCapabilities :modelSupports :supportsVision])))))) +(deftest test-switch-model-with-context-tier + (testing "switch-model! forwards contextTier (upstream PR #1522)" + (let [captured-params (atom nil) + _ (mock/set-request-hook! *mock-server* + (fn [method params] + (when (= method "session.model.switchTo") + (reset! captured-params params)))) + session (sdk/create-session *test-client* {:on-permission-request sdk/approve-all}) + _ (sdk/switch-model! session "gpt-5.4" {:context-tier :long-context})] + (is (= "long_context" (:contextTier @captured-params)) + "context-tier keyword must convert to the underscore wire value"))) + + (testing "switch-model! forwards reasoningSummary (setModel parity)" + (let [captured-params (atom nil) + _ (mock/set-request-hook! *mock-server* + (fn [method params] + (when (= method "session.model.switchTo") + (reset! captured-params params)))) + session (sdk/create-session *test-client* {:on-permission-request sdk/approve-all}) + _ (sdk/switch-model! session "gpt-5.4" {:reasoning-summary "concise"})] + (is (= "concise" (:reasoningSummary @captured-params))))) + + (testing "set-model! forwards contextTier (alias for switch-model!)" + (let [captured-params (atom nil) + _ (mock/set-request-hook! *mock-server* + (fn [method params] + (when (= method "session.model.switchTo") + (reset! captured-params params)))) + session (sdk/create-session *test-client* {:on-permission-request sdk/approve-all}) + _ (sdk/set-model! session "gpt-5.4" {:context-tier :default})] + (is (= "default" (:contextTier @captured-params))))) + + (testing "switch-model! omits contextTier when :context-tier is nil" + (let [captured-params (atom nil) + _ (mock/set-request-hook! *mock-server* + (fn [method params] + (when (= method "session.model.switchTo") + (reset! captured-params params)))) + session (sdk/create-session *test-client* {:on-permission-request sdk/approve-all}) + _ (sdk/switch-model! session "gpt-5.4" {:context-tier nil})] + (is (not (contains? @captured-params :contextTier)) + "nil :context-tier must be omitted, not sent as contextTier: null (switchTo schema has no null tier)")))) + (deftest test-history-compact-rpc-name (testing "compaction-compact! uses session.history.compact RPC (upstream #1039)" (let [requests (atom [])