diff --git a/.copilot-schema-version b/.copilot-schema-version index fa2dd9d..46354d7 100644 --- a/.copilot-schema-version +++ b/.copilot-schema-version @@ -1 +1 @@ -1.0.52-1 +1.0.52 diff --git a/CHANGELOG.md b/CHANGELOG.md index 76106b7..ce76e0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,60 @@ All notable changes to this project will be documented in this file. This change ## [Unreleased] +### Added (post-v1.0.0-beta.4 sync, round 5) +- **`:runtime-instructions` system message section** — New section recognized + by the SDK's `:system-message` `:customize` mode. Wire-encoded as + `"runtime_instructions"` and accepted by `::specs/system-prompt-section`. + Upstream PR #1377 also renamed `SystemPromptSection` → `SystemMessageSection` + in TypeScript; for source compatibility the Clojure side keeps + `specs/system-prompt-sections` as the canonical name and exposes + `specs/system-message-sections` (and `::specs/system-message-section`) as + aliases pointing at the same data. (upstream PR #1377) +- **`:copilot/mcp_app.tool_call_complete` event** — New session event emitted + when a tool call from an MCP App completes (upstream schema 1.0.52-4, + SEP-1865). Added to the public `event-types` set. The `:arguments` and + `:result` fields are preserved opaquely by `protocol/preserve-event-opaque-fields` + (they survive `normalize-incoming` without kebab-case rewriting so + source-defined keys round-trip verbatim). +- **Additional event-data fields (passive, via schema regen)** — All optional; + generated `:opt-un` specs pick them up automatically: + - `:service-request-id` on `:error`, `:assistant.message`, `:assistant.usage`, + `:model.call_failure`, `:session.compaction_complete` event data + (Copilot CAPI service-request-id for correlation with CAPI logs). + - `:context-tier` (`"long_context" | "default" | nil`) on + `:session.model_change` data. + - `:transport`, `:plugin-name`, `:plugin-version` on the loaded MCP server + spec inside `:session.mcp_servers_loaded` data. + - `:error` on `:session.mcp_server_status_changed` data. + - `:source` and `:trigger` (`"user-invoked" | "agent-invoked" | "context-load"`) + on `:skill.invoked` data. + - `:tool-description` and `:ui-resource` on `:tool.execution_complete` data. +- **Schema bump** — `.copilot-schema-version` advanced from `1.0.52-1` to + `1.0.52` (stable). Picked up the 1.0.52-4 pre-release (upstream PR #1393) + and then advanced to the 1.0.52 stable release (upstream PR #1405); the + shipped JSON Schemas are byte-identical between 1.0.52-4 and 1.0.52, so + no additional schema-driven changes were required. + +### Changed (post-v1.0.0-beta.4 sync, round 5) +- **BREAKING: Minimum supported protocol version raised from 2 to 3.** The + SDK will now reject CLI servers that report protocol version 2. The + back-compat shims that adapted v2 `tool.call` / `permission.request` + JSON-RPC requests into v3 broadcast-event flows have been removed from + `set-request-handler!` and from `protocol/normalize-incoming`. Clients + must use a Copilot CLI that supports protocol v3 (CLI 1.0.46 or later). + (upstream PR #1378) + +### Removed (post-v1.0.0-beta.4 sync, round 5) +- **v2 protocol RPC dispatcher cases** — `tool.call` and `permission.request` + request handlers (and their associated tests + `test-tool-call-response-shape`, `test-tool-handler-runs-on-blocking-thread`, + `test-permission-denied-with-deny-handler`, + `test-permission-approved-with-handler`, + `test-permission-unknown-session-response-shape`, + `test-permission-custom-handler`, `test-permission-no-result-v2`). v3 + broadcast handlers `handle-v3-tool-requested!` / `handle-v3-permission-requested!` + cover the same behaviour. (upstream PR #1378) + ### Added (post-v1.0.0-beta.4 sync, round 4) - **`:on-pre-mcp-tool-call` hook** — New lifecycle hook in the `:hooks` map that fires before an MCP tool call is dispatched to its server (upstream diff --git a/doc/reference/API.md b/doc/reference/API.md index 8979af2..efdd179 100644 --- a/doc/reference/API.md +++ b/doc/reference/API.md @@ -1435,6 +1435,7 @@ Convert an unqualified event keyword to a namespace-qualified `:copilot/` keywor | `:copilot/sampling.completed` | MCP sampling request completed; ephemeral | | `:copilot/session.remote_steerable_changed` | Session remote steering capability changed; data: `{:remote-steerable true/false}` | | `:copilot/capabilities.changed` | Session capabilities dynamically changed (e.g., elicitation support); ephemeral. Data: `{:ui {:elicitation true/false}}` | +| `:copilot/mcp_app.tool_call_complete` | An MCP App tool call completed (upstream schema 1.0.52-4, SEP-1865); ephemeral. Data: `{:server-name ... :tool-name ... :duration-ms ... :success bool :arguments {...} :result {...}}` — `:arguments` and `:result` are opaque source-defined maps whose keys are preserved verbatim (not kebab-cased). | ### Example: Handling Events @@ -1705,7 +1706,7 @@ For full control (removes all guardrails), use `:mode :replace`: #### Customize Mode -The `:customize` mode enables section-level overrides of the system prompt. Ten sections are configurable: +The `:customize` mode enables section-level overrides of the system prompt. Eleven sections are configurable: | Section | Description | |---------|-------------| @@ -1718,6 +1719,7 @@ The `:customize` mode enables section-level overrides of the system prompt. Ten | `:safety` | Environment limitations, prohibited actions, security | | `:tool-instructions` | Per-tool usage instructions | | `:custom-instructions` | Repository and organization custom instructions | +| `:runtime-instructions` | Runtime-provided context (system notifications, memories, mode-specific instructions, content-exclusion policy) — added in upstream PR #1377 | | `:last-instructions` | End-of-prompt instructions | Each section supports static actions (`:replace`, `:remove`, `:append`, `:prepend`) and transform callbacks (1-arity functions). @@ -1756,9 +1758,20 @@ Inspect available sections with the `system-prompt-sections` constant: ```clojure copilot/system-prompt-sections ;; => {:identity {:description "Agent identity preamble and mode statement"} -;; :tone {:description "Response style, conciseness rules, ..."} ...} +;; :tone {:description "Response style, conciseness rules, ..."} +;; :runtime-instructions {:description "Runtime instructions injected ..."} ...} ``` +Available section keys: `:identity`, `:tone`, `:tool-efficiency`, +`:environment-context`, `:code-change-rules`, `:guidelines`, `:safety`, +`:tool-instructions`, `:custom-instructions`, `:runtime-instructions` +(added in upstream PR #1377), `:last-instructions`. + +> **Naming note** — Upstream renamed `SystemPromptSection` → +> `SystemMessageSection` in the TypeScript SDK. The Clojure SDK keeps +> `system-prompt-sections` as the canonical name (for back-compat) and +> exposes `system-message-sections` as an alias. + Unknown section keywords are allowed — they gracefully fall back to appending content to additional instructions. ### Default Agent Tool Exclusions diff --git a/schemas/README.md b/schemas/README.md index c045c4e..360fa59 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.52-1` +Currently pinned version: `1.0.52` diff --git a/schemas/api.schema.json b/schemas/api.schema.json index 2e2a346..8d82d86 100644 --- a/schemas/api.schema.json +++ b/schemas/api.schema.json @@ -2094,6 +2094,217 @@ }, "stability": "experimental" } + }, + "apps": { + "readResource": { + "rpcMethod": "session.mcp.apps.readResource", + "description": "Fetch an MCP resource (typically a `ui://` MCP App bundle, per SEP-1865) from a connected server. Requires the `mcp-apps` session capability.", + "params": { + "type": "object", + "properties": { + "sessionId": { + "type": "string", + "description": "Target session identifier" + }, + "serverName": { + "type": "string", + "minLength": 1, + "pattern": "^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$", + "description": "Name of the MCP server hosting the resource" + }, + "uri": { + "type": "string", + "description": "Resource URI (typically ui://...)" + } + }, + "required": [ + "sessionId", + "serverName", + "uri" + ], + "additionalProperties": false, + "description": "MCP server and resource URI to fetch.", + "title": "McpAppsReadResourceRequest" + }, + "result": { + "$ref": "#/definitions/McpAppsReadResourceResult", + "description": "Resource contents returned by the MCP server." + }, + "stability": "experimental" + }, + "listTools": { + "rpcMethod": "session.mcp.apps.listTools", + "description": "List tools that an MCP App view is allowed to call (SEP-1865 visibility filter). Returns tools whose `_meta.ui.visibility` is unset (default `[\"model\",\"app\"]`) or includes `\"app\"`.", + "params": { + "type": "object", + "properties": { + "sessionId": { + "type": "string", + "description": "Target session identifier" + }, + "serverName": { + "type": "string", + "minLength": 1, + "pattern": "^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$", + "description": "MCP server hosting the app" + }, + "originServerName": { + "type": "string", + "minLength": 1, + "pattern": "^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$", + "description": "**Required.** Server whose ui:// view issued the request. Per SEP-1865 ('callable by the app from this server only'), the call is rejected when this differs from `serverName`, and rejected outright when missing." + } + }, + "required": [ + "sessionId", + "serverName", + "originServerName" + ], + "additionalProperties": false, + "description": "MCP server to list app-callable tools for.", + "title": "McpAppsListToolsRequest" + }, + "result": { + "$ref": "#/definitions/McpAppsListToolsResult", + "description": "App-callable tools from the named MCP server." + }, + "stability": "experimental" + }, + "callTool": { + "rpcMethod": "session.mcp.apps.callTool", + "description": "Call an MCP tool from an MCP App view (SEP-1865). Enforces the visibility check that prevents an app iframe from invoking model-only tools. Returns the standard MCP `CallToolResult`.", + "params": { + "type": "object", + "properties": { + "sessionId": { + "type": "string", + "description": "Target session identifier" + }, + "serverName": { + "type": "string", + "minLength": 1, + "pattern": "^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$", + "description": "MCP server hosting the tool" + }, + "toolName": { + "type": "string", + "description": "MCP tool name" + }, + "arguments": { + "type": "object", + "additionalProperties": { + "x-opaque-json": true + }, + "description": "Tool arguments" + }, + "originServerName": { + "type": "string", + "minLength": 1, + "pattern": "^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$", + "description": "**Required.** Server whose ui:// view issued the request. Per SEP-1865 ('callable by the app from this server only'), the call is rejected when this differs from `serverName`, and rejected outright when missing." + } + }, + "required": [ + "sessionId", + "serverName", + "toolName", + "originServerName" + ], + "additionalProperties": false, + "description": "MCP server, tool name, and arguments to invoke from an MCP App view.", + "title": "McpAppsCallToolRequest" + }, + "result": { + "type": "object", + "additionalProperties": { + "x-opaque-json": true + }, + "description": "Standard MCP CallToolResult" + }, + "stability": "experimental" + }, + "setHostContext": { + "rpcMethod": "session.mcp.apps.setHostContext", + "description": "Replace the host context returned to MCP App guests on `ui/initialize`. Hosts use this to advertise theme, locale, or other metadata to the guest UI.", + "params": { + "type": "object", + "properties": { + "sessionId": { + "type": "string", + "description": "Target session identifier" + }, + "context": { + "$ref": "#/definitions/McpAppsSetHostContextDetails", + "description": "Host context advertised to MCP App guests" + } + }, + "required": [ + "sessionId", + "context" + ], + "additionalProperties": false, + "description": "Host context to advertise to MCP App guests.", + "title": "McpAppsSetHostContextRequest" + }, + "result": { + "type": "null" + }, + "stability": "experimental" + }, + "getHostContext": { + "rpcMethod": "session.mcp.apps.getHostContext", + "description": "Read the current host context advertised to MCP App guests.", + "params": { + "type": "object", + "description": "Identifies the target session.", + "properties": { + "sessionId": { + "type": "string", + "description": "Target session identifier" + } + }, + "required": [ + "sessionId" + ], + "additionalProperties": false + }, + "result": { + "$ref": "#/definitions/McpAppsHostContext", + "description": "Current host context advertised to MCP App guests." + }, + "stability": "experimental" + }, + "diagnose": { + "rpcMethod": "session.mcp.apps.diagnose", + "description": "Diagnose MCP Apps wiring for a specific MCP server. Reports the session capability, feature-flag state, advertised extension, and how many tools have `_meta.ui` populated.", + "params": { + "type": "object", + "properties": { + "sessionId": { + "type": "string", + "description": "Target session identifier" + }, + "serverName": { + "type": "string", + "minLength": 1, + "pattern": "^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$", + "description": "MCP server to probe" + } + }, + "required": [ + "sessionId", + "serverName" + ], + "additionalProperties": false, + "description": "MCP server to diagnose MCP Apps wiring for.", + "title": "McpAppsDiagnoseRequest" + }, + "result": { + "$ref": "#/definitions/McpAppsDiagnoseResult", + "description": "Diagnostic snapshot of MCP Apps wiring for the named server." + }, + "stability": "experimental" + } } }, "plugins": { @@ -6004,7 +6215,7 @@ }, "type": { "$ref": "#/definitions/DiscoveredMcpServerType", - "description": "Server transport type: stdio, http, sse, or memory" + "description": "Server transport type: stdio, http, sse (deprecated), or memory" }, "source": { "$ref": "#/definitions/McpServerSource", @@ -6032,12 +6243,12 @@ "sse", "memory" ], - "description": "Server transport type: stdio, http, sse, or memory", + "description": "Server transport type: stdio, http, sse (deprecated), or memory", "title": "DiscoveredMcpServerType", "x-enumDescriptions": { "stdio": "Server communicates over stdio with a local child process.", "http": "Server communicates over streamable HTTP.", - "sse": "Server communicates over Server-Sent Events.", + "sse": "Server communicates over Server-Sent Events (deprecated).", "memory": "Server is backed by an in-memory runtime implementation." } }, @@ -6531,6 +6742,13 @@ "description": { "type": "string", "description": "Human-readable description of the binary data" + }, + "metadata": { + "type": "object", + "additionalProperties": { + "x-opaque-json": true + }, + "description": "Optional metadata from the producing tool." } }, "required": [ @@ -7506,6 +7724,480 @@ "description": "Parameters for (re)loading the merged LSP configuration set.", "title": "LspInitializeRequest" }, + "McpAppsCallToolRequest": { + "type": "object", + "properties": { + "serverName": { + "type": "string", + "minLength": 1, + "pattern": "^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$", + "description": "MCP server hosting the tool" + }, + "toolName": { + "type": "string", + "description": "MCP tool name" + }, + "arguments": { + "type": "object", + "additionalProperties": { + "x-opaque-json": true + }, + "description": "Tool arguments" + }, + "originServerName": { + "type": "string", + "minLength": 1, + "pattern": "^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$", + "description": "**Required.** Server whose ui:// view issued the request. Per SEP-1865 ('callable by the app from this server only'), the call is rejected when this differs from `serverName`, and rejected outright when missing." + } + }, + "required": [ + "serverName", + "toolName", + "originServerName" + ], + "additionalProperties": false, + "description": "MCP server, tool name, and arguments to invoke from an MCP App view.", + "title": "McpAppsCallToolRequest" + }, + "McpAppsDiagnoseCapability": { + "type": "object", + "properties": { + "sessionHasMcpApps": { + "type": "boolean", + "description": "Whether the session has the `mcp-apps` capability" + }, + "featureFlagEnabled": { + "type": "boolean", + "description": "Whether the MCP_APPS feature flag (or COPILOT_MCP_APPS env override) is on" + }, + "advertised": { + "type": "boolean", + "description": "Whether the runtime advertises `extensions.io.modelcontextprotocol/ui` to MCP servers" + } + }, + "required": [ + "sessionHasMcpApps", + "featureFlagEnabled", + "advertised" + ], + "additionalProperties": false, + "description": "Capability negotiation snapshot", + "title": "McpAppsDiagnoseCapability" + }, + "McpAppsDiagnoseRequest": { + "type": "object", + "properties": { + "serverName": { + "type": "string", + "minLength": 1, + "pattern": "^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$", + "description": "MCP server to probe" + } + }, + "required": [ + "serverName" + ], + "additionalProperties": false, + "description": "MCP server to diagnose MCP Apps wiring for.", + "title": "McpAppsDiagnoseRequest" + }, + "McpAppsDiagnoseResult": { + "type": "object", + "properties": { + "capability": { + "$ref": "#/definitions/McpAppsDiagnoseCapability", + "description": "Capability negotiation snapshot" + }, + "server": { + "$ref": "#/definitions/McpAppsDiagnoseServer", + "description": "What the server returned for this session" + } + }, + "required": [ + "capability", + "server" + ], + "additionalProperties": false, + "description": "Diagnostic snapshot of MCP Apps wiring for the named server.", + "title": "McpAppsDiagnoseResult" + }, + "McpAppsDiagnoseServer": { + "type": "object", + "properties": { + "connected": { + "type": "boolean", + "description": "Whether the named server is currently connected" + }, + "toolCount": { + "type": "number", + "description": "Total tools returned by the server's tools/list" + }, + "toolsWithUiMeta": { + "type": "number", + "description": "Tools whose `_meta.ui` is populated (resourceUri and/or visibility set)" + }, + "sampleToolNames": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Up to 5 tool names with `_meta.ui` for quick inspection" + } + }, + "required": [ + "connected", + "toolCount", + "toolsWithUiMeta", + "sampleToolNames" + ], + "additionalProperties": false, + "description": "What the server returned for this session", + "title": "McpAppsDiagnoseServer" + }, + "McpAppsHostContext": { + "type": "object", + "properties": { + "context": { + "$ref": "#/definitions/McpAppsHostContextDetails", + "description": "Current host context" + } + }, + "required": [ + "context" + ], + "additionalProperties": false, + "description": "Current host context advertised to MCP App guests.", + "title": "McpAppsHostContext" + }, + "McpAppsHostContextDetails": { + "type": "object", + "properties": { + "theme": { + "$ref": "#/definitions/McpAppsHostContextDetailsTheme", + "description": "UI theme preference per SEP-1865" + }, + "locale": { + "type": "string", + "description": "BCP-47 locale, e.g. 'en-US'" + }, + "timeZone": { + "type": "string", + "description": "IANA timezone, e.g. 'America/New_York'" + }, + "displayMode": { + "$ref": "#/definitions/McpAppsHostContextDetailsDisplayMode", + "description": "Current display mode (SEP-1865)" + }, + "availableDisplayModes": { + "type": "array", + "items": { + "$ref": "#/definitions/McpAppsHostContextDetailsAvailableDisplayMode" + }, + "description": "Display modes the host supports" + }, + "platform": { + "$ref": "#/definitions/McpAppsHostContextDetailsPlatform", + "description": "Platform type for responsive design" + }, + "userAgent": { + "type": "string", + "description": "Host application identifier" + } + }, + "additionalProperties": {}, + "description": "Current host context", + "title": "McpAppsHostContextDetails" + }, + "McpAppsHostContextDetailsAvailableDisplayMode": { + "type": "string", + "enum": [ + "inline", + "fullscreen", + "pip" + ], + "title": "McpAppsHostContextDetailsAvailableDisplayMode", + "x-enumDescriptions": { + "inline": "Rendered inline within the host conversation surface", + "fullscreen": "Rendered as a fullscreen overlay", + "pip": "Rendered as a picture-in-picture floating panel" + }, + "description": "Allowed values for the `McpAppsHostContextDetailsAvailableDisplayMode` enumeration." + }, + "McpAppsHostContextDetailsDisplayMode": { + "type": "string", + "enum": [ + "inline", + "fullscreen", + "pip" + ], + "description": "Current display mode (SEP-1865)", + "title": "McpAppsHostContextDetailsDisplayMode", + "x-enumDescriptions": { + "inline": "Rendered inline within the host conversation surface", + "fullscreen": "Rendered as a fullscreen overlay", + "pip": "Rendered as a picture-in-picture floating panel" + } + }, + "McpAppsHostContextDetailsPlatform": { + "type": "string", + "enum": [ + "web", + "desktop", + "mobile" + ], + "description": "Platform type for responsive design", + "title": "McpAppsHostContextDetailsPlatform", + "x-enumDescriptions": { + "web": "Host runs in a web browser", + "desktop": "Host runs as a desktop application", + "mobile": "Host runs on a mobile device" + } + }, + "McpAppsHostContextDetailsTheme": { + "type": "string", + "enum": [ + "light", + "dark" + ], + "description": "UI theme preference per SEP-1865", + "title": "McpAppsHostContextDetailsTheme", + "x-enumDescriptions": { + "light": "Light UI theme", + "dark": "Dark UI theme" + } + }, + "McpAppsListToolsRequest": { + "type": "object", + "properties": { + "serverName": { + "type": "string", + "minLength": 1, + "pattern": "^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$", + "description": "MCP server hosting the app" + }, + "originServerName": { + "type": "string", + "minLength": 1, + "pattern": "^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$", + "description": "**Required.** Server whose ui:// view issued the request. Per SEP-1865 ('callable by the app from this server only'), the call is rejected when this differs from `serverName`, and rejected outright when missing." + } + }, + "required": [ + "serverName", + "originServerName" + ], + "additionalProperties": false, + "description": "MCP server to list app-callable tools for.", + "title": "McpAppsListToolsRequest" + }, + "McpAppsListToolsResult": { + "type": "object", + "properties": { + "tools": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": { + "x-opaque-json": true + } + }, + "description": "App-callable tools from the server" + } + }, + "required": [ + "tools" + ], + "additionalProperties": false, + "description": "App-callable tools from the named MCP server.", + "title": "McpAppsListToolsResult" + }, + "McpAppsReadResourceRequest": { + "type": "object", + "properties": { + "serverName": { + "type": "string", + "minLength": 1, + "pattern": "^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$", + "description": "Name of the MCP server hosting the resource" + }, + "uri": { + "type": "string", + "description": "Resource URI (typically ui://...)" + } + }, + "required": [ + "serverName", + "uri" + ], + "additionalProperties": false, + "description": "MCP server and resource URI to fetch.", + "title": "McpAppsReadResourceRequest" + }, + "McpAppsReadResourceResult": { + "type": "object", + "properties": { + "contents": { + "type": "array", + "items": { + "$ref": "#/definitions/McpAppsResourceContent" + }, + "description": "Resource contents returned by the server" + } + }, + "required": [ + "contents" + ], + "additionalProperties": false, + "description": "Resource contents returned by the MCP server.", + "title": "McpAppsReadResourceResult" + }, + "McpAppsResourceContent": { + "type": "object", + "properties": { + "uri": { + "type": "string", + "description": "The resource URI (typically ui://...)" + }, + "mimeType": { + "type": "string", + "description": "MIME type of the content" + }, + "text": { + "type": "string", + "description": "Text content (e.g. HTML)" + }, + "blob": { + "type": "string", + "description": "Base64-encoded binary content" + }, + "_meta": { + "type": "object", + "additionalProperties": { + "x-opaque-json": true + }, + "description": "Resource-level metadata (CSP, permissions, etc.)" + } + }, + "required": [ + "uri" + ], + "additionalProperties": false, + "title": "McpAppsResourceContent", + "description": "Schema for the `McpAppsResourceContent` type." + }, + "McpAppsSetHostContextDetails": { + "type": "object", + "properties": { + "theme": { + "$ref": "#/definitions/McpAppsSetHostContextDetailsTheme", + "description": "UI theme preference per SEP-1865" + }, + "locale": { + "type": "string", + "description": "BCP-47 locale, e.g. 'en-US'" + }, + "timeZone": { + "type": "string", + "description": "IANA timezone, e.g. 'America/New_York'" + }, + "displayMode": { + "$ref": "#/definitions/McpAppsSetHostContextDetailsDisplayMode", + "description": "Current display mode (SEP-1865)" + }, + "availableDisplayModes": { + "type": "array", + "items": { + "$ref": "#/definitions/McpAppsSetHostContextDetailsAvailableDisplayMode" + }, + "description": "Display modes the host supports" + }, + "platform": { + "$ref": "#/definitions/McpAppsSetHostContextDetailsPlatform", + "description": "Platform type for responsive design" + }, + "userAgent": { + "type": "string", + "description": "Host application identifier" + } + }, + "additionalProperties": {}, + "description": "Host context advertised to MCP App guests", + "title": "McpAppsSetHostContextDetails" + }, + "McpAppsSetHostContextDetailsAvailableDisplayMode": { + "type": "string", + "enum": [ + "inline", + "fullscreen", + "pip" + ], + "title": "McpAppsSetHostContextDetailsAvailableDisplayMode", + "x-enumDescriptions": { + "inline": "Rendered inline within the host conversation surface", + "fullscreen": "Rendered as a fullscreen overlay", + "pip": "Rendered as a picture-in-picture floating panel" + }, + "description": "Allowed values for the `McpAppsSetHostContextDetailsAvailableDisplayMode` enumeration." + }, + "McpAppsSetHostContextDetailsDisplayMode": { + "type": "string", + "enum": [ + "inline", + "fullscreen", + "pip" + ], + "description": "Current display mode (SEP-1865)", + "title": "McpAppsSetHostContextDetailsDisplayMode", + "x-enumDescriptions": { + "inline": "Rendered inline within the host conversation surface", + "fullscreen": "Rendered as a fullscreen overlay", + "pip": "Rendered as a picture-in-picture floating panel" + } + }, + "McpAppsSetHostContextDetailsPlatform": { + "type": "string", + "enum": [ + "web", + "desktop", + "mobile" + ], + "description": "Platform type for responsive design", + "title": "McpAppsSetHostContextDetailsPlatform", + "x-enumDescriptions": { + "web": "Host runs in a web browser", + "desktop": "Host runs as a desktop application", + "mobile": "Host runs on a mobile device" + } + }, + "McpAppsSetHostContextDetailsTheme": { + "type": "string", + "enum": [ + "light", + "dark" + ], + "description": "UI theme preference per SEP-1865", + "title": "McpAppsSetHostContextDetailsTheme", + "x-enumDescriptions": { + "light": "Light UI theme", + "dark": "Dark UI theme" + } + }, + "McpAppsSetHostContextRequest": { + "type": "object", + "properties": { + "context": { + "$ref": "#/definitions/McpAppsSetHostContextDetails", + "description": "Host context advertised to MCP App guests" + } + }, + "required": [ + "context" + ], + "additionalProperties": false, + "description": "Host context to advertise to MCP App guests.", + "title": "McpAppsSetHostContextRequest" + }, "McpCancelSamplingExecutionParams": { "type": "object", "properties": { @@ -8483,31 +9175,66 @@ "type": "object", "properties": { "inputPrice": { - "type": "integer", + "type": "number", "minimum": 0, - "description": "Price per billing batch of input tokens in nano-AIUs (1 nano-AIU = 0.000000001 AIU, 1 AIU = $0.01 USD)" + "description": "AI Credits cost per billing batch of input tokens" }, "outputPrice": { - "type": "integer", + "type": "number", "minimum": 0, - "description": "Price per billing batch of output tokens in nano-AIUs (1 nano-AIU = 0.000000001 AIU, 1 AIU = $0.01 USD)" + "description": "AI Credits cost per billing batch of output tokens" }, "cachePrice": { - "type": "integer", + "type": "number", "minimum": 0, - "description": "Price per billing batch of cached tokens in nano-AIUs (1 nano-AIU = 0.000000001 AIU, 1 AIU = $0.01 USD)" + "description": "AI Credits cost per billing batch of cached tokens" }, "batchSize": { "type": "integer", "exclusiveMinimum": true, "minimum": 0, "description": "Number of tokens per standard billing batch" + }, + "contextMax": { + "type": "integer", + "description": "Maximum context window tokens for the default tier" + }, + "longContext": { + "$ref": "#/definitions/ModelBillingTokenPricesLongContext", + "description": "Long context tier pricing (available for models with extended context windows)" } }, "additionalProperties": false, "description": "Token-level pricing information for this model", "title": "ModelBillingTokenPrices" }, + "ModelBillingTokenPricesLongContext": { + "type": "object", + "properties": { + "inputPrice": { + "type": "number", + "minimum": 0, + "description": "AI Credits cost per billing batch of input tokens" + }, + "outputPrice": { + "type": "number", + "minimum": 0, + "description": "AI Credits cost per billing batch of output tokens" + }, + "cachePrice": { + "type": "number", + "minimum": 0, + "description": "AI Credits cost per billing batch of cached tokens" + }, + "contextMax": { + "type": "integer", + "description": "Maximum context window tokens for the long context tier" + } + }, + "additionalProperties": false, + "description": "Long context tier pricing (available for models with extended context windows)", + "title": "ModelBillingTokenPricesLongContext" + }, "ModelCapabilities": { "type": "object", "properties": { diff --git a/schemas/session-events.schema.json b/schemas/session-events.schema.json index 160fb55..88fb105 100644 --- a/schemas/session-events.schema.json +++ b/schemas/session-events.schema.json @@ -208,6 +208,10 @@ "type": "string", "description": "GitHub request tracing ID (x-github-request-id header) for correlating with server-side logs" }, + "serviceRequestId": { + "type": "string", + "description": "Copilot service request ID (x-copilot-service-request-id header) for CAPI log correlation" + }, "anthropicAdvisorBlocks": { "type": "array", "items": { @@ -1016,6 +1020,10 @@ "type": "string", "description": "GitHub request tracing ID (x-github-request-id header) for server-side log correlation" }, + "serviceRequestId": { + "type": "string", + "description": "Copilot service request ID (x-copilot-service-request-id header) for CAPI log correlation" + }, "apiEndpoint": { "$ref": "#/definitions/AssistantUsageApiEndpoint", "description": "API endpoint used for this model call, matching CAPI supported_endpoints vocabulary" @@ -1478,6 +1486,10 @@ "elicitation": { "type": "boolean", "description": "Whether elicitation is now supported" + }, + "mcpApps": { + "type": "boolean", + "description": "Whether MCP Apps (SEP-1865) UI passthrough is now supported" } }, "additionalProperties": false, @@ -1972,6 +1984,10 @@ "type": "string", "description": "GitHub request tracing ID (x-github-request-id header) for the compaction LLM call" }, + "serviceRequestId": { + "type": "string", + "description": "Copilot service request ID (x-copilot-service-request-id header) for the compaction LLM call" + }, "systemTokens": { "type": "integer", "minimum": 0, @@ -2797,6 +2813,10 @@ "type": "string", "description": "GitHub request tracing ID (x-github-request-id header) for correlating with server-side logs" }, + "serviceRequestId": { + "type": "string", + "description": "Copilot service request ID (x-copilot-service-request-id header) for CAPI log correlation" + }, "url": { "type": "string", "description": "Optional URL associated with this error that the user can open in a browser" @@ -3829,6 +3849,160 @@ "description": "Session event \"session.info\". Informational message for timeline display with categorization", "title": "InfoEvent" }, + "McpAppToolCallCompleteData": { + "type": "object", + "properties": { + "serverName": { + "type": "string", + "description": "Name of the MCP server hosting the tool" + }, + "toolName": { + "type": "string", + "description": "MCP tool name that was invoked" + }, + "arguments": { + "type": "object", + "additionalProperties": { + "x-opaque-json": true + }, + "description": "Arguments passed to the tool by the app view, if any" + }, + "success": { + "type": "boolean", + "description": "True when the call completed without throwing AND the MCP CallToolResult did not set isError" + }, + "durationMs": { + "type": "number", + "description": "Wall-clock duration of the underlying tools/call in milliseconds" + }, + "result": { + "type": "object", + "additionalProperties": { + "x-opaque-json": true + }, + "description": "Standard MCP CallToolResult returned by the server. Present whether or not the call set isError." + }, + "error": { + "$ref": "#/definitions/McpAppToolCallCompleteError", + "description": "Set when the underlying tools/call threw an error before returning a CallToolResult" + }, + "toolMeta": { + "$ref": "#/definitions/McpAppToolCallCompleteToolMeta", + "description": "The tool's `_meta.ui` block at the time of the call, so consumers can decide whether to forward the result to the model without re-listing tools." + } + }, + "required": [ + "serverName", + "toolName", + "success", + "durationMs" + ], + "additionalProperties": false, + "description": "MCP App view called a tool on a connected MCP server (SEP-1865)", + "title": "McpAppToolCallCompleteData" + }, + "McpAppToolCallCompleteError": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Human-readable error message" + } + }, + "required": [ + "message" + ], + "additionalProperties": false, + "description": "Set when the underlying tools/call threw an error before returning a CallToolResult", + "title": "McpAppToolCallCompleteError" + }, + "McpAppToolCallCompleteEvent": { + "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": "mcp_app.tool_call_complete", + "description": "Type discriminator. Always \"mcp_app.tool_call_complete\"." + }, + "data": { + "$ref": "#/definitions/McpAppToolCallCompleteData", + "description": "MCP App view called a tool on a connected MCP server (SEP-1865)" + } + }, + "required": [ + "id", + "timestamp", + "parentId", + "ephemeral", + "type", + "data" + ], + "additionalProperties": false, + "description": "Session event \"mcp_app.tool_call_complete\". MCP App view called a tool on a connected MCP server (SEP-1865)", + "title": "McpAppToolCallCompleteEvent" + }, + "McpAppToolCallCompleteToolMeta": { + "type": "object", + "properties": { + "ui": { + "$ref": "#/definitions/McpAppToolCallCompleteToolMetaUI", + "description": "Schema for the `McpAppToolCallCompleteToolMetaUI` type." + } + }, + "additionalProperties": true, + "description": "The tool's `_meta.ui` block at the time of the call, so consumers can decide whether to forward the result to the model without re-listing tools.", + "title": "McpAppToolCallCompleteToolMeta" + }, + "McpAppToolCallCompleteToolMetaUI": { + "type": "object", + "properties": { + "resourceUri": { + "type": "string", + "description": "`ui://` URI declared by the tool's `_meta.ui.resourceUri`" + }, + "visibility": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Tool visibility per SEP-1865 (typically a subset of `[\"model\",\"app\"]`)" + } + }, + "additionalProperties": true, + "title": "McpAppToolCallCompleteToolMetaUI", + "description": "Schema for the `McpAppToolCallCompleteToolMetaUI` type." + }, "McpOauthCompletedData": { "type": "object", "properties": { @@ -4101,6 +4275,18 @@ "error": { "type": "string", "description": "Error message if the server failed to connect" + }, + "transport": { + "$ref": "#/definitions/McpServerTransport", + "description": "Transport mechanism: stdio, http, sse (deprecated), or memory (in-process MCP server)" + }, + "pluginName": { + "type": "string", + "description": "Name of the plugin that supplied the effective MCP server config, only when source is plugin" + }, + "pluginVersion": { + "type": "string", + "description": "Version of the plugin that supplied the effective MCP server config, only when source is plugin" } }, "required": [ @@ -4159,6 +4345,10 @@ "status": { "$ref": "#/definitions/McpServerStatus", "description": "Connection status: connected, failed, needs-auth, pending, disabled, or not_configured" + }, + "error": { + "type": "string", + "description": "Error message if the server entered a failed state" } }, "required": [ @@ -4225,6 +4415,23 @@ "description": "Session event \"session.mcp_server_status_changed\".", "title": "McpServerStatusChangedEvent" }, + "McpServerTransport": { + "type": "string", + "enum": [ + "stdio", + "http", + "sse", + "memory" + ], + "description": "Transport mechanism: stdio, http, sse (deprecated), or memory (in-process MCP server)", + "title": "McpServerTransport", + "x-enumDescriptions": { + "stdio": "Server communicates over stdio with a local child process.", + "http": "Server communicates over streamable HTTP.", + "sse": "Server communicates over Server-Sent Events (deprecated).", + "memory": "Server is backed by an in-memory runtime implementation." + } + }, "ModeChangedData": { "type": "object", "properties": { @@ -4318,6 +4525,10 @@ "type": "string", "description": "GitHub request tracing ID (x-github-request-id header) for server-side log correlation" }, + "serviceRequestId": { + "type": "string", + "description": "Copilot service request ID (x-copilot-service-request-id header) for CAPI log correlation" + }, "statusCode": { "type": "integer", "minimum": 0, @@ -4447,6 +4658,25 @@ "$ref": "#/definitions/ReasoningSummary", "description": "Reasoning summary mode after the model change, if applicable" }, + "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." + } + }, + { + "type": "null" + } + ], + "description": "Context tier after the model change; null explicitly clears a previously selected tier" + }, "cause": { "type": "string", "description": "Reason the change happened, when not user-initiated. Currently `\"rate_limit_auto_switch\"` for changes triggered by the auto-mode-switch rate-limit recovery path. UI clients can use this to render contextual copy." @@ -6872,6 +7102,10 @@ { "$ref": "#/definitions/ExtensionsLoadedEvent", "description": "Session event \"session.extensions_loaded\"." + }, + { + "$ref": "#/definitions/McpAppToolCallCompleteEvent", + "description": "Session event \"mcp_app.tool_call_complete\". MCP App view called a tool on a connected MCP server (SEP-1865)" } ], "description": "Union of all session event variants emitted by the Copilot CLI runtime." @@ -7221,6 +7455,10 @@ }, "description": "Tool names that should be auto-approved when this skill is active" }, + "source": { + "type": "string", + "description": "Source identifier for where the skill was discovered. Known values include: project (workspace skill), inherited (parent-directory skill), personal-copilot (~/.copilot/skills), personal-agents (~/.agents/skills), personal-claude (~/.claude/skills), custom (configured directory), plugin (installed plugin), builtin (bundled runtime skill), and remote (org/enterprise skill)" + }, "pluginName": { "type": "string", "description": "Name of the plugin this skill originated from, when applicable" @@ -7232,6 +7470,10 @@ "description": { "type": "string", "description": "Description of the skill from its SKILL.md frontmatter" + }, + "trigger": { + "$ref": "#/definitions/SkillInvokedTrigger", + "description": "What triggered the skill invocation: `user-invoked` (explicit user action, such as via a slash command or UI affordance), `agent-invoked` (agent requested the skill), or `context-load` (loaded as part of another context, such as preloading skills configured on a custom agent or subagent)" } }, "required": [ @@ -7297,6 +7539,21 @@ "description": "Session event \"skill.invoked\". Skill invocation details including content, allowed tools, and plugin metadata", "title": "SkillInvokedEvent" }, + "SkillInvokedTrigger": { + "type": "string", + "enum": [ + "user-invoked", + "agent-invoked", + "context-load" + ], + "description": "What triggered the skill invocation: `user-invoked` (explicit user action, such as via a slash command or UI affordance), `agent-invoked` (agent requested the skill), or `context-load` (loaded as part of another context, such as preloading skills configured on a custom agent or subagent)", + "title": "SkillInvokedTrigger", + "x-enumDescriptions": { + "user-invoked": "Skill invocation requested explicitly by the user, such as via a slash command or UI affordance.", + "agent-invoked": "Skill invocation requested by the agent.", + "context-load": "Skill content loaded as part of another context, such as a configured custom agent or subagent." + } + }, "SkillsLoadedData": { "type": "object", "properties": { @@ -8934,6 +9191,10 @@ "type": "string", "description": "Identifier for the agent loop turn this tool was invoked in, matching the corresponding assistant.turn_start event" }, + "toolDescription": { + "$ref": "#/definitions/ToolExecutionCompleteToolDescription", + "description": "Tool definition metadata, present for MCP tools with MCP Apps support" + }, "sandboxed": { "type": "boolean", "description": "Whether this tool execution ran inside a sandbox container" @@ -9043,6 +9304,10 @@ "description": "A content block within a tool result, which may be text, terminal output, image, audio, or a resource" }, "description": "Structured content blocks (text, images, audio, resources) returned by the tool in their native format" + }, + "uiResource": { + "$ref": "#/definitions/ToolExecutionCompleteUIResource", + "description": "MCP Apps UI resource content for rendering in a sandboxed iframe" } }, "required": [ @@ -9052,6 +9317,223 @@ "description": "Tool execution result on success", "title": "ToolExecutionCompleteResult" }, + "ToolExecutionCompleteToolDescription": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Tool name" + }, + "description": { + "type": "string", + "description": "Tool description" + }, + "_meta": { + "$ref": "#/definitions/ToolExecutionCompleteToolDescriptionMeta", + "description": "MCP Apps metadata for UI resource association" + } + }, + "required": [ + "name" + ], + "additionalProperties": false, + "description": "Tool definition metadata, present for MCP tools with MCP Apps support", + "title": "ToolExecutionCompleteToolDescription" + }, + "ToolExecutionCompleteToolDescriptionMeta": { + "type": "object", + "properties": { + "ui": { + "$ref": "#/definitions/ToolExecutionCompleteToolDescriptionMetaUI", + "description": "Schema for the `ToolExecutionCompleteToolDescriptionMetaUI` type." + } + }, + "additionalProperties": false, + "description": "MCP Apps metadata for UI resource association", + "title": "ToolExecutionCompleteToolDescriptionMeta" + }, + "ToolExecutionCompleteToolDescriptionMetaUI": { + "type": "object", + "properties": { + "resourceUri": { + "type": "string", + "description": "URI of the UI resource" + }, + "visibility": { + "type": "array", + "items": { + "$ref": "#/definitions/ToolExecutionCompleteToolDescriptionMetaUIVisibility" + }, + "description": "Who can access this tool" + } + }, + "additionalProperties": false, + "title": "ToolExecutionCompleteToolDescriptionMetaUI", + "description": "Schema for the `ToolExecutionCompleteToolDescriptionMetaUI` type." + }, + "ToolExecutionCompleteToolDescriptionMetaUIVisibility": { + "type": "string", + "enum": [ + "model", + "app" + ], + "title": "ToolExecutionCompleteToolDescriptionMetaUIVisibility", + "x-enumDescriptions": { + "model": "Tool is callable by the model (LLM tool surface)", + "app": "Tool is callable by the MCP App view (iframe) via session.mcp.apps.callTool" + }, + "description": "Allowed values for the `ToolExecutionCompleteToolDescriptionMetaUIVisibility` enumeration." + }, + "ToolExecutionCompleteUIResource": { + "type": "object", + "properties": { + "uri": { + "type": "string", + "description": "The ui:// URI of the resource" + }, + "mimeType": { + "type": "string", + "description": "MIME type of the content" + }, + "text": { + "type": "string", + "description": "HTML content as a string" + }, + "blob": { + "type": "string", + "description": "Base64-encoded HTML content" + }, + "_meta": { + "$ref": "#/definitions/ToolExecutionCompleteUIResourceMeta", + "description": "Resource-level UI metadata (CSP, permissions, visual preferences)" + } + }, + "required": [ + "uri", + "mimeType" + ], + "additionalProperties": false, + "description": "MCP Apps UI resource content for rendering in a sandboxed iframe", + "title": "ToolExecutionCompleteUIResource" + }, + "ToolExecutionCompleteUIResourceMeta": { + "type": "object", + "properties": { + "ui": { + "$ref": "#/definitions/ToolExecutionCompleteUIResourceMetaUI", + "description": "Schema for the `ToolExecutionCompleteUIResourceMetaUI` type." + } + }, + "additionalProperties": false, + "description": "Resource-level UI metadata (CSP, permissions, visual preferences)", + "title": "ToolExecutionCompleteUIResourceMeta" + }, + "ToolExecutionCompleteUIResourceMetaUI": { + "type": "object", + "properties": { + "csp": { + "$ref": "#/definitions/ToolExecutionCompleteUIResourceMetaUICsp", + "description": "Schema for the `ToolExecutionCompleteUIResourceMetaUICsp` type." + }, + "permissions": { + "$ref": "#/definitions/ToolExecutionCompleteUIResourceMetaUIPermissions", + "description": "Schema for the `ToolExecutionCompleteUIResourceMetaUIPermissions` type." + }, + "domain": { + "type": "string" + }, + "prefersBorder": { + "type": "boolean" + } + }, + "additionalProperties": false, + "title": "ToolExecutionCompleteUIResourceMetaUI", + "description": "Schema for the `ToolExecutionCompleteUIResourceMetaUI` type." + }, + "ToolExecutionCompleteUIResourceMetaUICsp": { + "type": "object", + "properties": { + "connectDomains": { + "type": "array", + "items": { + "type": "string" + } + }, + "resourceDomains": { + "type": "array", + "items": { + "type": "string" + } + }, + "frameDomains": { + "type": "array", + "items": { + "type": "string" + } + }, + "baseUriDomains": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false, + "title": "ToolExecutionCompleteUIResourceMetaUICsp", + "description": "Schema for the `ToolExecutionCompleteUIResourceMetaUICsp` type." + }, + "ToolExecutionCompleteUIResourceMetaUIPermissions": { + "type": "object", + "properties": { + "camera": { + "$ref": "#/definitions/ToolExecutionCompleteUIResourceMetaUIPermissionsCamera", + "description": "Schema for the `ToolExecutionCompleteUIResourceMetaUIPermissionsCamera` type." + }, + "microphone": { + "$ref": "#/definitions/ToolExecutionCompleteUIResourceMetaUIPermissionsMicrophone", + "description": "Schema for the `ToolExecutionCompleteUIResourceMetaUIPermissionsMicrophone` type." + }, + "geolocation": { + "$ref": "#/definitions/ToolExecutionCompleteUIResourceMetaUIPermissionsGeolocation", + "description": "Schema for the `ToolExecutionCompleteUIResourceMetaUIPermissionsGeolocation` type." + }, + "clipboardWrite": { + "$ref": "#/definitions/ToolExecutionCompleteUIResourceMetaUIPermissionsClipboardWrite", + "description": "Schema for the `ToolExecutionCompleteUIResourceMetaUIPermissionsClipboardWrite` type." + } + }, + "additionalProperties": false, + "title": "ToolExecutionCompleteUIResourceMetaUIPermissions", + "description": "Schema for the `ToolExecutionCompleteUIResourceMetaUIPermissions` type." + }, + "ToolExecutionCompleteUIResourceMetaUIPermissionsCamera": { + "type": "object", + "properties": {}, + "additionalProperties": true, + "title": "ToolExecutionCompleteUIResourceMetaUIPermissionsCamera", + "description": "Schema for the `ToolExecutionCompleteUIResourceMetaUIPermissionsCamera` type." + }, + "ToolExecutionCompleteUIResourceMetaUIPermissionsClipboardWrite": { + "type": "object", + "properties": {}, + "additionalProperties": true, + "title": "ToolExecutionCompleteUIResourceMetaUIPermissionsClipboardWrite", + "description": "Schema for the `ToolExecutionCompleteUIResourceMetaUIPermissionsClipboardWrite` type." + }, + "ToolExecutionCompleteUIResourceMetaUIPermissionsGeolocation": { + "type": "object", + "properties": {}, + "additionalProperties": true, + "title": "ToolExecutionCompleteUIResourceMetaUIPermissionsGeolocation", + "description": "Schema for the `ToolExecutionCompleteUIResourceMetaUIPermissionsGeolocation` type." + }, + "ToolExecutionCompleteUIResourceMetaUIPermissionsMicrophone": { + "type": "object", + "properties": {}, + "additionalProperties": true, + "title": "ToolExecutionCompleteUIResourceMetaUIPermissionsMicrophone", + "description": "Schema for the `ToolExecutionCompleteUIResourceMetaUIPermissionsMicrophone` type." + }, "ToolExecutionPartialData": { "type": "object", "properties": { diff --git a/src/github/copilot_sdk.clj b/src/github/copilot_sdk.clj index ae64749..990241e 100644 --- a/src/github/copilot_sdk.clj +++ b/src/github/copilot_sdk.clj @@ -120,7 +120,9 @@ :copilot/sampling.requested :copilot/sampling.completed :copilot/session.remote_steerable_changed - :copilot/capabilities.changed}) + :copilot/capabilities.changed + ;; MCP Apps tool-call complete (upstream schema 1.0.52-4, SEP-1865) + :copilot/mcp_app.tool_call_complete}) (def session-events "Session lifecycle and state management events." diff --git a/src/github/copilot_sdk/client.clj b/src/github/copilot_sdk/client.clj index f3eb3be..29e6a1f 100644 --- a/src/github/copilot_sdk/client.clj +++ b/src/github/copilot_sdk/client.clj @@ -15,7 +15,10 @@ [java.util.concurrent LinkedBlockingQueue])) (def ^:private sdk-protocol-version-max 3) -(def ^:private sdk-protocol-version-min 2) +;; Upstream PR #1378 raised the minimum protocol version to 3 by removing +;; the v2 back-compat shims (the `tool.call` and `permission.request` +;; server→client RPCs). Servers reporting a version below 3 are now rejected. +(def ^:private sdk-protocol-version-min 3) (defn- get-trace-context "Call the user-provided trace context provider. Returns {} when no provider @@ -632,34 +635,20 @@ (str/join "\n" lines))))) (defn- setup-request-handler! - "Set up handler for incoming requests (tool calls, permission requests, hooks, user input, sessionFs)." + "Set up handler for incoming requests (hooks, user input, sessionFs, etc.). + + Protocol v3 (the only supported version, see [[sdk-protocol-version-min]]) + delivers tool calls and permission requests as broadcast events on + `session.event` — see [[handle-v3-tool-requested!]] and + [[handle-v3-permission-requested!]] — not as server→client RPCs. The v2 + `tool.call` / `permission.request` dispatcher cases were removed + alongside upstream PR #1378." [client] (let [{:keys [connection-io]} @(:state client)] (proto/set-request-handler! connection-io (fn [method params] (go (case method - "tool.call" - (let [{:keys [session-id tool-call-id tool-name arguments]} params] - (if-not (get-in @(:state client) [:sessions session-id]) - {:error {:code -32001 :message (str "Unknown session: " session-id)}} - {:result ( converted-event + (contains? (:data raw-event) :arguments) + (assoc-in [:data :arguments] (get-in raw-event [:data :arguments])) + (contains? (:data raw-event) :result) + (assoc-in [:data :result] (get-in raw-event [:data :result]))) + converted-event)) (defn- normalize-incoming "Convert wire-format keys to Clojure keys, preserving opaque user data. - For v2 tool.call RPC and v3 external_tool.requested broadcast events, - tool arguments are kept in their original wire format so user-defined - tool handlers receive the keys the server sent. For v3 - session.custom_notification events, the source-defined `:subject` and - opaque `:payload` are also preserved verbatim. The same preservation - applies to historical events returned in `session.getMessages` responses - so live and historical event shapes agree." + + For v3 `external_tool.requested` broadcast events, tool arguments are + kept in their original wire format so user-defined tool handlers receive + the keys the server sent. For v3 `session.custom_notification` events, + the source-defined `:subject` and opaque `:payload` are also preserved + verbatim. For v3 `mcp_app.tool_call_complete` events (schema 1.0.52-4, + SEP-1865), the `:arguments` and `:result` payloads are similarly + preserved. The same preservation applies to historical events returned + in `session.getMessages` responses so live and historical event shapes + agree." [msg] (let [method (:method msg) params (:params msg) converted (util/wire->clj msg) raw-events (get-in msg [:result :events])] (cond - ;; v2: preserve raw arguments for tool.call RPC - (and (= "tool.call" method) (map? params) (contains? params :arguments)) - (assoc-in converted [:params :arguments] (:arguments params)) - ;; Upstream PR #1299: SQL bind parameters are opaque keyed values ;; (e.g. `$user_id`). Preserve the raw map so kebab-case conversion ;; doesn't mangle placeholder names before the handler binds them. diff --git a/src/github/copilot_sdk/specs.clj b/src/github/copilot_sdk/specs.clj index f4c2a6e..4a2b70e 100644 --- a/src/github/copilot_sdk/specs.clj +++ b/src/github/copilot_sdk/specs.clj @@ -233,22 +233,34 @@ (s/def ::system-message-mode #{:append :replace :customize}) (s/def ::system-message-content string?) -;; System prompt sections for customize mode (upstream PR #816) +;; System message sections for customize mode (upstream PR #816) +;; Upstream PR #1377 renamed `SystemPromptSection` to `SystemMessageSection` +;; and added the `runtime_instructions` section. The Clojure SDK keeps the +;; historical `system-prompt-sections` var as the canonical name and exposes +;; `system-message-sections` as an alias to match the upstream naming. (def system-prompt-sections - "Known system prompt section identifiers for the customize mode. - Each section corresponds to a distinct part of the system prompt." - {:identity {:description "Agent identity preamble and mode statement"} - :tone {:description "Response style, conciseness rules, output formatting preferences"} - :tool-efficiency {:description "Tool usage patterns, parallel calling, batching guidelines"} - :environment-context {:description "CWD, OS, git root, directory listing, available tools"} - :code-change-rules {:description "Coding rules, linting/testing, ecosystem tools, style"} - :guidelines {:description "Tips, behavioral best practices, behavioral guidelines"} - :safety {:description "Environment limitations, prohibited actions, security policies"} - :tool-instructions {:description "Per-tool usage instructions"} - :custom-instructions {:description "Repository and organization custom instructions"} - :last-instructions {:description "End-of-prompt instructions: parallel tool calling, persistence, task completion"}}) + "Known system message section identifiers for the customize mode. + Each section corresponds to a distinct part of the system message. + Upstream alias: `SystemMessageSection`." + {:identity {:description "Agent identity preamble and mode statement"} + :tone {:description "Response style, conciseness rules, output formatting preferences"} + :tool-efficiency {:description "Tool usage patterns, parallel calling, batching guidelines"} + :environment-context {:description "CWD, OS, git root, directory listing, available tools"} + :code-change-rules {:description "Coding rules, linting/testing, ecosystem tools, style"} + :guidelines {:description "Tips, behavioral best practices, behavioral guidelines"} + :safety {:description "Environment limitations, prohibited actions, security policies"} + :tool-instructions {:description "Per-tool usage instructions"} + :custom-instructions {:description "Repository and organization custom instructions"} + :runtime-instructions {:description "Runtime-provided context and instructions (e.g. system notifications, memories, workspace context, mode-specific instructions, content-exclusion policy)"} + :last-instructions {:description "End-of-prompt instructions: parallel tool calling, persistence, task completion"}}) + +(def system-message-sections + "Alias for [[system-prompt-sections]] matching the upstream + `SYSTEM_MESSAGE_SECTIONS` name (upstream PR #1377)." + system-prompt-sections) (s/def ::system-prompt-section (set (keys system-prompt-sections))) +(s/def ::system-message-section ::system-prompt-section) ;; Section override: action can be a keyword for static overrides, or a fn for transforms (s/def ::section-action @@ -888,7 +900,9 @@ ;; Capabilities changed (upstream PR #908) :copilot/capabilities.changed ;; Schedule events (upstream schema 1.0.42) - :copilot/session.schedule_created :copilot/session.schedule_cancelled}) + :copilot/session.schedule_created :copilot/session.schedule_cancelled + ;; MCP Apps tool-call complete (upstream schema 1.0.52-4, SEP-1865) + :copilot/mcp_app.tool_call_complete}) ;; Session events (s/def ::already-in-use? boolean?) diff --git a/src/github/copilot_sdk/util.clj b/src/github/copilot_sdk/util.clj index e315c44..6e943c9 100644 --- a/src/github/copilot_sdk/util.clj +++ b/src/github/copilot_sdk/util.clj @@ -54,17 +54,18 @@ ;; ----------------------------------------------------------------------------- (def section-key->wire - "Map from Clojure keyword to wire string for system prompt sections." - {:identity "identity" - :tone "tone" - :tool-efficiency "tool_efficiency" - :environment-context "environment_context" - :code-change-rules "code_change_rules" - :guidelines "guidelines" - :safety "safety" - :tool-instructions "tool_instructions" - :custom-instructions "custom_instructions" - :last-instructions "last_instructions"}) + "Map from Clojure keyword to wire string for system message sections." + {:identity "identity" + :tone "tone" + :tool-efficiency "tool_efficiency" + :environment-context "environment_context" + :code-change-rules "code_change_rules" + :guidelines "guidelines" + :safety "safety" + :tool-instructions "tool_instructions" + :custom-instructions "custom_instructions" + :runtime-instructions "runtime_instructions" + :last-instructions "last_instructions"}) (def wire->section-key "Map from wire string to Clojure keyword for system prompt sections." diff --git a/test/github/copilot_sdk/integration_test.clj b/test/github/copilot_sdk/integration_test.clj index 7d9598f..860c20c 100644 --- a/test/github/copilot_sdk/integration_test.clj +++ b/test/github/copilot_sdk/integration_test.clj @@ -8,6 +8,7 @@ [github.copilot-sdk.client :as client] [github.copilot-sdk.protocol :as protocol] [github.copilot-sdk.session :as session] + [github.copilot-sdk.specs :as specs] [github.copilot-sdk.tools :as tools] [github.copilot-sdk.util :as util] [github.copilot-sdk.generated.event-specs] @@ -160,7 +161,7 @@ (deftest test-ping (testing "Ping returns protocol version" (let [result (sdk/ping *test-client*)] - (is (= 2 (:protocol-version result))) + (is (= 3 (:protocol-version result))) ;; Upstream PR #1340 / CLI 1.0.51 changed timestamp from epoch number ;; to ISO 8601 string (`timestamp: string, format: date-time`). (is (string? (:timestamp result))) @@ -181,7 +182,7 @@ (testing "Get CLI status returns version and protocol" (let [result (sdk/get-status *test-client*)] (is (string? (:version result))) - (is (= 2 (:protocol-version result)))))) + (is (= 3 (:protocol-version result)))))) (deftest test-get-auth-status (testing "Get auth status returns authentication info" @@ -685,40 +686,12 @@ :handler (fn [_args _invocation] "result")})]})] (is (some? session))))) -(deftest test-tool-call-response-shape - (testing "tool.call handler returns a nested result wrapper" - (let [tool (sdk/define-tool "echo" - {:handler (fn [args _] args)}) - session (sdk/create-session *test-client* {:on-permission-request sdk/approve-all :tools [tool]}) - handler (get-in @(:state *test-client*) [:connection :request-handler]) - response (clj on relevant event data specs" + ;; Upstream schema 1.0.52-4 adds optional `serviceRequestId` (the + ;; Copilot CAPI x-copilot-service-request-id header) to several + ;; event-data shapes for correlation with CAPI logs. The generated + ;; specs accept it via `:opt-un`; we verify roundtrip through + ;; normalize-incoming. + (doseq [spec-key [:github.copilot-sdk.generated.event-specs/assistant.message-data + :github.copilot-sdk.generated.event-specs/assistant.usage-data + :github.copilot-sdk.generated.event-specs/model.call_failure-data + :github.copilot-sdk.generated.event-specs/session.compaction_complete-data + :github.copilot-sdk.generated.event-specs/session.error-data]] + (is (some? (s/get-spec spec-key)) (str spec-key " should exist"))) + (let [normalize @#'protocol/normalize-incoming + raw-msg {:jsonrpc "2.0" + :method "session.event" + :params {:sessionId "abc" + :event {:type "assistant.usage" + :id "evt-2" + :timestamp "2026-05-23T08:00:00.000Z" + :parentId nil + :data {:model "gpt-5" + :serviceRequestId "svc-req-abc"}}}} + data (get-in (normalize raw-msg) [:params :event :data])] + (is (= "svc-req-abc" (:service-request-id data)) + "serviceRequestId must arrive as :service-request-id")))) + +(deftest test-schema-1-0-52-4-model-change-context-tier + (testing "session.model_change accepts :context-tier (default | long_context | nil)" + ;; Upstream schema 1.0.52-4 adds optional :context-tier to ModelChangeData. + ;; A literal `null` explicitly clears a previously-selected tier. + (let [spec :github.copilot-sdk.generated.event-specs/session.model_change-data] + (is (s/valid? spec {:new-model "gpt-5" :context-tier "default"})) + (is (s/valid? spec {:new-model "gpt-5" :context-tier "long_context"})) + (is (s/valid? spec {:new-model "gpt-5" :context-tier nil})) + (is (s/valid? spec {:new-model "gpt-5"})) + (is (not (s/valid? spec {:new-model "gpt-5" :context-tier "tiny"})))))) + +(deftest test-schema-1-0-52-4-skill-invoked-source-trigger + (testing "skill.invoked accepts :source and :trigger" + (let [spec :github.copilot-sdk.generated.event-specs/skill.invoked-data] + (is (s/valid? spec {:name "foo" :path "/x" :content "..."})) + (is (s/valid? spec {:name "foo" :path "/x" :content "..." + :source "project" + :trigger "user-invoked"})) + (is (s/valid? spec {:name "foo" :path "/x" :content "..." + :trigger "agent-invoked"})) + (is (s/valid? spec {:name "foo" :path "/x" :content "..." + :trigger "context-load"})) + (is (not (s/valid? spec {:name "foo" :path "/x" :content "..." + :trigger "bogus"})))))) + +(deftest test-schema-1-0-52-4-runtime-instructions-section + (testing ":runtime-instructions is a known system message section (upstream PR #1377)" + (is (contains? (set (keys specs/system-prompt-sections)) :runtime-instructions)) + (is (= "runtime_instructions" (util/section-kw->wire-id :runtime-instructions)) + ":runtime-instructions converts to the wire string \"runtime_instructions\"") + (is (s/valid? :github.copilot-sdk.specs/system-prompt-section :runtime-instructions)) + (is (s/valid? :github.copilot-sdk.specs/system-message-section :runtime-instructions) + "::system-message-section alias also accepts it")) + (testing "system-message-sections alias points at the same map (upstream rename)" + (is (identical? specs/system-prompt-sections specs/system-message-sections)))) + +(deftest test-schema-1-0-52-4-runtime-instructions-wire-roundtrip + (testing ":runtime-instructions section survives the create-session wire conversion" + (let [seen (atom {}) + _ (mock/set-request-hook! *mock-server* + (fn [method params] + (when (#{"session.create"} method) + (swap! seen assoc method params)))) + _ (sdk/create-session *test-client* + {:on-permission-request sdk/approve-all + :system-message {:mode :customize + :sections + {:runtime-instructions + {:action :replace + :content "runtime ctx"}}}}) + wire (get-in @seen ["session.create" :systemMessage :sections])] + (is (contains? wire :runtime_instructions) + ":runtime-instructions must be sent as wire key :runtime_instructions") + (is (= "replace" (get-in wire [:runtime_instructions :action]))) + (is (= "runtime ctx" (get-in wire [:runtime_instructions :content])))))) + +(deftest test-schema-1-0-52-4-min-protocol-version-3 + (testing "client rejects servers reporting protocol version < 3 (upstream PR #1378)" + ;; The SDK no longer supports v2 servers after the cleanup PR removed + ;; the v2 `tool.call` / `permission.request` back-compat adapters. + (let [server (mock/create-mock-server) + _ (reset! (:protocol-version server) 2) + _ (mock/start-mock-server! server) + client (sdk/client {:auto-start? false}) + [in out] (mock/client-streams server)] + (try + (is (thrown-with-msg? clojure.lang.ExceptionInfo + #"(?i)protocol.*version" + (client/connect-with-streams! client in out))) + (finally + (try (sdk/disconnect! client) (catch Exception _)) + (mock/stop-mock-server! server)))))) + ;; ----------------------------------------------------------------------------- ;; Last Session ID Tests ;; ----------------------------------------------------------------------------- diff --git a/test/github/copilot_sdk/mock_server.clj b/test/github/copilot_sdk/mock_server.clj index 5748689..ea44113 100644 --- a/test/github/copilot_sdk/mock_server.clj +++ b/test/github/copilot_sdk/mock_server.clj @@ -9,7 +9,7 @@ PipedInputStream PipedOutputStream] [java.util.concurrent.atomic AtomicLong])) -(def ^:private PROTOCOL_VERSION 2) +(def ^:private DEFAULT_PROTOCOL_VERSION 3) (defn- write-message "Write a JSON-RPC message with Content-Length framing." @@ -69,7 +69,8 @@ expected-token ; atom string-or-nil - when set, `connect` validates token supports-connect? ; atom boolean - when false, `connect` returns -32601 (legacy fallback) pending-events ; atom - events to send on next opportunity - pending-responses]) ; atom {id -> chan} - responses to server→client RPCs + pending-responses ; atom {id -> chan} - responses to server→client RPCs + protocol-version]) ; atom Long - protocol version reported by ping/connect/status (defn- generate-id [^AtomicLong counter] (str "evt-" (.incrementAndGet counter))) @@ -96,7 +97,7 @@ (defn- handle-ping [server params] {:message (:message params) :timestamp (.toString (java.time.Instant/now)) - :protocolVersion PROTOCOL_VERSION}) + :protocolVersion (or (some-> server :protocol-version deref) DEFAULT_PROTOCOL_VERSION)}) (defn- handle-connect "Handle the `connect` handshake (upstream PR #1176). When :expected-token is @@ -109,12 +110,12 @@ (throw (ex-info "Invalid connection token" {:code -32603 :method "connect"}))) {:ok true - :protocolVersion PROTOCOL_VERSION + :protocolVersion (or (some-> server :protocol-version deref) DEFAULT_PROTOCOL_VERSION) :version "0.0.389-mock"})) (defn- handle-status-get [server params] {:version "0.0.389-mock" - :protocolVersion PROTOCOL_VERSION}) + :protocolVersion (or (some-> server :protocol-version deref) DEFAULT_PROTOCOL_VERSION)}) (defn- handle-auth-get-status [server params] {:isAuthenticated true @@ -460,7 +461,8 @@ :expected-token (atom nil) :supports-connect? (atom true) :pending-events (atom []) - :pending-responses (atom {})}))) + :pending-responses (atom {}) + :protocol-version (atom DEFAULT_PROTOCOL_VERSION)}))) (defn start-mock-server! "Start the mock server in a background thread."