From caba3e5d6c31a475d0b6e504822f3640ca67624f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 15 Jul 2026 03:05:28 +0000 Subject: [PATCH 1/7] docs: update version references to 1.0.7-preview.3 --- java/README.md | 6 +++--- java/jbang-example.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/java/README.md b/java/README.md index fc3713a2d2..72b3f0ea6e 100644 --- a/java/README.md +++ b/java/README.md @@ -39,7 +39,7 @@ Replace `${copilot.sdk.version}` with the latest release from Maven Central. ### Gradle ```groovy -implementation 'com.github:copilot-sdk-java:1.0.7-preview.2-01' +implementation 'com.github:copilot-sdk-java:1.0.7-preview.3-01' ``` #### Snapshot Builds @@ -58,7 +58,7 @@ Snapshot builds of the next development version are published to Maven Central S com.github copilot-sdk-java - 1.0.8-preview.2-SNAPSHOT + 1.0.8-preview.3-SNAPSHOT ``` @@ -67,7 +67,7 @@ Snapshot builds of the next development version are published to Maven Central S Replace `${copilot.sdk.version}` with the latest release from Maven Central. ```groovy -implementation 'com.github:copilot-sdk-java:1.0.7-preview.2-01-SNAPSHOT' +implementation 'com.github:copilot-sdk-java:1.0.7-preview.3-01-SNAPSHOT' ``` ## Quick Start diff --git a/java/jbang-example.java b/java/jbang-example.java index 1312286b42..abc1e0b02e 100644 --- a/java/jbang-example.java +++ b/java/jbang-example.java @@ -1,5 +1,5 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS com.github:copilot-sdk-java:1.0.7-preview.2-01 +//DEPS com.github:copilot-sdk-java:1.0.7-preview.3-01 import com.github.copilot.CopilotClient; import com.github.copilot.generated.AssistantMessageEvent; import com.github.copilot.generated.SessionUsageInfoEvent; From a4b7285e098a8ce3d8f5bf6193e6cc9e56f85142 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 15 Jul 2026 03:06:05 +0000 Subject: [PATCH 2/7] [maven-release-plugin] prepare release java/v1.0.7-preview.3 --- java/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/pom.xml b/java/pom.xml index d0660b3d58..e2de2c575c 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -7,7 +7,7 @@ com.github copilot-sdk-java - 1.0.8-preview.2-SNAPSHOT + 1.0.7-preview.3 jar GitHub Copilot SDK :: Java @@ -33,7 +33,7 @@ scm:git:https://github.com/github/copilot-sdk.git scm:git:https://github.com/github/copilot-sdk.git https://github.com/github/copilot-sdk - HEAD + java/v1.0.7-preview.3 From 6e3893c8c45a12c56b6049076de74ec1fc4fff6f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 15 Jul 2026 03:06:08 +0000 Subject: [PATCH 3/7] [maven-release-plugin] prepare for next development iteration --- java/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/pom.xml b/java/pom.xml index e2de2c575c..81d01b0056 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -7,7 +7,7 @@ com.github copilot-sdk-java - 1.0.7-preview.3 + 1.0.8-preview.3-SNAPSHOT jar GitHub Copilot SDK :: Java @@ -33,7 +33,7 @@ scm:git:https://github.com/github/copilot-sdk.git scm:git:https://github.com/github/copilot-sdk.git https://github.com/github/copilot-sdk - java/v1.0.7-preview.3 + HEAD From ffb4d98b8fb4de55135e2067e9b835bac99e9f8a Mon Sep 17 00:00:00 2001 From: Sunbrye Ly <56200261+sunbrye@users.noreply.github.com> Date: Wed, 15 Jul 2026 12:02:18 -0700 Subject: [PATCH 4/7] Remove HMAC key authentication method from public SDK auth docs (#1994) The HMAC key auth method (CAPI_HMAC_KEY / COPILOT_HMAC_KEY) should not be documented publicly; it was re-added by doc automation. Removes it from the authentication priority list in authenticate.md and the summary in README.md. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/auth/README.md | 2 +- docs/auth/authenticate.md | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/auth/README.md b/docs/auth/README.md index b09646d5d7..282bcb0192 100644 --- a/docs/auth/README.md +++ b/docs/auth/README.md @@ -7,6 +7,6 @@ Choose the authentication method that best fits your deployment scenario for the ## Authentication priority -When multiple credentials are configured, an explicit SDK token takes priority, followed by HMAC or direct Copilot API environment authentication, environment variable GitHub tokens, stored Copilot CLI credentials, and then GitHub CLI credentials. See [Authenticate Copilot SDK](authenticate.md#authentication-priority) for details. +When multiple credentials are configured, an explicit SDK token takes priority, followed by direct Copilot API environment authentication, environment variable GitHub tokens, stored Copilot CLI credentials, and then GitHub CLI credentials. See [Authenticate Copilot SDK](authenticate.md#authentication-priority) for details. For multi-user server mode, pass a per-session `gitHubToken` so each session runs with the correct GitHub identity; see [Multi-user and server deployments](../setup/multi-tenancy.md). diff --git a/docs/auth/authenticate.md b/docs/auth/authenticate.md index 7661060263..1a01901863 100644 --- a/docs/auth/authenticate.md +++ b/docs/auth/authenticate.md @@ -301,7 +301,6 @@ BYOK allows you to use your own API keys from model providers like Azure AI Foun When multiple authentication methods are available, the SDK uses them in this priority order: 1. **Explicit `gitHubToken`** - Token passed directly to the SDK client or session configuration -1. **HMAC key** - `CAPI_HMAC_KEY` or `COPILOT_HMAC_KEY` environment variables 1. **Direct API token** - `GITHUB_COPILOT_API_TOKEN` with `COPILOT_API_URL` 1. **Environment variable tokens** - `COPILOT_GITHUB_TOKEN` → `GH_TOKEN` → `GITHUB_TOKEN` 1. **Stored OAuth credentials** - From previous `copilot` CLI login From 82b96a1b9448f11452eb5690c186126c66375ee1 Mon Sep 17 00:00:00 2001 From: Belal Taher Date: Wed, 15 Jul 2026 16:31:24 -0400 Subject: [PATCH 5/7] Add opaque `metadata` passthrough to SDK tool definitions (#1864) * Add opaque metadata passthrough to SDK tool definitions Add an optional, opaque `metadata` bag to tool definitions across all SDK languages and forward it verbatim over the session.create/resume RPC. This lets hosts attach namespaced metadata to tools without expanding the typed public contract; the runtime may recognize specific keys to inform host-specific behavior. Unknown keys are round-tripped untouched. Languages: nodejs (Tool.metadata + defineTool), python (Tool.metadata + define_tool), go (Tool.Metadata), rust (Tool.metadata + with_metadata), java (ToolDefinition.metadata + createWithMetadata), dotnet (CopilotToolOptions.Metadata + wire ToolDefinition.Metadata). Tests added per language for wire/serialization forwarding and omission when unset. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address review feedback on tool metadata passthrough - dotnet: change Metadata value type to IDictionary for NativeAOT-safe serialization (CopilotToolOptions + wire ToolDefinition, FromAIFunction cast, unit test); drop redundant [JsonPropertyName("metadata")] since the Web camelCase policy already maps it; remove the block from the Metadata property. - Reword metadata doc comments across nodejs/python/go/rust/java to describe the bag opaquely without the SDK-vs-runtime/CLI distinction. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bf2bf0dd-ec9f-42f5-a6e1-4f2a9e562d5f * Java: enable tool metadata for @CopilotTool + compat constructor Address Java SME feedback so annotation-based tools reach parity with the programmatic API and existing constructor call sites keep compiling. - ToolDefinition: add a backward-compatible 7-arg constructor delegating to the canonical 8-arg with metadata=null. - CopilotTool: add metadata() plus nested MetadataEntry/MetadataValue/ MetadataFlag annotations (@Target({})) with a shallow bool|str|flag-map representation, documenting the programmatic API for richer values. - CopilotToolProcessor: generate the metadata constructor argument (Map.of(...) with an explicit type witness) instead of a hardcoded null; null when no metadata is present. - Tests: processor generation cases (nested flags, absent, combined flags), ToolDefinition 7-arg/.metadata(...) copy/flag-chaining cases, and a fromObject metadata assertion; extend the SimpleTools fixture pair to emit a safeForTelemetry metadata map. - ADR-005: update the generated snippet to the 8-arg shape and document the @CopilotTool(metadata = ...) syntax and emitted shape. Note: mvn verify / spotless:apply not run locally (no JDK/Maven in the dev environment); relies on PR CI. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bf2bf0dd-ec9f-42f5-a6e1-4f2a9e562d5f * Apply spotless formatting to Java metadata changes Reflow javadoc and wrap annotation/constructor args per the Eclipse formatter (mvn spotless:apply). No behavioral change. Verified locally: mvn spotless:check clean; ToolDefinitionTest (8), ToolDefinitionFromObjectTest (31), CopilotToolProcessorTest (37) all pass on JDK 26. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bf2bf0dd-ec9f-42f5-a6e1-4f2a9e562d5f * test: assert null metadata arg in generated tool definition Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ab2c14cd-d2e1-4524-a6dc-e9c10899343c * docs: describe the metadata-less ToolDefinition overload by its steady state Reword the 7-arg constructor's Javadoc to describe what the overload is (a convenience constructor equivalent to the canonical one with metadata=null) rather than its relationship to a specific change ("backward-compatible", "retained so ... keep compiling after metadata was added"), which is only meaningful in the context of the PR that introduced it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bf2bf0dd-ec9f-42f5-a6e1-4f2a9e562d5f * rust: use IndexMap for tool metadata to serialize deterministically HashMap serializes keys in a randomized order, unlike the sibling `parameters` field (IndexMap) and the other SDKs (nodejs/python insertion order, Go sorted). Switch the tool `metadata` bag to IndexMap so key order is deterministic and consistent, avoiding non-reproducible wire output. No new dependency; indexmap is already used by this struct. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bf2bf0dd-ec9f-42f5-a6e1-4f2a9e562d5f --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Ed Burns --- dotnet/src/Client.cs | 8 +- dotnet/src/CopilotTool.cs | 16 ++- dotnet/test/Unit/CopilotToolTests.cs | 29 +++++ go/client_test.go | 47 ++++++++ go/types.go | 5 + java/docs/adr/adr-005-tool-definition.md | 33 +++++- .../github/copilot/rpc/ToolDefinition.java | 109 +++++++++++++++--- .../com/github/copilot/tool/CopilotTool.java | 83 +++++++++++++ .../copilot/tool/CopilotToolProcessor.java | 40 ++++++- .../github/copilot/ToolDefinitionTest.java | 69 +++++++++++ .../ErgonomicTestTools$$CopilotToolMeta.java | 8 +- .../rpc/ToolDefinitionFromObjectTest.java | 15 +++ .../ArgCoercionTools$$CopilotToolMeta.java | 2 +- .../DateTimeTools$$CopilotToolMeta.java | 2 +- .../DefaultValueTools$$CopilotToolMeta.java | 2 +- ...InvocationAwareTools$$CopilotToolMeta.java | 12 +- .../MultiReturnTools$$CopilotToolMeta.java | 6 +- .../OptionalParamTools$$CopilotToolMeta.java | 8 +- .../OverrideTools$$CopilotToolMeta.java | 2 +- .../SimpleTools$$CopilotToolMeta.java | 6 +- .../copilot/rpc/fixtures/SimpleTools.java | 5 +- ...taticInvocationTools$$CopilotToolMeta.java | 2 +- .../StaticTools$$CopilotToolMeta.java | 2 +- .../tool/CopilotToolProcessorTest.java | 82 +++++++++++++ nodejs/src/client.ts | 2 + nodejs/src/types.ts | 9 ++ nodejs/test/client.test.ts | 65 +++++++++++ python/copilot/client.py | 4 + python/copilot/tools.py | 11 ++ python/test_client.py | 41 +++++++ rust/src/types.rs | 40 +++++++ 31 files changed, 716 insertions(+), 49 deletions(-) diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs index 97e4f1094d..a512978225 100644 --- a/dotnet/src/Client.cs +++ b/dotnet/src/Client.cs @@ -15,6 +15,7 @@ using System.Runtime.InteropServices; using System.Text; using System.Text.Json; +using System.Text.Json.Nodes; using System.Text.Json.Serialization; using System.Text.RegularExpressions; @@ -2768,17 +2769,20 @@ internal record ToolDefinition( JsonElement Parameters, /* JSON schema */ bool? OverridesBuiltInTool = null, bool? SkipPermission = null, - CopilotToolDefer? Defer = null) + CopilotToolDefer? Defer = null, + IDictionary? Metadata = null) { public static ToolDefinition FromAIFunction(AIFunctionDeclaration function) { var overrides = function.AdditionalProperties.TryGetValue(CopilotTool.OverridesBuiltInToolKey, out var val) && val is true; var skipPerm = function.AdditionalProperties.TryGetValue(CopilotTool.SkipPermissionKey, out var skipVal) && skipVal is true; var defer = function.AdditionalProperties.TryGetValue(CopilotTool.DeferKey, out var deferVal) && deferVal is CopilotToolDefer d ? d : (CopilotToolDefer?)null; + var metadata = function.AdditionalProperties.TryGetValue(CopilotTool.MetadataKey, out var metaVal) && metaVal is IDictionary m ? m : null; return new ToolDefinition(function.Name, function.Description, function.JsonSchema, overrides ? true : null, skipPerm ? true : null, - defer); + defer, + metadata); } } diff --git a/dotnet/src/CopilotTool.cs b/dotnet/src/CopilotTool.cs index d6dc0351e4..e22296bccd 100644 --- a/dotnet/src/CopilotTool.cs +++ b/dotnet/src/CopilotTool.cs @@ -3,6 +3,7 @@ *--------------------------------------------------------------------------------------------*/ using Microsoft.Extensions.AI; +using System.Text.Json.Nodes; namespace GitHub.Copilot; @@ -20,6 +21,9 @@ public static class CopilotTool /// The key used in to carry the tool's deferral mode. internal const string DeferKey = "defer"; + /// The key used in to carry the tool's opaque host-defined metadata. + internal const string MetadataKey = "metadata"; + /// /// Defines a tool for use in a . /// @@ -87,7 +91,7 @@ static void ApplyToolInvocationBinding(AIFunctionFactoryOptions factoryOptions) static void ApplyToolOptions(AIFunctionFactoryOptions factoryOptions, CopilotToolOptions? toolOptions) { - if (toolOptions is not null && (toolOptions.OverridesBuiltInTool || toolOptions.SkipPermission || toolOptions.Defer is not null)) + if (toolOptions is not null && (toolOptions.OverridesBuiltInTool || toolOptions.SkipPermission || toolOptions.Defer is not null || toolOptions.Metadata is not null)) { Dictionary additionalProperties = new(StringComparer.Ordinal); if (factoryOptions.AdditionalProperties is not null) @@ -113,6 +117,11 @@ static void ApplyToolOptions(AIFunctionFactoryOptions factoryOptions, CopilotToo additionalProperties[DeferKey] = defer; } + if (toolOptions.Metadata is { } metadata) + { + additionalProperties[MetadataKey] = metadata; + } + factoryOptions.AdditionalProperties = additionalProperties; } } @@ -151,6 +160,11 @@ public sealed class CopilotToolOptions /// SDK forwards it to the CLI as the tool's defer mode. Defaults to "auto". /// public CopilotToolDefer? Defer { get; set; } + + /// + /// Gets or sets opaque, host-defined metadata associated with the tool definition. + /// + public IDictionary? Metadata { get; set; } } /// diff --git a/dotnet/test/Unit/CopilotToolTests.cs b/dotnet/test/Unit/CopilotToolTests.cs index 9c9e2a93ba..c3f5861492 100644 --- a/dotnet/test/Unit/CopilotToolTests.cs +++ b/dotnet/test/Unit/CopilotToolTests.cs @@ -5,6 +5,7 @@ using Microsoft.Extensions.AI; using System.ComponentModel; using System.Text.Json; +using System.Text.Json.Nodes; using Xunit; namespace GitHub.Copilot.Test.Unit; @@ -43,6 +44,34 @@ public void DefineTool_Omits_Copilot_Metadata_When_Flags_Are_False() Assert.False(function.AdditionalProperties.ContainsKey("defer")); } + [Fact] + public void DefineTool_Sets_Metadata_In_Additional_Properties() + { + var metadata = new Dictionary + { + ["github.com/copilot:safeForTelemetry"] = new JsonObject + { + ["name"] = true, + ["inputsNames"] = false + } + }; + + var function = CopilotTool.DefineTool( + ReturnsOk, + new CopilotToolOptions { Metadata = metadata }); + + Assert.True(function.AdditionalProperties.TryGetValue("metadata", out var value)); + Assert.Same(metadata, value); + } + + [Fact] + public void DefineTool_Omits_Metadata_When_Unset() + { + var function = CopilotTool.DefineTool(ReturnsOk); + + Assert.False(function.AdditionalProperties.ContainsKey("metadata")); + } + [Fact] public void DefineTool_Accepts_Lambda_Handlers_Without_Casts() { diff --git a/go/client_test.go b/go/client_test.go index 48871e71f6..b24628d836 100644 --- a/go/client_test.go +++ b/go/client_test.go @@ -1428,6 +1428,53 @@ func TestToolDefer(t *testing.T) { }) } +func TestToolMetadata(t *testing.T) { + t.Run("Metadata is serialized in tool definition", func(t *testing.T) { + tool := Tool{ + Name: "my_tool", + Description: "A custom tool", + Metadata: map[string]any{ + "github.com/copilot:safeForTelemetry": map[string]any{"name": true, "inputsNames": false}, + }, + Handler: func(_ ToolInvocation) (ToolResult, error) { return ToolResult{}, nil }, + } + data, err := json.Marshal(tool) + if err != nil { + t.Fatalf("failed to marshal: %v", err) + } + var m map[string]any + if err := json.Unmarshal(data, &m); err != nil { + t.Fatalf("failed to unmarshal: %v", err) + } + meta, ok := m["metadata"].(map[string]any) + if !ok { + t.Fatalf("expected metadata object, got %v", m) + } + if _, ok := meta["github.com/copilot:safeForTelemetry"]; !ok { + t.Errorf("expected namespaced key preserved, got %v", meta) + } + }) + + t.Run("Metadata omitted when unset", func(t *testing.T) { + tool := Tool{ + Name: "custom_tool", + Description: "A custom tool", + Handler: func(_ ToolInvocation) (ToolResult, error) { return ToolResult{}, nil }, + } + data, err := json.Marshal(tool) + if err != nil { + t.Fatalf("failed to marshal: %v", err) + } + var m map[string]any + if err := json.Unmarshal(data, &m); err != nil { + t.Fatalf("failed to unmarshal: %v", err) + } + if _, ok := m["metadata"]; ok { + t.Errorf("expected metadata to be omitted, got %v", m) + } + }) +} + func TestClient_CreateSession_AllowsMissingPermissionHandler(t *testing.T) { t.Run("accepts nil config before connection validation", func(t *testing.T) { client := NewClient(&ClientOptions{Connection: StdioConnection{Path: "/__nonexistent_copilot_binary__"}}) diff --git a/go/types.go b/go/types.go index c9dd71d19b..d487d6d8ab 100644 --- a/go/types.go +++ b/go/types.go @@ -1341,6 +1341,11 @@ type Tool struct { // Defer controls whether the tool may be deferred (loaded lazily via tool // search) rather than always pre-loaded. When empty, the runtime decides. Defer ToolDefer `json:"defer,omitempty"` + // Metadata is opaque, host-defined metadata associated with the tool + // definition. Keys are namespaced and not part of the stable public API; + // values are not interpreted and may be recognized to inform host-specific + // behavior. Unknown keys are preserved and round-tripped untouched. + Metadata map[string]any `json:"metadata,omitempty"` // Handler is optional. When nil, the SDK exposes the tool declaration but does // not automatically invoke it. Handler ToolHandler `json:"-"` diff --git a/java/docs/adr/adr-005-tool-definition.md b/java/docs/adr/adr-005-tool-definition.md index e1b4dcc764..dc1eb36143 100644 --- a/java/docs/adr/adr-005-tool-definition.md +++ b/java/docs/adr/adr-005-tool-definition.md @@ -181,14 +181,45 @@ final class MyTools$$CopilotToolMeta { Phase phase = invocation.getArgumentsAs(Phase.class); return CompletableFuture.completedFuture( instance.setCurrentPhase(phase)); - }, null, null, null) + }, null, null, null, null) ); } } ``` +The trailing constructor arguments are `overridesBuiltInTool`, `skipPermission`, `defer`, and `metadata` — all `null` here because none were set on the annotation. + At runtime, `ToolDefinition.fromObject(myTools)` loads the generated `$$CopilotToolMeta` class — zero reflection, zero dependency on `-parameters`. +### Host-defined metadata + +`@CopilotTool` also accepts an opaque `metadata` bag via nested annotations. Because annotation members can't express arbitrary maps, the representation is deliberately shallow: each entry maps a namespaced key to a boolean, a string, or a one-level map of named boolean flags. + +```java +@CopilotTool( + value = "Reports phase", + metadata = { + @CopilotTool.MetadataEntry( + key = "github.com/copilot:safeForTelemetry", + value = @CopilotTool.MetadataValue(flags = { + @CopilotTool.MetadataFlag(name = "name", value = true), + @CopilotTool.MetadataFlag(name = "inputsNames", value = false) + })) + }) +public String reportPhase(@CopilotToolParam("Phase") String phase) { + return phase; +} +``` + +The processor emits this as the `metadata` constructor argument: + +```java +Map.of("github.com/copilot:safeForTelemetry", + Map.of("name", true, "inputsNames", false)) +``` + +For richer values (numbers, arrays, deeper nesting), use the programmatic `ToolDefinition.createWithMetadata(...)` / `ToolDefinition.metadata(...)` API instead. + ### Compile-time validation Because the processor has full access to the source AST, it can emit compile errors for: diff --git a/java/src/main/java/com/github/copilot/rpc/ToolDefinition.java b/java/src/main/java/com/github/copilot/rpc/ToolDefinition.java index 8a336c749a..ccf0ef5309 100644 --- a/java/src/main/java/com/github/copilot/rpc/ToolDefinition.java +++ b/java/src/main/java/com/github/copilot/rpc/ToolDefinition.java @@ -75,6 +75,9 @@ * controls whether the tool may be deferred (loaded lazily via tool * search) rather than always pre-loaded; {@code null} lets the * runtime decide + * @param metadata + * opaque, host-defined metadata; keys are namespaced and not part of + * the stable public API; {@code null} when unset * @see SessionConfig#setTools(java.util.List) * @see ToolHandler * @since 1.0.0 @@ -83,7 +86,36 @@ public record ToolDefinition(@JsonProperty("name") String name, @JsonProperty("description") String description, @JsonProperty("parameters") Object parameters, @JsonIgnore ToolHandler handler, @JsonProperty("overridesBuiltInTool") Boolean overridesBuiltInTool, - @JsonProperty("skipPermission") Boolean skipPermission, @JsonProperty("defer") ToolDefer defer) { + @JsonProperty("skipPermission") Boolean skipPermission, @JsonProperty("defer") ToolDefer defer, + @JsonProperty("metadata") Map metadata) { + + /** + * Creates a tool definition without a {@code metadata} bag. + *

+ * Convenience overload equivalent to the canonical constructor with + * {@code metadata} set to {@code null}. + * + * @param name + * the unique name of the tool + * @param description + * a description of what the tool does + * @param parameters + * the JSON Schema for the tool's parameters + * @param handler + * the handler function to execute when invoked + * @param overridesBuiltInTool + * whether this tool overrides a built-in tool; {@code null} for the + * default + * @param skipPermission + * whether the tool may run without a permission check; {@code null} + * for the default + * @param defer + * the deferral mode; {@code null} lets the runtime decide + */ + public ToolDefinition(String name, String description, Object parameters, ToolHandler handler, + Boolean overridesBuiltInTool, Boolean skipPermission, ToolDefer defer) { + this(name, description, parameters, handler, overridesBuiltInTool, skipPermission, defer, null); + } /** * Creates a tool definition with a JSON schema for parameters. @@ -103,7 +135,7 @@ public record ToolDefinition(@JsonProperty("name") String name, @JsonProperty("d */ public static ToolDefinition create(String name, String description, Map schema, ToolHandler handler) { - return new ToolDefinition(name, description, schema, handler, null, null, null); + return new ToolDefinition(name, description, schema, handler, null, null, null, null); } /** @@ -127,7 +159,7 @@ public static ToolDefinition create(String name, String description, Map schema, ToolHandler handler) { - return new ToolDefinition(name, description, schema, handler, true, null, null); + return new ToolDefinition(name, description, schema, handler, true, null, null, null); } /** @@ -150,7 +182,7 @@ public static ToolDefinition createOverride(String name, String description, Map */ public static ToolDefinition createSkipPermission(String name, String description, Map schema, ToolHandler handler) { - return new ToolDefinition(name, description, schema, handler, null, true, null); + return new ToolDefinition(name, description, schema, handler, null, true, null, null); } /** @@ -176,7 +208,32 @@ public static ToolDefinition createSkipPermission(String name, String descriptio */ public static ToolDefinition createWithDefer(String name, String description, Map schema, ToolHandler handler, ToolDefer defer) { - return new ToolDefinition(name, description, schema, handler, null, null, defer); + return new ToolDefinition(name, description, schema, handler, null, null, defer, null); + } + + /** + * Creates a tool definition with opaque, host-defined metadata. + *

+ * Use this factory method to attach namespaced metadata to the tool. The keys + * are not part of the stable public API; specific keys may be recognized to + * inform host-specific behavior. + * + * @param name + * the unique name of the tool + * @param description + * a description of what the tool does + * @param schema + * the JSON Schema as a {@code Map} + * @param handler + * the handler function to execute when invoked + * @param metadata + * the opaque metadata map + * @return a new tool definition with the metadata set + * @since 1.0.7 + */ + public static ToolDefinition createWithMetadata(String name, String description, Map schema, + ToolHandler handler, Map metadata) { + return new ToolDefinition(name, description, schema, handler, null, null, null, metadata); } /** @@ -247,7 +304,7 @@ public static List fromClass(Class clazz) { */ @CopilotExperimental public ToolDefinition overridesBuiltInTool(boolean value) { - return new ToolDefinition(name, description, parameters, handler, value, skipPermission, defer); + return new ToolDefinition(name, description, parameters, handler, value, skipPermission, defer, metadata); } /** @@ -261,7 +318,7 @@ public ToolDefinition overridesBuiltInTool(boolean value) { */ @CopilotExperimental public ToolDefinition skipPermission(boolean value) { - return new ToolDefinition(name, description, parameters, handler, overridesBuiltInTool, value, defer); + return new ToolDefinition(name, description, parameters, handler, overridesBuiltInTool, value, defer, metadata); } /** @@ -275,7 +332,23 @@ public ToolDefinition skipPermission(boolean value) { */ @CopilotExperimental public ToolDefinition defer(ToolDefer value) { - return new ToolDefinition(name, description, parameters, handler, overridesBuiltInTool, skipPermission, value); + return new ToolDefinition(name, description, parameters, handler, overridesBuiltInTool, skipPermission, value, + metadata); + } + + /** + * Returns a copy with the opaque {@code metadata} bag set. + * + * @param value + * the opaque, host-defined metadata; keys are namespaced and not + * part of the stable public API + * @return a new {@code ToolDefinition} with the metadata applied + * @since 1.0.7 + */ + @CopilotExperimental + public ToolDefinition metadata(Map value) { + return new ToolDefinition(name, description, parameters, handler, overridesBuiltInTool, skipPermission, defer, + value); } // ------------------------------------------------------------------ @@ -319,7 +392,7 @@ public static ToolDefinition from(String name, String description, Supplier< R result = handler.get(); return CompletableFuture.completedFuture(formatResult(result, mapper)); }; - return new ToolDefinition(name, description, schema, toolHandler, null, null, null); + return new ToolDefinition(name, description, schema, toolHandler, null, null, null, null); } /** @@ -361,7 +434,7 @@ public static ToolDefinition from(String name, String description, Param R result = handler.apply(arg1); return CompletableFuture.completedFuture(formatResult(result, mapper)); }; - return new ToolDefinition(name, description, schema, toolHandler, null, null, null); + return new ToolDefinition(name, description, schema, toolHandler, null, null, null, null); } /** @@ -409,7 +482,7 @@ public static ToolDefinition from(String name, String description, P R result = handler.apply(arg1, arg2); return CompletableFuture.completedFuture(formatResult(result, mapper)); }; - return new ToolDefinition(name, description, schema, toolHandler, null, null, null); + return new ToolDefinition(name, description, schema, toolHandler, null, null, null, null); } // ------------------------------------------------------------------ @@ -458,7 +531,7 @@ public static ToolDefinition fromAsync(String name, String description, } return future.thenApply(result -> formatResult(result, mapper)); }; - return new ToolDefinition(name, description, schema, toolHandler, null, null, null); + return new ToolDefinition(name, description, schema, toolHandler, null, null, null, null); } /** @@ -506,7 +579,7 @@ public static ToolDefinition fromAsync(String name, String description, } return future.thenApply(result -> formatResult(result, mapper)); }; - return new ToolDefinition(name, description, schema, toolHandler, null, null, null); + return new ToolDefinition(name, description, schema, toolHandler, null, null, null, null); } /** @@ -551,7 +624,7 @@ public static ToolDefinition fromAsync(String name, String descripti } return future.thenApply(result -> formatResult(result, mapper)); }; - return new ToolDefinition(name, description, schema, toolHandler, null, null, null); + return new ToolDefinition(name, description, schema, toolHandler, null, null, null, null); } // ------------------------------------------------------------------ @@ -594,7 +667,7 @@ public static ToolDefinition fromWithToolInvocation(String name, String desc R result = handler.apply(invocation); return CompletableFuture.completedFuture(formatResult(result, mapper)); }; - return new ToolDefinition(name, description, schema, toolHandler, null, null, null); + return new ToolDefinition(name, description, schema, toolHandler, null, null, null, null); } /** @@ -640,7 +713,7 @@ public static ToolDefinition fromWithToolInvocation(String name, String R result = handler.apply(arg1, invocation); return CompletableFuture.completedFuture(formatResult(result, mapper)); }; - return new ToolDefinition(name, description, schema, toolHandler, null, null, null); + return new ToolDefinition(name, description, schema, toolHandler, null, null, null, null); } // ------------------------------------------------------------------ @@ -689,7 +762,7 @@ public static ToolDefinition fromAsyncWithToolInvocation(String name, String } return future.thenApply(result -> formatResult(result, mapper)); }; - return new ToolDefinition(name, description, schema, toolHandler, null, null, null); + return new ToolDefinition(name, description, schema, toolHandler, null, null, null, null); } /** @@ -741,7 +814,7 @@ public static ToolDefinition fromAsyncWithToolInvocation(String name, St } return future.thenApply(result -> formatResult(result, mapper)); }; - return new ToolDefinition(name, description, schema, toolHandler, null, null, null); + return new ToolDefinition(name, description, schema, toolHandler, null, null, null, null); } // ------------------------------------------------------------------ diff --git a/java/src/main/java/com/github/copilot/tool/CopilotTool.java b/java/src/main/java/com/github/copilot/tool/CopilotTool.java index 0bad327b99..db9e3ca62d 100644 --- a/java/src/main/java/com/github/copilot/tool/CopilotTool.java +++ b/java/src/main/java/com/github/copilot/tool/CopilotTool.java @@ -50,4 +50,87 @@ /** Defer configuration for this tool. */ ToolDefer defer() default ToolDefer.NONE; + + /** + * Opaque, host-defined metadata for this tool. Keys are namespaced and not part + * of the stable public API; specific keys may be recognized to inform + * host-specific behavior. + * + *

+ * Because annotation members cannot express arbitrary maps, this uses a + * deliberately shallow representation: each {@link MetadataEntry} maps a string + * key to a single {@link MetadataValue} that is either a boolean, a string, or + * a one-level map of named boolean {@link MetadataFlag flags}. Numbers, arrays, + * and deeper nesting are not supported here; use the programmatic + * {@code ToolDefinition.createWithMetadata(...)} / + * {@code ToolDefinition.metadata(...)} API for richer values. + * + *

+ * Example emitted shape: + * + *

+     * Map.of("github.com/copilot:safeForTelemetry", Map.of("name", true, "inputsNames", false))
+     * 
+ */ + MetadataEntry[] metadata() default {}; + + /** + * A single metadata key/value pair. Used only as a member value of + * {@link CopilotTool#metadata()}. + */ + @Documented + @Retention(RetentionPolicy.RUNTIME) + @Target({}) + @interface MetadataEntry { + + /** The namespaced metadata key. */ + String key(); + + /** The value associated with {@link #key()}. */ + MetadataValue value(); + } + + /** + * A metadata value. Exactly one representation is intended per value: a map of + * named boolean {@link #flags()} (when non-empty), otherwise a {@link #str()} + * (when non-empty), otherwise a {@link #bool()}. + */ + @Documented + @Retention(RetentionPolicy.RUNTIME) + @Target({}) + @interface MetadataValue { + + /** + * Scalar boolean value. Used when {@link #flags()} and {@link #str()} are + * unset. + */ + boolean bool() default false; + + /** + * Scalar string value. Used when {@link #flags()} is empty and this is + * non-empty. + */ + String str() default ""; + + /** + * Object-like value: a one-level map of named boolean flags. Takes precedence + * when non-empty. + */ + MetadataFlag[] flags() default {}; + } + + /** + * A single named boolean flag within a {@link MetadataValue#flags()} map. + */ + @Documented + @Retention(RetentionPolicy.RUNTIME) + @Target({}) + @interface MetadataFlag { + + /** The flag name (map key). */ + String name(); + + /** The flag value. */ + boolean value(); + } } diff --git a/java/src/main/java/com/github/copilot/tool/CopilotToolProcessor.java b/java/src/main/java/com/github/copilot/tool/CopilotToolProcessor.java index 9bf4e99c03..03af4a7cd9 100644 --- a/java/src/main/java/com/github/copilot/tool/CopilotToolProcessor.java +++ b/java/src/main/java/com/github/copilot/tool/CopilotToolProcessor.java @@ -276,10 +276,48 @@ private void writeToolDefinition(PrintWriter out, ExecutableElement method) { out.println(" },"); out.println(" " + overridesArg + ","); out.println(" " + skipPermArg + ","); - out.println(" " + deferArg); + out.println(" " + deferArg + ","); + out.println(" " + metadataSource(annotation)); out.print(" )"); } + /** + * Converts the {@code @CopilotTool(metadata = ...)} entries into a Java source + * literal. Returns {@code "null"} when no metadata is present, otherwise a + * {@code Map.of(...)} expression. + */ + private String metadataSource(CopilotTool annotation) { + CopilotTool.MetadataEntry[] entries = annotation.metadata(); + if (entries.length == 0) { + return "null"; + } + List parts = new ArrayList<>(); + for (CopilotTool.MetadataEntry entry : entries) { + parts.add("\"" + escapeJava(entry.key()) + "\", " + metadataValueSource(entry.value())); + } + return "Map.of(" + String.join(", ", parts) + ")"; + } + + /** + * Converts a single {@link CopilotTool.MetadataValue} into a Java source + * literal. A non-empty {@code flags} map takes precedence, then a non-empty + * {@code str}, otherwise the {@code bool} scalar. + */ + private String metadataValueSource(CopilotTool.MetadataValue value) { + CopilotTool.MetadataFlag[] flags = value.flags(); + if (flags.length > 0) { + List flagParts = new ArrayList<>(); + for (CopilotTool.MetadataFlag flag : flags) { + flagParts.add("\"" + escapeJava(flag.name()) + "\", " + flag.value()); + } + return "Map.of(" + String.join(", ", flagParts) + ")"; + } + if (!value.str().isEmpty()) { + return "\"" + escapeJava(value.str()) + "\""; + } + return String.valueOf(value.bool()); + } + private String generateSchemaWithParamMetadata(List parameters) { List schemaParameters = getSchemaParameters(parameters); diff --git a/java/src/test/java/com/github/copilot/ToolDefinitionTest.java b/java/src/test/java/com/github/copilot/ToolDefinitionTest.java index 614e6ab4fa..66c9f9ec86 100644 --- a/java/src/test/java/com/github/copilot/ToolDefinitionTest.java +++ b/java/src/test/java/com/github/copilot/ToolDefinitionTest.java @@ -59,4 +59,73 @@ void testDeferNeverIsSerialized() throws Exception { assertEquals("never", json.get("defer").asText()); } + + @Test + void testMetadataIsSerialized() throws Exception { + Map metadata = Map.of("github.com/copilot:safeForTelemetry", + Map.of("name", true, "inputsNames", false)); + ToolDefinition tool = ToolDefinition.createWithMetadata("my_tool", "A tool", schema(), + invocation -> CompletableFuture.completedFuture("ok"), metadata); + + ObjectNode json = (ObjectNode) MAPPER.readTree(MAPPER.writeValueAsString(tool)); + + assertTrue(json.has("metadata")); + assertTrue(json.get("metadata").has("github.com/copilot:safeForTelemetry")); + } + + @Test + void testMetadataOmittedWhenNull() throws Exception { + ToolDefinition tool = ToolDefinition.create("my_tool", "A tool", schema(), + invocation -> CompletableFuture.completedFuture("ok")); + + ObjectNode json = (ObjectNode) MAPPER.readTree(MAPPER.writeValueAsString(tool)); + + assertFalse(json.has("metadata")); + } + + @Test + void testSevenArgConstructorLeavesMetadataNull() throws Exception { + ToolDefinition tool = new ToolDefinition("my_tool", "A tool", schema(), + invocation -> CompletableFuture.completedFuture("ok"), null, null, null); + + assertNull(tool.metadata()); + + ObjectNode json = (ObjectNode) MAPPER.readTree(MAPPER.writeValueAsString(tool)); + + assertFalse(json.has("metadata")); + } + + @Test + void testMetadataCopyMethodSerializes() throws Exception { + Map metadata = Map.of("github.com/copilot:safeForTelemetry", + Map.of("name", true, "inputsNames", false)); + ToolDefinition tool = ToolDefinition + .create("my_tool", "A tool", schema(), invocation -> CompletableFuture.completedFuture("ok")) + .metadata(metadata); + + assertEquals(metadata, tool.metadata()); + + ObjectNode json = (ObjectNode) MAPPER.readTree(MAPPER.writeValueAsString(tool)); + + assertTrue(json.get("metadata").has("github.com/copilot:safeForTelemetry")); + } + + @Test + void testChainingFlagsPreservesMetadata() throws Exception { + Map metadata = Map.of("github.com/copilot:safeForTelemetry", Map.of("name", true)); + + ToolDefinition metadataFirst = ToolDefinition + .create("my_tool", "A tool", schema(), invocation -> CompletableFuture.completedFuture("ok")) + .metadata(metadata).overridesBuiltInTool(true).skipPermission(true).defer(ToolDefer.NEVER); + + ToolDefinition flagsFirst = ToolDefinition + .create("my_tool", "A tool", schema(), invocation -> CompletableFuture.completedFuture("ok")) + .overridesBuiltInTool(true).skipPermission(true).defer(ToolDefer.NEVER).metadata(metadata); + + assertEquals(metadata, metadataFirst.metadata()); + assertEquals(metadata, flagsFirst.metadata()); + assertEquals(Boolean.TRUE, flagsFirst.overridesBuiltInTool()); + assertEquals(Boolean.TRUE, flagsFirst.skipPermission()); + assertEquals(ToolDefer.NEVER, flagsFirst.defer()); + } } diff --git a/java/src/test/java/com/github/copilot/e2e/ErgonomicTestTools$$CopilotToolMeta.java b/java/src/test/java/com/github/copilot/e2e/ErgonomicTestTools$$CopilotToolMeta.java index 3e6291984f..56b8b281e6 100644 --- a/java/src/test/java/com/github/copilot/e2e/ErgonomicTestTools$$CopilotToolMeta.java +++ b/java/src/test/java/com/github/copilot/e2e/ErgonomicTestTools$$CopilotToolMeta.java @@ -32,7 +32,7 @@ public List definitions(ErgonomicTestTools instance, ObjectMappe Map args = invocation.getArguments(); String phase = (String) args.get("phase"); return CompletableFuture.completedFuture(instance.setCurrentPhase(phase)); - }, null, null, null), + }, null, null, null, null), new ToolDefinition( "search_items", "Search for items by keyword", Map .of("type", "object", "properties", @@ -44,11 +44,11 @@ public List definitions(ErgonomicTestTools instance, ObjectMappe Map args = invocation.getArguments(); String keyword = (String) args.get("keyword"); return CompletableFuture.completedFuture(instance.searchItems(keyword)); - }, null, null, null), + }, null, null, null, null), new ToolDefinition("get_status", "Returns the current status", Map.of("type", "object", "properties", Map.of(), "required", List.of()), invocation -> { return CompletableFuture.completedFuture(instance.getStatus()); - }, null, null, null), + }, null, null, null, null), new ToolDefinition("combine_values", "Combines two values into a single string", Map.of( "type", "object", "properties", Map .ofEntries( @@ -63,6 +63,6 @@ public List definitions(ErgonomicTestTools instance, ObjectMappe String value1 = (String) args.get("value1"); String value2 = (String) args.get("value2"); return CompletableFuture.completedFuture(instance.combineValues(value1, value2)); - }, null, null, null)); + }, null, null, null, null)); } } diff --git a/java/src/test/java/com/github/copilot/rpc/ToolDefinitionFromObjectTest.java b/java/src/test/java/com/github/copilot/rpc/ToolDefinitionFromObjectTest.java index 37ea9f6a50..afa3d42511 100644 --- a/java/src/test/java/com/github/copilot/rpc/ToolDefinitionFromObjectTest.java +++ b/java/src/test/java/com/github/copilot/rpc/ToolDefinitionFromObjectTest.java @@ -92,6 +92,21 @@ void fromObject_handlerInvocation() throws Exception { assertEquals("Hello, Alice!", result); } + @Test + void fromObject_toolMetadata() { + var tools = ToolDefinition.fromObject(new SimpleTools()); + + var withMetadata = findTool(tools, "greet_user"); + assertNotNull(withMetadata); + assertNotNull(withMetadata.metadata()); + assertEquals(Map.of("github.com/copilot:safeForTelemetry", Map.of("name", true, "inputsNames", false)), + withMetadata.metadata()); + + var withoutMetadata = findTool(tools, "add_numbers"); + assertNotNull(withoutMetadata); + assertNull(withoutMetadata.metadata()); + } + // ── Test 2: Handler return type patterns ──────────────────────────────────── @Test diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/ArgCoercionTools$$CopilotToolMeta.java b/java/src/test/java/com/github/copilot/rpc/fixtures/ArgCoercionTools$$CopilotToolMeta.java index 882c0555f7..5cc5ee87a5 100644 --- a/java/src/test/java/com/github/copilot/rpc/fixtures/ArgCoercionTools$$CopilotToolMeta.java +++ b/java/src/test/java/com/github/copilot/rpc/fixtures/ArgCoercionTools$$CopilotToolMeta.java @@ -45,6 +45,6 @@ public List definitions(ArgCoercionTools instance, ObjectMapper boolean flag = (Boolean) args.get("flag"); ArgCoercionTools.Color color = ArgCoercionTools.Color.valueOf((String) args.get("color")); return CompletableFuture.completedFuture(instance.mixedArgs(text, count, flag, color)); - }, null, null, null)); + }, null, null, null, null)); } } diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/DateTimeTools$$CopilotToolMeta.java b/java/src/test/java/com/github/copilot/rpc/fixtures/DateTimeTools$$CopilotToolMeta.java index 7001336504..0c2b1f07e7 100644 --- a/java/src/test/java/com/github/copilot/rpc/fixtures/DateTimeTools$$CopilotToolMeta.java +++ b/java/src/test/java/com/github/copilot/rpc/fixtures/DateTimeTools$$CopilotToolMeta.java @@ -33,6 +33,6 @@ public List definitions(DateTimeTools instance, ObjectMapper map Map args = invocation.getArguments(); LocalDateTime when = mapper.convertValue(args.get("when"), LocalDateTime.class); return CompletableFuture.completedFuture(instance.scheduleEvent(when)); - }, null, null, null)); + }, null, null, null, null)); } } diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/DefaultValueTools$$CopilotToolMeta.java b/java/src/test/java/com/github/copilot/rpc/fixtures/DefaultValueTools$$CopilotToolMeta.java index ee5369b849..6cef2e03a0 100644 --- a/java/src/test/java/com/github/copilot/rpc/fixtures/DefaultValueTools$$CopilotToolMeta.java +++ b/java/src/test/java/com/github/copilot/rpc/fixtures/DefaultValueTools$$CopilotToolMeta.java @@ -40,6 +40,6 @@ public List definitions(DefaultValueTools instance, ObjectMapper Object countRaw = args.containsKey("count") ? args.get("count") : 42; int count = ((Number) countRaw).intValue(); return CompletableFuture.completedFuture(instance.withDefault(label, count)); - }, null, null, null)); + }, null, null, null, null)); } } diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/InvocationAwareTools$$CopilotToolMeta.java b/java/src/test/java/com/github/copilot/rpc/fixtures/InvocationAwareTools$$CopilotToolMeta.java index cc7150e57c..e7c78608a0 100644 --- a/java/src/test/java/com/github/copilot/rpc/fixtures/InvocationAwareTools$$CopilotToolMeta.java +++ b/java/src/test/java/com/github/copilot/rpc/fixtures/InvocationAwareTools$$CopilotToolMeta.java @@ -23,7 +23,7 @@ public List definitions(InvocationAwareTools instance, ObjectMap Map args = invocation.getArguments(); String phase = (String) args.get("phase"); return CompletableFuture.completedFuture(instance.reportProgress(phase, invocation)); - }, null, null, null), + }, null, null, null, null), new ToolDefinition("report_progress_async", "Reports progress asynchronously with invocation context", Map.of("type", "object", "properties", Map.ofEntries( @@ -33,7 +33,7 @@ public List definitions(InvocationAwareTools instance, ObjectMap Map args = invocation.getArguments(); String phase = (String) args.get("phase"); return instance.reportProgressAsync(phase, invocation).thenApply(r -> (Object) r); - }, null, null, null), + }, null, null, null, null), new ToolDefinition("report_progress_first", "Reports progress with invocation first", Map.of("type", "object", "properties", Map.ofEntries( @@ -43,11 +43,11 @@ public List definitions(InvocationAwareTools instance, ObjectMap Map args = invocation.getArguments(); String phase = (String) args.get("phase"); return CompletableFuture.completedFuture(instance.reportProgressFirst(invocation, phase)); - }, null, null, null), + }, null, null, null, null), new ToolDefinition("only_context", "Reports context with invocation only", Map.of("type", "object", "properties", Map.of(), "required", List.of()), invocation -> CompletableFuture.completedFuture(instance.onlyContext(invocation)), null, null, - null), + null, null), new ToolDefinition("report_progress_middle", "Reports progress with invocation in the middle", Map.of( "type", "object", "properties", Map.ofEntries(Map.entry("phase", Map.of("type", "string", "description", "Current phase")), @@ -58,7 +58,7 @@ public List definitions(InvocationAwareTools instance, ObjectMap int limit = ((Number) args.get("limit")).intValue(); return CompletableFuture .completedFuture(instance.reportProgressMiddle(phase, invocation, limit)); - }, null, null, null), + }, null, null, null, null), new ToolDefinition("report_progress_with_record", "Reports progress with record args and invocation", Map.of("type", "object", "properties", Map.ofEntries(Map.entry("query", Map.of("type", "string")), @@ -69,6 +69,6 @@ public List definitions(InvocationAwareTools instance, ObjectMap RecordInvocationArgs.class); return CompletableFuture .completedFuture(instance.reportProgressWithRecord(args, invocation)); - }, null, null, null)); + }, null, null, null, null)); } } diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/MultiReturnTools$$CopilotToolMeta.java b/java/src/test/java/com/github/copilot/rpc/fixtures/MultiReturnTools$$CopilotToolMeta.java index e1ac5c38dd..571db8e7cb 100644 --- a/java/src/test/java/com/github/copilot/rpc/fixtures/MultiReturnTools$$CopilotToolMeta.java +++ b/java/src/test/java/com/github/copilot/rpc/fixtures/MultiReturnTools$$CopilotToolMeta.java @@ -16,14 +16,14 @@ public List definitions(MultiReturnTools instance, ObjectMapper return List.of(new ToolDefinition("string_method", "Returns a string", Map.of("type", "object", "properties", Map.of(), "required", List.of()), invocation -> { return CompletableFuture.completedFuture(instance.stringMethod()); - }, null, null, null), new ToolDefinition("void_method", "Void method", + }, null, null, null, null), new ToolDefinition("void_method", "Void method", Map.of("type", "object", "properties", Map.of(), "required", List.of()), invocation -> { instance.voidMethod(); return CompletableFuture.completedFuture("Success"); - }, null, null, null), + }, null, null, null, null), new ToolDefinition("async_method", "Async method", Map.of("type", "object", "properties", Map.of(), "required", List.of()), invocation -> { return instance.asyncMethod().thenApply(r -> (Object) r); - }, null, null, null)); + }, null, null, null, null)); } } diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/OptionalParamTools$$CopilotToolMeta.java b/java/src/test/java/com/github/copilot/rpc/fixtures/OptionalParamTools$$CopilotToolMeta.java index df6c39fd66..75fde6bb3c 100644 --- a/java/src/test/java/com/github/copilot/rpc/fixtures/OptionalParamTools$$CopilotToolMeta.java +++ b/java/src/test/java/com/github/copilot/rpc/fixtures/OptionalParamTools$$CopilotToolMeta.java @@ -39,7 +39,7 @@ public List definitions(OptionalParamTools instance, ObjectMappe Object titleRaw = args.get("title"); Optional title = titleRaw != null ? Optional.of((String) titleRaw) : Optional.empty(); return CompletableFuture.completedFuture(instance.greetWithTitle(name, title)); - }, null, null, null), + }, null, null, null, null), new ToolDefinition("multiply", "Multiply with optional factor", Map.of("type", "object", "properties", Map.ofEntries( @@ -58,7 +58,7 @@ public List definitions(OptionalParamTools instance, ObjectMappe ? OptionalInt.of(((Number) factorRaw).intValue()) : OptionalInt.empty(); return CompletableFuture.completedFuture(instance.multiply(base, factor)); - }, null, null, null), + }, null, null, null, null), new ToolDefinition("scale", "Scale with optional ratio", Map.of("type", "object", "properties", Map.ofEntries( @@ -77,7 +77,7 @@ public List definitions(OptionalParamTools instance, ObjectMappe ? OptionalDouble.of(((Number) ratioRaw).doubleValue()) : OptionalDouble.empty(); return CompletableFuture.completedFuture(instance.scale(value, ratio)); - }, null, null, null), + }, null, null, null, null), new ToolDefinition("offset", "Offset with optional delta", Map.of("type", "object", "properties", Map.ofEntries( @@ -96,6 +96,6 @@ public List definitions(OptionalParamTools instance, ObjectMappe ? OptionalLong.of(((Number) deltaRaw).longValue()) : OptionalLong.empty(); return CompletableFuture.completedFuture(instance.offset(base, delta)); - }, null, null, null)); + }, null, null, null, null)); } } diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/OverrideTools$$CopilotToolMeta.java b/java/src/test/java/com/github/copilot/rpc/fixtures/OverrideTools$$CopilotToolMeta.java index 127dc922b4..2d37204f82 100644 --- a/java/src/test/java/com/github/copilot/rpc/fixtures/OverrideTools$$CopilotToolMeta.java +++ b/java/src/test/java/com/github/copilot/rpc/fixtures/OverrideTools$$CopilotToolMeta.java @@ -34,6 +34,6 @@ public List definitions(OverrideTools instance, ObjectMapper map Map args = invocation.getArguments(); String pattern = (String) args.get("pattern"); return CompletableFuture.completedFuture(instance.customGrep(pattern)); - }, Boolean.TRUE, null, null)); + }, Boolean.TRUE, null, null, null)); } } diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/SimpleTools$$CopilotToolMeta.java b/java/src/test/java/com/github/copilot/rpc/fixtures/SimpleTools$$CopilotToolMeta.java index 0b52bd1ef2..ac38d0cce2 100644 --- a/java/src/test/java/com/github/copilot/rpc/fixtures/SimpleTools$$CopilotToolMeta.java +++ b/java/src/test/java/com/github/copilot/rpc/fixtures/SimpleTools$$CopilotToolMeta.java @@ -30,7 +30,9 @@ public List definitions(SimpleTools instance, ObjectMapper mappe Map args = invocation.getArguments(); String name = (String) args.get("name"); return CompletableFuture.completedFuture(instance.greetUser(name)); - }, null, null, null), + }, null, null, null, + Map.of("github.com/copilot:safeForTelemetry", + Map.of("name", true, "inputsNames", false))), new ToolDefinition("add_numbers", "Adds two numbers together", Map.of("type", "object", "properties", Map.ofEntries( @@ -46,6 +48,6 @@ public List definitions(SimpleTools instance, ObjectMapper mappe int a = ((Number) args.get("a")).intValue(); int b = ((Number) args.get("b")).intValue(); return CompletableFuture.completedFuture(instance.addNumbers(a, b)); - }, null, null, null)); + }, null, null, null, null)); } } diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/SimpleTools.java b/java/src/test/java/com/github/copilot/rpc/fixtures/SimpleTools.java index 5bc3d841e5..814b3883c3 100644 --- a/java/src/test/java/com/github/copilot/rpc/fixtures/SimpleTools.java +++ b/java/src/test/java/com/github/copilot/rpc/fixtures/SimpleTools.java @@ -12,7 +12,10 @@ */ public class SimpleTools { - @CopilotTool("Greets a user by name") + @CopilotTool(value = "Greets a user by name", metadata = { + @CopilotTool.MetadataEntry(key = "github.com/copilot:safeForTelemetry", value = @CopilotTool.MetadataValue(flags = { + @CopilotTool.MetadataFlag(name = "name", value = true), + @CopilotTool.MetadataFlag(name = "inputsNames", value = false)}))}) public String greetUser(@CopilotToolParam(value = "The user's name", required = true) String name) { return "Hello, " + name + "!"; } diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/StaticInvocationTools$$CopilotToolMeta.java b/java/src/test/java/com/github/copilot/rpc/fixtures/StaticInvocationTools$$CopilotToolMeta.java index c2fd7d7f6c..2535d671e8 100644 --- a/java/src/test/java/com/github/copilot/rpc/fixtures/StaticInvocationTools$$CopilotToolMeta.java +++ b/java/src/test/java/com/github/copilot/rpc/fixtures/StaticInvocationTools$$CopilotToolMeta.java @@ -24,6 +24,6 @@ public List definitions(StaticInvocationTools instance, ObjectMa Map args = invocation.getArguments(); String phase = (String) args.get("phase"); return CompletableFuture.completedFuture(StaticInvocationTools.reportStatic(phase, invocation)); - }, null, null, null)); + }, null, null, null, null)); } } diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/StaticTools$$CopilotToolMeta.java b/java/src/test/java/com/github/copilot/rpc/fixtures/StaticTools$$CopilotToolMeta.java index 842547b68d..a0c6e66855 100644 --- a/java/src/test/java/com/github/copilot/rpc/fixtures/StaticTools$$CopilotToolMeta.java +++ b/java/src/test/java/com/github/copilot/rpc/fixtures/StaticTools$$CopilotToolMeta.java @@ -32,6 +32,6 @@ public List definitions(StaticTools instance, ObjectMapper mappe // Mimics what the processor now generates for static methods: // QualifiedClassName.method(...) instead of instance.method(...) return CompletableFuture.completedFuture(StaticTools.greet(name)); - }, null, null, null)); + }, null, null, null, null)); } } diff --git a/java/src/test/java/com/github/copilot/tool/CopilotToolProcessorTest.java b/java/src/test/java/com/github/copilot/tool/CopilotToolProcessorTest.java index c2bda9f9ad..574d3acaa0 100644 --- a/java/src/test/java/com/github/copilot/tool/CopilotToolProcessorTest.java +++ b/java/src/test/java/com/github/copilot/tool/CopilotToolProcessorTest.java @@ -212,6 +212,88 @@ public String doSomething(@CopilotToolParam("Input") String input) { "Expected completedFuture wrapping for String return, got:\n" + generated); } + @Test + void generatesMetadata_withNestedFlags() { + String source = """ + package test; + import com.github.copilot.tool.CopilotTool; + import com.github.copilot.tool.CopilotToolParam; + public class MetaTools { + @CopilotTool(value = "Reports phase", metadata = { + @CopilotTool.MetadataEntry( + key = "github.com/copilot:safeForTelemetry", + value = @CopilotTool.MetadataValue(flags = { + @CopilotTool.MetadataFlag(name = "name", value = true), + @CopilotTool.MetadataFlag(name = "inputsNames", value = false) + })) + }) + public String reportPhase(@CopilotToolParam("Phase") String phase) { + return phase; + } + } + """; + + CompilationResult result = compileWithProcessor(List.of(inMemorySource("test.MetaTools", source))); + assertNoErrors(result); + String generated = result.getGeneratedSource("test.MetaTools$$CopilotToolMeta"); + assertTrue(generated.contains("Map.of(\"github.com/copilot:safeForTelemetry\""), + "Expected typed metadata map, got:\n" + generated); + assertTrue(generated.contains("Map.of(\"name\", true, \"inputsNames\", false)"), + "Expected nested flag map, got:\n" + generated); + } + + @Test + void generatesNullMetadata_whenAbsent() { + String source = """ + package test; + import com.github.copilot.tool.CopilotTool; + import com.github.copilot.tool.CopilotToolParam; + public class PlainTools { + @CopilotTool("Plain tool") + public String doSomething(@CopilotToolParam("Input") String input) { + return input; + } + } + """; + + CompilationResult result = compileWithProcessor(List.of(inMemorySource("test.PlainTools", source))); + assertNoErrors(result); + String generated = result.getGeneratedSource("test.PlainTools$$CopilotToolMeta"); + assertFalse(generated.contains("Map.of("), + "Expected no metadata map for a tool without metadata, got:\n" + generated); + assertTrue(generated.contains(" null\n )"), + "Expected metadata constructor argument to be null when metadata is absent, got:\n" + generated); + } + + @Test + void generatesMetadata_alongsideOtherFlags() { + String source = """ + package test; + import com.github.copilot.tool.CopilotTool; + import com.github.copilot.rpc.ToolDefer; + import com.github.copilot.tool.CopilotToolParam; + public class ComboTools { + @CopilotTool(value = "Combo", name = "combo", overridesBuiltInTool = true, + skipPermission = true, defer = ToolDefer.NEVER, + metadata = { + @CopilotTool.MetadataEntry(key = "k", + value = @CopilotTool.MetadataValue(bool = true)) + }) + public String doSomething(@CopilotToolParam("Input") String input) { + return input; + } + } + """; + + CompilationResult result = compileWithProcessor(List.of(inMemorySource("test.ComboTools", source))); + assertNoErrors(result); + String generated = result.getGeneratedSource("test.ComboTools$$CopilotToolMeta"); + assertTrue(generated.contains("Boolean.TRUE"), "Expected overrides/skip flags, got:\n" + generated); + assertTrue(generated.contains("ToolDefer.NEVER"), "Expected defer, got:\n" + generated); + assertTrue(generated.contains("Map.of(\"k\", true)"), + "Expected scalar bool metadata, got:\n" + generated); + } + @Test void generatesCorrectCode_forVoidReturnType() { String source = """ diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index 65784569cc..002836ad33 100644 --- a/nodejs/src/client.ts +++ b/nodejs/src/client.ts @@ -1521,6 +1521,7 @@ export class CopilotClient { overridesBuiltInTool: tool.overridesBuiltInTool, skipPermission: tool.skipPermission, defer: tool.defer, + metadata: tool.metadata, })), toolSearch: config.toolSearch, canvases: config.canvases?.map((canvas) => canvas.declaration), @@ -1740,6 +1741,7 @@ export class CopilotClient { overridesBuiltInTool: tool.overridesBuiltInTool, skipPermission: tool.skipPermission, defer: tool.defer, + metadata: tool.metadata, })), toolSearch: config.toolSearch, canvases: config.canvases?.map((canvas) => canvas.declaration), diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index 12ab0860b0..7fecef2e6c 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -640,6 +640,14 @@ export interface Tool { * Optional; defaults to `"auto"`. */ defer?: "auto" | "never"; + /** + * Opaque, host-defined metadata associated with the tool definition. + * + * Keys are namespaced and are not part of the stable public API. Values are + * not interpreted and may be recognized to inform host-specific behavior. + * Unknown keys are preserved and round-tripped untouched. + */ + metadata?: Record; } /** @@ -655,6 +663,7 @@ export function defineTool( overridesBuiltInTool?: boolean; skipPermission?: boolean; defer?: "auto" | "never"; + metadata?: Record; } ): Tool { return { name, ...config }; diff --git a/nodejs/test/client.test.ts b/nodejs/test/client.test.ts index 07be2b95e3..2d93e9e075 100644 --- a/nodejs/test/client.test.ts +++ b/nodejs/test/client.test.ts @@ -433,6 +433,71 @@ describe("CopilotClient", () => { expect(resumePayload.contextTier).toBe("default"); }); + it("forwards tool metadata verbatim in session.create and session.resume", async () => { + const client = new CopilotClient(); + await client.start(); + onTestFinished(() => client.forceStop()); + + const spy = vi + .spyOn((client as any).connection!, "sendRequest") + .mockImplementation(async (method: string, params: any) => { + if (method === "session.create") return { sessionId: params.sessionId }; + if (method === "session.resume") return { sessionId: params.sessionId }; + throw new Error(`Unexpected method: ${method}`); + }); + + const metadata = { + "github.com/copilot:safeForTelemetry": { name: true, inputsNames: false }, + }; + const tool = { + name: "my_tool", + description: "a tool", + parameters: { type: "object", properties: {} }, + metadata, + }; + + const session = await client.createSession({ + onPermissionRequest: approveAll, + tools: [tool], + }); + await client.resumeSession(session.sessionId, { + onPermissionRequest: approveAll, + tools: [tool], + }); + + const createPayload = spy.mock.calls.find( + ([method]) => method === "session.create" + )![1] as any; + const resumePayload = spy.mock.calls.find( + ([method]) => method === "session.resume" + )![1] as any; + expect(createPayload.tools[0].metadata).toEqual(metadata); + expect(resumePayload.tools[0].metadata).toEqual(metadata); + }); + + it("omits tool metadata from session.create when unset", async () => { + const client = new CopilotClient(); + await client.start(); + onTestFinished(() => client.forceStop()); + + const spy = vi + .spyOn((client as any).connection!, "sendRequest") + .mockImplementation(async (method: string, params: any) => { + if (method === "session.create") return { sessionId: params.sessionId }; + throw new Error(`Unexpected method: ${method}`); + }); + + await client.createSession({ + onPermissionRequest: approveAll, + tools: [{ name: "my_tool", description: "a tool" }], + }); + + const createPayload = spy.mock.calls.find( + ([method]) => method === "session.create" + )![1] as any; + expect(createPayload.tools[0].metadata).toBeUndefined(); + }); + it("forwards new session options in session.create and session.resume", async () => { const client = new CopilotClient(); await client.start(); diff --git a/python/copilot/client.py b/python/copilot/client.py index 94eca4f89c..a56748478d 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -2158,6 +2158,8 @@ async def create_session( definition["skipPermission"] = True if tool.defer is not None: definition["defer"] = tool.defer + if tool.metadata is not None: + definition["metadata"] = tool.metadata tool_defs.append(definition) # Empty-mode validation and normalization @@ -2832,6 +2834,8 @@ async def resume_session( definition["skipPermission"] = True if tool.defer is not None: definition["defer"] = tool.defer + if tool.metadata is not None: + definition["metadata"] = tool.metadata tool_defs.append(definition) # Empty-mode validation and normalization diff --git a/python/copilot/tools.py b/python/copilot/tools.py index 648dff3411..b96e303e3b 100644 --- a/python/copilot/tools.py +++ b/python/copilot/tools.py @@ -75,6 +75,7 @@ class Tool: overrides_built_in_tool: bool = False skip_permission: bool = False defer: Literal["auto", "never"] | None = None + metadata: dict[str, Any] | None = None T = TypeVar("T", bound=BaseModel) @@ -89,6 +90,7 @@ def define_tool( overrides_built_in_tool: bool = False, skip_permission: bool = False, defer: Literal["auto", "never"] | None = None, + metadata: dict[str, Any] | None = None, ) -> Callable[[Callable[..., Any]], Tool]: pass @@ -103,6 +105,7 @@ def define_tool( overrides_built_in_tool: bool = False, skip_permission: bool = False, defer: Literal["auto", "never"] | None = None, + metadata: dict[str, Any] | None = None, ) -> Tool: pass @@ -117,6 +120,7 @@ def define_tool( overrides_built_in_tool: bool = False, skip_permission: bool = False, defer: Literal["auto", "never"] | None = None, + metadata: dict[str, Any] | None = None, ) -> Tool: pass @@ -130,6 +134,7 @@ def define_tool( overrides_built_in_tool: bool = False, skip_permission: bool = False, defer: Literal["auto", "never"] | None = None, + metadata: dict[str, Any] | None = None, ) -> Tool | Callable[[Callable[[Any, ToolInvocation], Any]], Tool]: """ Define a tool with automatic JSON schema generation from Pydantic models. @@ -178,6 +183,10 @@ def lookup_issue(params: LookupIssueParams) -> str: rather than always pre-loaded. When "auto", the tool can be deferred and surfaced through tool search. When "never", the tool is always pre-loaded. Optional; defaults to "auto". + metadata: Opaque, host-defined metadata associated with the tool definition. + Keys are namespaced and not part of the stable public API; values + are not interpreted and may be recognized to inform host-specific + behavior. Unknown keys are preserved. Returns: A Tool instance @@ -272,6 +281,7 @@ async def wrapped_handler(invocation: ToolInvocation) -> ToolResult: overrides_built_in_tool=overrides_built_in_tool, skip_permission=skip_permission, defer=defer, + metadata=metadata, ) # If handler is provided, call decorator immediately @@ -291,6 +301,7 @@ async def wrapped_handler(invocation: ToolInvocation) -> ToolResult: overrides_built_in_tool=overrides_built_in_tool, skip_permission=skip_permission, defer=defer, + metadata=metadata, ) # Otherwise return decorator for @define_tool(...) usage diff --git a/python/test_client.py b/python/test_client.py index 9cb00d809f..aac334cd4a 100644 --- a/python/test_client.py +++ b/python/test_client.py @@ -40,6 +40,7 @@ SessionEvent, SessionEventType, ) +from copilot.tools import Tool from e2e.testharness import CLI_PATH @@ -567,6 +568,46 @@ async def mock_request(method, params, **kwargs): finally: await client.force_stop() + @pytest.mark.asyncio + async def test_create_and_resume_session_forward_tool_metadata(self): + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) + await client.start() + try: + captured = {} + + async def mock_request(method, params, **kwargs): + captured[method] = params + if method in ("session.create", "session.resume"): + result = {"sessionId": params.get("sessionId") or "session-1"} + callback = kwargs.get("on_response_inline") + if callback is not None: + callback(result) + return result + return {} + + client._client.request = mock_request + metadata = {"github.com/copilot:safeForTelemetry": {"name": True, "inputsNames": False}} + tool = Tool(name="my_tool", description="a tool", metadata=metadata) + plain_tool = Tool(name="plain_tool", description="a tool") + + session = await client.create_session( + on_permission_request=PermissionHandler.approve_all, + tools=[tool, plain_tool], + ) + await client.resume_session( + session.session_id, + on_permission_request=PermissionHandler.approve_all, + tools=[tool], + ) + + create_tools = captured["session.create"]["tools"] + assert create_tools[0]["metadata"] == metadata + # Omitted when unset. + assert "metadata" not in create_tools[1] + assert captured["session.resume"]["tools"][0]["metadata"] == metadata + finally: + await client.force_stop() + @pytest.mark.asyncio async def test_create_and_resume_session_forward_canvas_provider(self): client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) diff --git a/rust/src/types.rs b/rust/src/types.rs index 81eac81ca2..b34d8fff45 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -352,6 +352,12 @@ pub struct Tool { /// runtime decide. #[serde(default, skip_serializing_if = "Option::is_none")] pub defer: Option, + /// Opaque, host-defined metadata associated with the tool definition. + /// Keys are namespaced and not part of the stable public API; values are + /// not interpreted and may be recognized to inform host-specific behavior. + /// Unknown keys are preserved and round-tripped untouched. + #[serde(default, skip_serializing_if = "IndexMap::is_empty")] + pub metadata: IndexMap, /// Optional runtime implementation. When `Some`, the SDK dispatches /// matching `external_tool.requested` broadcasts to this handler. /// When `None`, the tool is declaration-only. @@ -471,6 +477,13 @@ impl Tool { self } + /// Set opaque, host-defined metadata for the tool. Keys are namespaced and + /// not part of the stable public API. Replaces any previously-set metadata. + pub fn with_metadata(mut self, metadata: IndexMap) -> Self { + self.metadata = metadata; + self + } + /// Attach a runtime implementation. The SDK will dispatch matching /// `external_tool.requested` broadcasts to `handler` for this tool's /// name. Without a handler the tool is declaration-only. @@ -499,6 +512,7 @@ impl std::fmt::Debug for Tool { .field("overrides_built_in_tool", &self.overrides_built_in_tool) .field("skip_permission", &self.skip_permission) .field("defer", &self.defer) + .field("metadata", &self.metadata) .field( "handler", &self.handler.as_ref().map(|_| "").unwrap_or("None"), @@ -5444,6 +5458,32 @@ mod tests { assert!(value.get("defer").is_none()); } + #[test] + fn tool_metadata_serialization() { + use indexmap::IndexMap; + + let mut metadata = IndexMap::new(); + metadata.insert( + "github.com/copilot:safeForTelemetry".to_string(), + json!({ "name": true, "inputsNames": false }), + ); + let tool = Tool::new("lookup").with_metadata(metadata); + let value = serde_json::to_value(&tool).unwrap(); + assert_eq!( + value + .get("metadata") + .unwrap() + .get("github.com/copilot:safeForTelemetry") + .unwrap(), + &json!({ "name": true, "inputsNames": false }) + ); + + // Empty metadata is omitted on the wire. + let plain = Tool::new("plain"); + let value = serde_json::to_value(&plain).unwrap(); + assert!(value.get("metadata").is_none()); + } + #[test] fn custom_agent_config_builder_with_model() { let agent = CustomAgentConfig::new("my-agent", "You are helpful.") From a0239348bb8c9fec418f63a1eddd5e1717553dc4 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 15 Jul 2026 22:14:54 -0400 Subject: [PATCH 6/7] Avoid Windows in-process test teardown deadlock (#1997) * Instrument Node in-process test stalls Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6e902b9a-5527-4a32-a5a3-e0bf5bfef3f7 * Trace runtime and proxy during Node stalls Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6e902b9a-5527-4a32-a5a3-e0bf5bfef3f7 * Create focused Windows in-process stress run Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6e902b9a-5527-4a32-a5a3-e0bf5bfef3f7 * Measure Windows test resource pressure Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6e902b9a-5527-4a32-a5a3-e0bf5bfef3f7 * Preserve runtime diagnostics on failure Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6e902b9a-5527-4a32-a5a3-e0bf5bfef3f7 * Trace Windows session database locks Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6e902b9a-5527-4a32-a5a3-e0bf5bfef3f7 * Avoid perturbing runtime lock timing Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6e902b9a-5527-4a32-a5a3-e0bf5bfef3f7 * Avoid Windows in-process teardown deadlock Do not retry removal of the in-process runtime's session home while its Vitest worker still owns a locked session database. Retrying until the hook timeout prevents the worker from exiting and releasing the lock. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6e902b9a-5527-4a32-a5a3-e0bf5bfef3f7 * Clarify Windows teardown deadlock Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6e902b9a-5527-4a32-a5a3-e0bf5bfef3f7 --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- nodejs/test/e2e/harness/sdkTestContext.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/nodejs/test/e2e/harness/sdkTestContext.ts b/nodejs/test/e2e/harness/sdkTestContext.ts index 624450e47c..bf62db4826 100644 --- a/nodejs/test/e2e/harness/sdkTestContext.ts +++ b/nodejs/test/e2e/harness/sdkTestContext.ts @@ -293,7 +293,15 @@ export async function createSdkTestContext({ afterAll(async () => { await copilotClient.stop(); await openAiEndpoint.stop(anyTestFailed); - await rmDir("remove e2e test copilotHomeDir", copilotHomeDir); + // On Windows, this Vitest worker can retain the in-process runtime's session.db + // lock until the worker exits. Retrying from its afterAll hook cannot succeed: + // the hook waits for the lock, while the lock cannot clear until the hook returns + // and lets the worker exit. + await rmDir( + "remove e2e test copilotHomeDir", + copilotHomeDir, + isInProcess && process.platform === "win32" ? 1 : 30 + ); await rmDir("remove e2e test homeDir", homeDir); await rmDir("remove e2e test workDir", workDir); }); @@ -320,14 +328,14 @@ function getTrafficCapturePath(testContext: TestContext): string { return join(SNAPSHOTS_DIR, testFileName, `${taskNameAsFilename}.yaml`); } -async function rmDir(message: string, path: string): Promise { +async function rmDir(message: string, path: string, maxTries = 30): Promise { // Use longer retries to tolerate Windows holding SQLite session-store.db // open briefly after the CLI subprocess exits. If the temp dir still can't // be removed (e.g. CLI background writer racing with cleanup), warn and // continue rather than failing the whole test run — the OS / CI runner // will reclaim the temp dir on shutdown. try { - await retry(message, () => rm(path, { recursive: true, force: true }), 30, 1000); + await retry(message, () => rm(path, { recursive: true, force: true }), maxTries, 1000); } catch (error) { console.warn( `WARN: ${message} failed; leaving temp dir for OS cleanup: ${formatError(error)}` From d95cfacc5d3ca13ce8cc9a3ece2746134da5c50c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 16 Jul 2026 09:49:10 -0400 Subject: [PATCH 7/7] Update @github/copilot to 1.0.71 (#1998) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update @github/copilot to 1.0.71 - Updated nodejs and test harness dependencies - Re-ran code generators - Formatted generated code * Route Python hooks.invoke through generated client-global handler CLI 1.0.71 promoted hooks.invoke to a client-global RPC method with a generated HooksHandler interface. The handwritten SDK still registered its own hooks.invoke handler, which only avoided colliding with the generated one because global handlers were skipped when no LLM/telemetry adapter was set. Make the wiring intentional: add _HooksAdapter implementing the generated HooksHandler protocol (routing HookInvokeRequest.sessionId to the matching session's dispatcher), always register the client-global handlers with the hooks adapter, and remove the redundant handwritten hooks.invoke registrations and dead client-level handler. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ae226b7f-caf9-46f3-b8c5-b9a21c5d7951 * Route Node hooks.invoke through generated client-global handler CLI 1.0.71 promoted hooks.invoke to a client-global RPC method with a generated HooksHandler interface. The handwritten SDK registered its own hooks.invoke handler on the connection, which the generated registerClientGlobalApiHandlers then shadowed with an unwired handler that threw "No hooks client-global handler registered" — so hooks never fired. Wire the existing handleHooksInvoke routing into the generated clientGlobalHandlers.hooks slot and drop the redundant handwritten connection.onRequest("hooks.invoke") registration. Behavior is unchanged; the dispatcher and its payload validation are reused as-is. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ae226b7f-caf9-46f3-b8c5-b9a21c5d7951 * Route Go hooks.invoke through generated client-global handler CLI 1.0.71 promoted hooks.invoke to a client-global RPC method whose generated registration installs a hooks.invoke handler that rejects all invocations unless the Hooks slot is populated. Whenever an LLM inference or telemetry adapter was configured, that generated handler overrode the handwritten hooks.invoke registration and hooks stopped firing (e.g. the sub-agent hook test). Always register the client-global handlers with a hooksAdapter that delegates to the existing per-session dispatcher, and drop the redundant handwritten hooks.invoke registration. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ae226b7f-caf9-46f3-b8c5-b9a21c5d7951 * Fix Rust hook input deserialization for float timestamps The Copilot CLI serializes hook input `timestamp` as a JSON float (e.g. `1784203878038.0`). Rust's hand-authored hook input structs typed `timestamp` as `i64`, so `serde_json::from_value` rejected the float, `dispatch_hook` returned an error, and the session handler fell back to an empty `{ "output": {} }` response. Hooks therefore never fired: e.g. a preToolUse deny was dropped, the CLI executed the tool, and the replayed conversation diverged ("No cached response" -> 500). Other SDKs tolerate this incidentally (Go decodes `input` into `any` and re-marshals, dropping the `.0`); Rust decodes strictly. Type the hook input `timestamp` fields as `f64` to match the shape the runtime sends. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ae226b7f-caf9-46f3-b8c5-b9a21c5d7951 * Fix .NET build break and float hook timestamps for hooks.invoke CLI 1.0.71 promoted hooks.invoke to an internal client-global RPC method. The C# codegen still emitted its internal request/result DTOs behind a public IHooksHandler surface, producing CS0050/CS0051 inconsistent-accessibility errors that broke the entire .NET build. It also registered a second, unwired hooks.invoke handler that would shadow the working handwritten one. Filter internal client-global and client-session methods in the C# code generator so no generated interface, handler property, or RPC registration is emitted for internal methods like hooks.invoke. The handwritten SetLocalRpcMethod(hooks.invoke, ...) registration continues to serve hooks. This mirrors, for .NET's static typing, the routing fixes already applied to Node, Python, and Go. Also tolerate hook timestamp epoch milliseconds encoded as either JSON integers or floats in UnixMillisecondsDateTimeOffsetConverter, covering the CLI 1.0.71 float serialization (matching the Rust fix). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 94a20428-6b0e-4733-a354-0abf2d186320 --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Steve Sanderson Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Stephen Toub --- dotnet/src/Generated/Rpc.cs | 112 ++++++- dotnet/src/Generated/SessionEvents.cs | 197 +++++++++++- ...UnixMillisecondsDateTimeOffsetConverter.cs | 10 +- go/client.go | 64 +++- go/rpc/zrpc.go | 137 ++++++++- go/rpc/zsession_encoding.go | 12 + go/rpc/zsession_events.go | 267 ++++++++++------ go/zsession_events.go | 10 + java/pom.xml | 2 +- java/scripts/codegen/package-lock.json | 72 ++--- java/scripts/codegen/package.json | 2 +- .../AssistantServerToolProgressEvent.java | 45 +++ .../github/copilot/generated/HeaderEntry.java | 29 ++ .../ManagedSettingsResolvedSource.java | 37 +++ .../generated/McpOauthHttpResponse.java | 32 ++ .../generated/McpOauthRequiredEvent.java | 2 + .../generated/McpPromptsListChangedEvent.java | 2 +- .../McpResourcesListChangedEvent.java | 2 +- .../generated/McpToolsListChangedEvent.java | 2 +- .../copilot/generated/SessionEvent.java | 4 + .../SessionManagedSettingsResolvedEvent.java | 54 ++++ .../generated/rpc/HookInvokeRequest.java | 28 ++ .../copilot/generated/rpc/HookType.java | 63 ++++ .../generated/rpc/HooksInvokeResult.java | 29 ++ .../copilot/generated/rpc/McpToolUi.java | 30 ++ .../generated/rpc/McpToolUiVisibility.java | 35 +++ .../copilot/generated/rpc/McpTools.java | 6 +- .../generated/rpc/SessionModelListResult.java | 2 + .../rpc/SessionModelPriceCategory.java | 27 ++ .../rpc/SessionOptionsUpdateResult.java | 4 +- nodejs/package-lock.json | 72 ++--- nodejs/package.json | 2 +- nodejs/samples/package-lock.json | 2 +- nodejs/src/client.ts | 14 +- nodejs/src/generated/rpc.ts | 137 ++++++++- nodejs/src/generated/session-events.ts | 162 +++++++++- nodejs/test/client.test.ts | 4 +- python/copilot/client.py | 58 ++-- python/copilot/generated/rpc.py | 284 ++++++++++++++---- python/copilot/generated/session_events.py | 156 +++++++++- python/test_client.py | 27 +- rust/src/generated/api_types.rs | 148 ++++++++- rust/src/generated/rpc.rs | 2 +- rust/src/generated/session_events.rs | 112 ++++++- rust/src/hooks.rs | 32 +- rust/tests/e2e/hooks_extended.rs | 10 +- rust/tests/e2e/pre_mcp_tool_call_hook.rs | 2 +- scripts/codegen/csharp.ts | 17 +- test/harness/package-lock.json | 72 ++--- test/harness/package.json | 2 +- 50 files changed, 2253 insertions(+), 380 deletions(-) create mode 100644 java/src/generated/java/com/github/copilot/generated/AssistantServerToolProgressEvent.java create mode 100644 java/src/generated/java/com/github/copilot/generated/HeaderEntry.java create mode 100644 java/src/generated/java/com/github/copilot/generated/ManagedSettingsResolvedSource.java create mode 100644 java/src/generated/java/com/github/copilot/generated/McpOauthHttpResponse.java create mode 100644 java/src/generated/java/com/github/copilot/generated/SessionManagedSettingsResolvedEvent.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/HookInvokeRequest.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/HookType.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/HooksInvokeResult.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/McpToolUi.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/McpToolUiVisibility.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/SessionModelPriceCategory.java diff --git a/dotnet/src/Generated/Rpc.cs b/dotnet/src/Generated/Rpc.cs index dfa2d3c9db..bf73bdfaf7 100644 --- a/dotnet/src/Generated/Rpc.cs +++ b/dotnet/src/Generated/Rpc.cs @@ -4241,6 +4241,19 @@ internal sealed class ModelSetReasoningEffortRequest public string SessionId { get; set; } = string.Empty; } +/// Cost-category metadata for a CAPI model. +[Experimental(Diagnostics.Experimental)] +public sealed class SessionModelPriceCategory +{ + /// Gets or sets the id value. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// Gets or sets the priceCategory value. + [JsonPropertyName("priceCategory")] + public ModelPickerPriceCategory PriceCategory { get; set; } +} + /// The list of models available to this session. [Experimental(Diagnostics.Experimental)] public sealed class SessionModelList @@ -4249,6 +4262,10 @@ public sealed class SessionModelList [JsonPropertyName("list")] public IList List { get => field ??= []; set; } + /// Cost categories for the full CAPI catalog, including picker-disabled models that Auto may select. Metadata only; entries absent from `list` are not manually selectable. + [JsonPropertyName("modelPriceCategories")] + public IList? ModelPriceCategories { get; set; } + /// Per-quota snapshots returned alongside the model list, keyed by quota type. [JsonPropertyName("quotaSnapshots")] public IDictionary? QuotaSnapshots { get; set; } @@ -5725,7 +5742,20 @@ internal sealed class SessionMcpListRequest public string SessionId { get; set; } = string.Empty; } -/// MCP tool metadata with tool name and optional description. +/// Normalized MCP Apps discovery metadata from a tool's `_meta.ui` block. +[Experimental(Diagnostics.Experimental)] +public sealed class McpToolUi +{ + /// URI of the tool's MCP App resource, typically a `ui://` resource identifier. Use `session.mcp.resources.read` to fetch its HTML and resource metadata. + [JsonPropertyName("resourceUri")] + public string? ResourceUri { get; set; } + + /// Tool visibility advertised by the server. When absent, MCP Apps defaults apply. + [JsonPropertyName("visibility")] + public IList? Visibility { get; set; } +} + +/// MCP tool metadata with tool name, optional description, and normalized MCP Apps discovery metadata. [Experimental(Diagnostics.Experimental)] public sealed class McpTools { @@ -5736,6 +5766,10 @@ public sealed class McpTools /// Tool name. [JsonPropertyName("name")] public string Name { get; set; } = string.Empty; + + /// Normalized MCP Apps discovery metadata. An empty object indicates that a valid `_meta.ui` block was present without recognized fields. + [JsonPropertyName("ui")] + public McpToolUi? Ui { get; set; } } /// Tools exposed by the connected MCP server. Throws when the server is not connected. @@ -7136,6 +7170,10 @@ internal sealed class ProviderAddRequest [Experimental(Diagnostics.Experimental)] public sealed class SessionUpdateOptionsResult { + /// Number of hooks loaded from installed plugins, returned when installedPlugins is updated. + [JsonPropertyName("pluginHookCount")] + public long? PluginHookCount { get; set; } + /// Whether the operation succeeded. [JsonPropertyName("success")] public bool Success { get; set; } @@ -15544,6 +15582,69 @@ public override void Write(Utf8JsonWriter writer, TaskShellInfoAttachmentMode va } +/// Consumer allowed to call an MCP tool. +[Experimental(Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct McpToolUiVisibility : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public McpToolUiVisibility(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// The model may call the tool. + public static McpToolUiVisibility Model { get; } = new("model"); + + /// An MCP App view may call the tool. + public static McpToolUiVisibility App { get; } = new("app"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpToolUiVisibility left, McpToolUiVisibility right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpToolUiVisibility left, McpToolUiVisibility right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is McpToolUiVisibility other && Equals(other); + + /// + public bool Equals(McpToolUiVisibility other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override McpToolUiVisibility Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, McpToolUiVisibility value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpToolUiVisibility)); + } + } +} + + /// Outcome of the sampling inference. 'success' produced a response; 'failure' encountered an error (including agent-side rejection by content filter or criteria); 'cancelled' the caller cancelled this execution via cancelSamplingExecution. [Experimental(Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] @@ -21428,7 +21529,7 @@ public async Task ListAsync(CancellationToken cancellationToken = return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.mcp.list", [request], cancellationToken); } - /// Lists the tools exposed by a connected MCP server on this session's host. + /// Lists the tools exposed by a connected MCP server on this session's host. This performs a live `tools/list` request. Tool UI metadata is returned independently of whether MCP Apps rendering is enabled for the session. /// Name of the connected MCP server whose tools to list. /// The to monitor for cancellation requests. The default is . /// Tools exposed by the connected MCP server. Throws when the server is not connected. @@ -23693,6 +23794,8 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(GitHub.Copilot.AssistantReasoningDeltaData), TypeInfoPropertyName = "SessionEventsAssistantReasoningDeltaData")] [JsonSerializable(typeof(GitHub.Copilot.AssistantReasoningDeltaEvent), TypeInfoPropertyName = "SessionEventsAssistantReasoningDeltaEvent")] [JsonSerializable(typeof(GitHub.Copilot.AssistantReasoningEvent), TypeInfoPropertyName = "SessionEventsAssistantReasoningEvent")] +[JsonSerializable(typeof(GitHub.Copilot.AssistantServerToolProgressData), TypeInfoPropertyName = "SessionEventsAssistantServerToolProgressData")] +[JsonSerializable(typeof(GitHub.Copilot.AssistantServerToolProgressEvent), TypeInfoPropertyName = "SessionEventsAssistantServerToolProgressEvent")] [JsonSerializable(typeof(GitHub.Copilot.AssistantStreamingDeltaData), TypeInfoPropertyName = "SessionEventsAssistantStreamingDeltaData")] [JsonSerializable(typeof(GitHub.Copilot.AssistantStreamingDeltaEvent), TypeInfoPropertyName = "SessionEventsAssistantStreamingDeltaEvent")] [JsonSerializable(typeof(GitHub.Copilot.AssistantToolCallDeltaData), TypeInfoPropertyName = "SessionEventsAssistantToolCallDeltaData")] @@ -23793,6 +23896,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(GitHub.Copilot.GitHubRepoRef), TypeInfoPropertyName = "SessionEventsGitHubRepoRef")] [JsonSerializable(typeof(GitHub.Copilot.HandoffRepository), TypeInfoPropertyName = "SessionEventsHandoffRepository")] [JsonSerializable(typeof(GitHub.Copilot.HandoffSourceType), TypeInfoPropertyName = "SessionEventsHandoffSourceType")] +[JsonSerializable(typeof(GitHub.Copilot.HeaderEntry), TypeInfoPropertyName = "SessionEventsHeaderEntry")] [JsonSerializable(typeof(GitHub.Copilot.HookEndData), TypeInfoPropertyName = "SessionEventsHookEndData")] [JsonSerializable(typeof(GitHub.Copilot.HookEndError), TypeInfoPropertyName = "SessionEventsHookEndError")] [JsonSerializable(typeof(GitHub.Copilot.HookEndEvent), TypeInfoPropertyName = "SessionEventsHookEndEvent")] @@ -23800,6 +23904,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(GitHub.Copilot.HookProgressEvent), TypeInfoPropertyName = "SessionEventsHookProgressEvent")] [JsonSerializable(typeof(GitHub.Copilot.HookStartData), TypeInfoPropertyName = "SessionEventsHookStartData")] [JsonSerializable(typeof(GitHub.Copilot.HookStartEvent), TypeInfoPropertyName = "SessionEventsHookStartEvent")] +[JsonSerializable(typeof(GitHub.Copilot.ManagedSettingsResolvedSource), TypeInfoPropertyName = "SessionEventsManagedSettingsResolvedSource")] [JsonSerializable(typeof(GitHub.Copilot.McpAppToolCallCompleteData), TypeInfoPropertyName = "SessionEventsMcpAppToolCallCompleteData")] [JsonSerializable(typeof(GitHub.Copilot.McpAppToolCallCompleteError), TypeInfoPropertyName = "SessionEventsMcpAppToolCallCompleteError")] [JsonSerializable(typeof(GitHub.Copilot.McpAppToolCallCompleteEvent), TypeInfoPropertyName = "SessionEventsMcpAppToolCallCompleteEvent")] @@ -23814,6 +23919,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(GitHub.Copilot.McpOauthCompletedData), TypeInfoPropertyName = "SessionEventsMcpOauthCompletedData")] [JsonSerializable(typeof(GitHub.Copilot.McpOauthCompletedEvent), TypeInfoPropertyName = "SessionEventsMcpOauthCompletedEvent")] [JsonSerializable(typeof(GitHub.Copilot.McpOauthCompletionOutcome), TypeInfoPropertyName = "SessionEventsMcpOauthCompletionOutcome")] +[JsonSerializable(typeof(GitHub.Copilot.McpOauthHttpResponse), TypeInfoPropertyName = "SessionEventsMcpOauthHttpResponse")] [JsonSerializable(typeof(GitHub.Copilot.McpOauthRequestReason), TypeInfoPropertyName = "SessionEventsMcpOauthRequestReason")] [JsonSerializable(typeof(GitHub.Copilot.McpOauthRequiredData), TypeInfoPropertyName = "SessionEventsMcpOauthRequiredData")] [JsonSerializable(typeof(GitHub.Copilot.McpOauthRequiredEvent), TypeInfoPropertyName = "SessionEventsMcpOauthRequiredEvent")] @@ -24201,6 +24307,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(McpStartServerRequest))] [JsonSerializable(typeof(McpStartServersResult))] [JsonSerializable(typeof(McpStopServerRequest))] +[JsonSerializable(typeof(McpToolUi))] [JsonSerializable(typeof(McpTools))] [JsonSerializable(typeof(McpUnregisterExternalClientRequest))] [JsonSerializable(typeof(MetadataContextAttributionResult))] @@ -24454,6 +24561,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(SessionModeGetRequest))] [JsonSerializable(typeof(SessionModelGetCurrentRequest))] [JsonSerializable(typeof(SessionModelList))] +[JsonSerializable(typeof(SessionModelPriceCategory))] [JsonSerializable(typeof(SessionNameGetRequest))] [JsonSerializable(typeof(SessionOpenResult))] [JsonSerializable(typeof(SessionPlanDeleteRequest))] diff --git a/dotnet/src/Generated/SessionEvents.cs b/dotnet/src/Generated/SessionEvents.cs index c72c28bde0..41b87d06ff 100644 --- a/dotnet/src/Generated/SessionEvents.cs +++ b/dotnet/src/Generated/SessionEvents.cs @@ -32,6 +32,7 @@ namespace GitHub.Copilot; [JsonDerivedType(typeof(AssistantMessageStartEvent), "assistant.message_start")] [JsonDerivedType(typeof(AssistantReasoningEvent), "assistant.reasoning")] [JsonDerivedType(typeof(AssistantReasoningDeltaEvent), "assistant.reasoning_delta")] +[JsonDerivedType(typeof(AssistantServerToolProgressEvent), "assistant.server_tool_progress")] [JsonDerivedType(typeof(AssistantStreamingDeltaEvent), "assistant.streaming_delta")] [JsonDerivedType(typeof(AssistantToolCallDeltaEvent), "assistant.tool_call_delta")] [JsonDerivedType(typeof(AssistantTurnEndEvent), "assistant.turn_end")] @@ -90,6 +91,7 @@ namespace GitHub.Copilot; [JsonDerivedType(typeof(SessionHandoffEvent), "session.handoff")] [JsonDerivedType(typeof(SessionIdleEvent), "session.idle")] [JsonDerivedType(typeof(SessionInfoEvent), "session.info")] +[JsonDerivedType(typeof(SessionManagedSettingsResolvedEvent), "session.managed_settings_resolved")] [JsonDerivedType(typeof(SessionMcpServerStatusChangedEvent), "session.mcp_server_status_changed")] [JsonDerivedType(typeof(SessionMcpServersLoadedEvent), "session.mcp_servers_loaded")] [JsonDerivedType(typeof(SessionModeChangedEvent), "session.mode_changed")] @@ -602,6 +604,19 @@ public sealed partial class AssistantIntentEvent : SessionEvent public required AssistantIntentData Data { get; set; } } +/// Live progress signal for a provider-hosted server tool (e.g. hosted web search) while it runs, before the finalized serverTools envelope lands on the terminal assistant.message. +/// Represents the assistant.server_tool_progress event. +public sealed partial class AssistantServerToolProgressEvent : SessionEvent +{ + /// + [JsonIgnore] + public override string Type => "assistant.server_tool_progress"; + + /// The assistant.server_tool_progress event payload. + [JsonPropertyName("data")] + public required AssistantServerToolProgressData Data { get; set; } +} + /// Assistant reasoning content for timeline display with complete thinking text. /// Represents the assistant.reasoning event. public sealed partial class AssistantReasoningEvent : SessionEvent @@ -1280,6 +1295,20 @@ public sealed partial class SessionAutoModeResolvedEvent : SessionEvent public required SessionAutoModeResolvedData Data { get; set; } } +/// Enterprise managed-settings resolution: the effective managed settings the session applied and where they came from, so SDK clients can show users what is enterprise-managed and by which authority. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted; for a session-independent pull, use the SDK `getManagedSettings()` API, which returns the identical payload. Managed settings have a single authoritative source, so the highest-authority present layer (server > device) wins wholesale; `bypassPermissionsDisabled` is deny-wins across layers. Marked experimental while the managed-settings surface stabilizes. +/// Represents the session.managed_settings_resolved event. +[Experimental(Diagnostics.Experimental)] +public sealed partial class SessionManagedSettingsResolvedEvent : SessionEvent +{ + /// + [JsonIgnore] + public override string Type => "session.managed_settings_resolved"; + + /// The session.managed_settings_resolved event payload. + [JsonPropertyName("data")] + public required SessionManagedSettingsResolvedData Data { get; set; } +} + /// SDK command registration change notification. /// Represents the commands.changed event. public sealed partial class CommandsChangedEvent : SessionEvent @@ -1410,7 +1439,7 @@ public sealed partial class SessionMcpServerStatusChangedEvent : SessionEvent public required SessionMcpServerStatusChangedData Data { get; set; } } -/// Payload of MCP `list_changed` notification events, emitted when an MCP server announces at runtime that one of its advertised lists changed. +/// Payload identifying the MCP server associated with a list change. /// Represents the mcp.tools.list_changed event. public sealed partial class McpToolsListChangedEvent : SessionEvent { @@ -1423,7 +1452,7 @@ public sealed partial class McpToolsListChangedEvent : SessionEvent public required McpToolsListChangedData Data { get; set; } } -/// Payload of MCP `list_changed` notification events, emitted when an MCP server announces at runtime that one of its advertised lists changed. +/// Payload identifying the MCP server associated with a list change. /// Represents the mcp.resources.list_changed event. public sealed partial class McpResourcesListChangedEvent : SessionEvent { @@ -1436,7 +1465,7 @@ public sealed partial class McpResourcesListChangedEvent : SessionEvent public required McpResourcesListChangedData Data { get; set; } } -/// Payload of MCP `list_changed` notification events, emitted when an MCP server announces at runtime that one of its advertised lists changed. +/// Payload identifying the MCP server associated with a list change. /// Represents the mcp.prompts.list_changed event. public sealed partial class McpPromptsListChangedEvent : SessionEvent { @@ -2512,6 +2541,22 @@ public sealed partial class AssistantIntentData public required string Intent { get; set; } } +/// Live progress signal for a provider-hosted server tool (e.g. hosted web search) while it runs, before the finalized serverTools envelope lands on the terminal assistant.message. +public sealed partial class AssistantServerToolProgressData +{ + /// Kind of hosted server tool that is running. Only `web_search` is emitted today. + [JsonPropertyName("kind")] + public required string Kind { get; set; } + + /// Position of the hosted tool call in the response output. Stable across the call's lifecycle events (unlike the provider's per-event item id, which CAPI rotates), so the host keys the live in-progress row on it. + [JsonPropertyName("outputIndex")] + public required long OutputIndex { get; set; } + + /// Lifecycle status of the hosted call: `in_progress`, `searching`, or `completed`. + [JsonPropertyName("status")] + public required string Status { get; set; } +} + /// Assistant reasoning content for timeline display with complete thinking text. public sealed partial class AssistantReasoningData { @@ -3564,6 +3609,11 @@ public sealed partial class SamplingCompletedData /// OAuth authentication request for an MCP server. public sealed partial class McpOauthRequiredData { + /// Raw HTTP response details from the OAuth auth challenge, as observed by the runtime. Header order and casing are transport-dependent, and duplicate header names may appear multiple times. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("httpResponse")] + public McpOauthHttpResponse? HttpResponse { get; set; } + /// Why the runtime is requesting host-provided OAuth credentials. [JsonPropertyName("reason")] public required McpOauthRequestReason Reason { get; set; } @@ -3850,6 +3900,40 @@ public sealed partial class SessionAutoModeResolvedData public AutoModeResolvedReasoningBucket? ReasoningBucket { get; set; } } +/// Enterprise managed-settings resolution: the effective managed settings the session applied and where they came from, so SDK clients can show users what is enterprise-managed and by which authority. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted; for a session-independent pull, use the SDK `getManagedSettings()` API, which returns the identical payload. Managed settings have a single authoritative source, so the highest-authority present layer (server > device) wins wholesale; `bypassPermissionsDisabled` is deny-wins across layers. Marked experimental while the managed-settings surface stabilizes. +[Experimental(Diagnostics.Experimental)] +public sealed partial class SessionManagedSettingsResolvedData +{ + /// Whether enterprise policy disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers, and forced on when `failClosed` is true. + [JsonPropertyName("bypassPermissionsDisabled")] + public required bool BypassPermissionsDisabled { get; set; } + + /// Whether the device (MDM/plist/registry/file) managed-settings layer was present. + [JsonPropertyName("deviceManaged")] + public required bool DeviceManaged { get; set; } + + /// Whether managed policy could not be determined (e.g. a failed server fetch) and the session fell back to the fail-closed restriction. When true, restrictions such as disabling bypass-permissions are enforced even though `settings` may be absent. + [JsonPropertyName("failClosed")] + public required bool FailClosed { get; set; } + + /// The setting keys under enterprise management in the effective managed settings (e.g. `model`, `enabledPlugins`, `permissions`). Empty when no managed settings are in force. + [JsonPropertyName("managedKeys")] + public required string[] ManagedKeys { get; set; } + + /// Whether the server (account/org) managed-settings layer was present. + [JsonPropertyName("serverManaged")] + public required bool ServerManaged { get; set; } + + /// The effective (resolved) managed settings values, so clients can render exactly what is enforced. Absent when no managed policy is in force. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("settings")] + public JsonElement? Settings { get; set; } + + /// Which channel supplied the effective managed settings (the winning layer), or `none` when no policy is in force. + [JsonPropertyName("source")] + public required ManagedSettingsResolvedSource Source { get; set; } +} + /// SDK command registration change notification. public sealed partial class CommandsChangedData { @@ -3981,7 +4065,7 @@ public sealed partial class SessionMcpServerStatusChangedData public required McpServerStatus Status { get; set; } } -/// Payload of MCP `list_changed` notification events, emitted when an MCP server announces at runtime that one of its advertised lists changed. +/// Payload identifying the MCP server associated with a list change. public sealed partial class McpToolsListChangedData { /// Name of the MCP server whose list changed. @@ -3989,7 +4073,7 @@ public sealed partial class McpToolsListChangedData public required string ServerName { get; set; } } -/// Payload of MCP `list_changed` notification events, emitted when an MCP server announces at runtime that one of its advertised lists changed. +/// Payload identifying the MCP server associated with a list change. public sealed partial class McpResourcesListChangedData { /// Name of the MCP server whose list changed. @@ -3997,7 +4081,7 @@ public sealed partial class McpResourcesListChangedData public required string ServerName { get; set; } } -/// Payload of MCP `list_changed` notification events, emitted when an MCP server announces at runtime that one of its advertised lists changed. +/// Payload identifying the MCP server associated with a list change. public sealed partial class McpPromptsListChangedData { /// Name of the MCP server whose list changed. @@ -7525,6 +7609,37 @@ public sealed partial class ElicitationRequestedSchema public required string Type { get; set; } } +/// Single HTTP header entry as a name/value pair. +/// Nested data type for HeaderEntry. +public sealed partial class HeaderEntry +{ + /// HTTP response header name as observed by the runtime. + [JsonPropertyName("name")] + public required string Name { get; set; } + + /// HTTP response header value as observed by the runtime. + [JsonPropertyName("value")] + public required string Value { get; set; } +} + +/// Raw HTTP response details from the OAuth auth challenge, as observed by the runtime. +/// Nested data type for McpOauthHttpResponse. +public sealed partial class McpOauthHttpResponse +{ + /// Complete UTF-8 response body for host-specific challenge handling, including an empty string for an empty body. Omitted when the complete body is not valid UTF-8; body read failures fail the HTTP operation rather than exposing a partial response. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("body")] + public string? Body { get; set; } + + /// HTTP response headers as observed by the runtime. Order and casing are transport-dependent, and duplicate header names may appear multiple times. + [JsonPropertyName("headers")] + public required HeaderEntry[] Headers { get; set; } + + /// HTTP status code returned with the auth challenge. + [JsonPropertyName("statusCode")] + public required int StatusCode { get; set; } +} + /// Static OAuth client configuration, if the server specifies one. /// Nested data type for McpOauthRequiredStaticClientConfig. public sealed partial class McpOauthRequiredStaticClientConfig @@ -10690,6 +10805,70 @@ public override void Write(Utf8JsonWriter writer, AutoModeResolvedReasoningBucke } } +/// Which channel supplied the effective enterprise managed settings (highest-authority present layer wins wholesale). +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ManagedSettingsResolvedSource : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ManagedSettingsResolvedSource(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// Account/org policy self-fetched from the GitHub managed-settings endpoint (higher authority). + public static ManagedSettingsResolvedSource Server { get; } = new("server"); + + /// Device-level MDM policy discovered from plist/registry/file (lower authority). + public static ManagedSettingsResolvedSource Device { get; } = new("device"); + + /// No managed policy is in force (no layer contributed). + public static ManagedSettingsResolvedSource None { get; } = new("none"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ManagedSettingsResolvedSource left, ManagedSettingsResolvedSource right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ManagedSettingsResolvedSource left, ManagedSettingsResolvedSource right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is ManagedSettingsResolvedSource other && Equals(other); + + /// + public bool Equals(ManagedSettingsResolvedSource other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override ManagedSettingsResolvedSource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, ManagedSettingsResolvedSource value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ManagedSettingsResolvedSource)); + } + } +} + /// Exit plan mode action. [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] @@ -11197,6 +11376,8 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(AssistantReasoningDeltaData))] [JsonSerializable(typeof(AssistantReasoningDeltaEvent))] [JsonSerializable(typeof(AssistantReasoningEvent))] +[JsonSerializable(typeof(AssistantServerToolProgressData))] +[JsonSerializable(typeof(AssistantServerToolProgressEvent))] [JsonSerializable(typeof(AssistantStreamingDeltaData))] [JsonSerializable(typeof(AssistantStreamingDeltaEvent))] [JsonSerializable(typeof(AssistantToolCallDeltaData))] @@ -11282,6 +11463,7 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(ExternalToolRequestedEvent))] [JsonSerializable(typeof(GitHubRepoRef))] [JsonSerializable(typeof(HandoffRepository))] +[JsonSerializable(typeof(HeaderEntry))] [JsonSerializable(typeof(HookEndData))] [JsonSerializable(typeof(HookEndError))] [JsonSerializable(typeof(HookEndEvent))] @@ -11300,6 +11482,7 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(McpHeadersRefreshRequiredEvent))] [JsonSerializable(typeof(McpOauthCompletedData))] [JsonSerializable(typeof(McpOauthCompletedEvent))] +[JsonSerializable(typeof(McpOauthHttpResponse))] [JsonSerializable(typeof(McpOauthRequiredData))] [JsonSerializable(typeof(McpOauthRequiredEvent))] [JsonSerializable(typeof(McpOauthRequiredStaticClientConfig))] @@ -11413,6 +11596,8 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(SessionLimitsExhaustedRequestedData))] [JsonSerializable(typeof(SessionLimitsExhaustedRequestedEvent))] [JsonSerializable(typeof(SessionLimitsExhaustedResponse))] +[JsonSerializable(typeof(SessionManagedSettingsResolvedData))] +[JsonSerializable(typeof(SessionManagedSettingsResolvedEvent))] [JsonSerializable(typeof(SessionMcpServerStatusChangedData))] [JsonSerializable(typeof(SessionMcpServerStatusChangedEvent))] [JsonSerializable(typeof(SessionMcpServersLoadedData))] diff --git a/dotnet/src/UnixMillisecondsDateTimeOffsetConverter.cs b/dotnet/src/UnixMillisecondsDateTimeOffsetConverter.cs index 4b8fcc3616..8e176fbafa 100644 --- a/dotnet/src/UnixMillisecondsDateTimeOffsetConverter.cs +++ b/dotnet/src/UnixMillisecondsDateTimeOffsetConverter.cs @@ -13,8 +13,14 @@ namespace GitHub.Copilot; public sealed class UnixMillisecondsDateTimeOffsetConverter : JsonConverter { /// - public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => - DateTimeOffset.FromUnixTimeMilliseconds(reader.GetInt64()); + public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + // The CLI may serialize the epoch-millisecond timestamp as a JSON integer + // or as a floating-point number (e.g. 1700000000000.0). GetInt64 throws on a + // fractional token, so fall back to reading a double and truncating. + long milliseconds = reader.TryGetInt64(out long value) ? value : (long)reader.GetDouble(); + return DateTimeOffset.FromUnixTimeMilliseconds(milliseconds); + } /// public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options) => diff --git a/go/client.go b/go/client.go index fa7159de74..f243268aa4 100644 --- a/go/client.go +++ b/go/client.go @@ -2290,7 +2290,6 @@ func (c *Client) setupNotificationHandler() { c.client.SetRequestHandler("userInput.request", jsonrpc2.RequestHandlerFor(c.handleUserInputRequest)) c.client.SetRequestHandler("exitPlanMode.request", jsonrpc2.RequestHandlerFor(c.handleExitPlanModeRequest)) c.client.SetRequestHandler("autoModeSwitch.request", jsonrpc2.RequestHandlerFor(c.handleAutoModeSwitchRequest)) - c.client.SetRequestHandler("hooks.invoke", jsonrpc2.RequestHandlerFor(c.handleHooksInvoke)) c.client.SetRequestHandler("systemMessage.transform", jsonrpc2.RequestHandlerFor(c.handleSystemMessageTransform)) rpc.RegisterClientSessionAPIHandlers(c.client, func(sessionID string) *rpc.ClientSessionAPIHandlers { c.sessionsMux.Lock() @@ -2301,21 +2300,25 @@ func (c *Client) setupNotificationHandler() { } return session.clientSessionAPIs }) - if c.options.RequestHandler != nil || c.options.OnGitHubTelemetry != nil { - handlers := &rpc.ClientGlobalAPIHandlers{} - if c.options.RequestHandler != nil { - handlers.LlmInference = newCopilotRequestAdapter(c.options.RequestHandler, func() *rpc.ServerLlmInferenceAPI { - if c.RPC == nil { - return nil - } - return c.RPC.LlmInference - }) - } - if c.options.OnGitHubTelemetry != nil { - handlers.GitHubTelemetry = &gitHubTelemetryAdapter{callback: c.options.OnGitHubTelemetry} - } - rpc.RegisterClientGlobalAPIHandlers(c.client, handlers) + // hooks.invoke is a client-global RPC method: one connection-level handler + // receives every hook callback and routes to the owning session via the + // payload's sessionId. Always register the global handlers so the generated + // hooks.invoke handler is wired to our dispatcher. + handlers := &rpc.ClientGlobalAPIHandlers{ + Hooks: &hooksAdapter{client: c}, + } + if c.options.RequestHandler != nil { + handlers.LlmInference = newCopilotRequestAdapter(c.options.RequestHandler, func() *rpc.ServerLlmInferenceAPI { + if c.RPC == nil { + return nil + } + return c.RPC.LlmInference + }) } + if c.options.OnGitHubTelemetry != nil { + handlers.GitHubTelemetry = &gitHubTelemetryAdapter{callback: c.options.OnGitHubTelemetry} + } + rpc.RegisterClientGlobalAPIHandlers(c.client, handlers) } // gitHubTelemetryAdapter adapts the OnGitHubTelemetry option to the generated @@ -2423,7 +2426,8 @@ func (c *Client) handleAutoModeSwitchRequest(req autoModeSwitchRequest) (*autoMo return &autoModeSwitchResponse{Response: response}, nil } -// handleHooksInvoke handles a hooks invocation from the CLI server. +// handleHooksInvoke routes a hook callback to its owning session, keyed by the +// payload's sessionId. func (c *Client) handleHooksInvoke(req hooksInvokeRequest) (map[string]any, *jsonrpc2.Error) { if req.SessionID == "" || req.Type == "" { return nil, &jsonrpc2.Error{Code: -32602, Message: "invalid hooks invoke payload"} @@ -2448,6 +2452,34 @@ func (c *Client) handleHooksInvoke(req hooksInvokeRequest) (map[string]any, *jso return result, nil } +// hooksAdapter implements the generated rpc.HooksHandler, delegating to the +// client's per-session hook dispatcher. +type hooksAdapter struct { + client *Client +} + +func (a *hooksAdapter) Invoke(request *rpc.HookInvokeRequest) (*rpc.HookInvokeResponse, error) { + rawInput, err := json.Marshal(request.Input) + if err != nil { + return nil, &jsonrpc2.Error{Code: -32602, Message: fmt.Sprintf("invalid hooks invoke payload: %v", err)} + } + + result, rpcErr := a.client.handleHooksInvoke(hooksInvokeRequest{ + SessionID: request.SessionID, + Type: string(request.HookType), + Input: rawInput, + }) + if rpcErr != nil { + return nil, rpcErr + } + + response := &rpc.HookInvokeResponse{} + if result != nil { + response.Output = result["output"] + } + return response, nil +} + // handleSystemMessageTransform handles a system message transform request from the CLI server. func (c *Client) handleSystemMessageTransform(req systemMessageTransformRequest) (systemMessageTransformResponse, *jsonrpc2.Error) { if req.SessionID == "" { diff --git a/go/rpc/zrpc.go b/go/rpc/zrpc.go index ffedb94462..30ae6ff271 100644 --- a/go/rpc/zrpc.go +++ b/go/rpc/zrpc.go @@ -2463,6 +2463,26 @@ type HistoryTruncateResult struct { EventsRemoved int64 `json:"eventsRemoved"` } +// Runtime-owned wire payload for a server-to-client hook callback invocation. +// Experimental: HookInvokeRequest is part of an experimental API and may change or be +// removed. +// Internal: HookInvokeRequest is an internal SDK API and is not part of the public surface. +type HookInvokeRequest struct { + // Internal: HookType is part of the SDK's internal API surface and is not intended for + // external use. + HookType HookType `json:"hookType"` + Input any `json:"input"` + SessionID string `json:"sessionId"` +} + +// Optional output returned by an SDK callback hook. +// Experimental: HookInvokeResponse is part of an experimental API and may change or be +// removed. +// Internal: HookInvokeResponse is an internal SDK API and is not part of the public surface. +type HookInvokeResponse struct { + Output any `json:"output,omitempty"` +} + // Installed plugin record from global state, with marketplace, version, install time, // enabled state, cache path, and source. // Experimental: InstalledPlugin is part of an experimental API and may change or be removed. @@ -3978,13 +3998,27 @@ type MCPStopServerRequest struct { ServerName string `json:"serverName"` } -// MCP tool metadata with tool name and optional description. +// MCP tool metadata with tool name, optional description, and normalized MCP Apps discovery +// metadata. // Experimental: MCPTools is part of an experimental API and may change or be removed. type MCPTools struct { // Tool description, when provided. Description *string `json:"description,omitempty"` // Tool name. Name string `json:"name"` + // Normalized MCP Apps discovery metadata. An empty object indicates that a valid `_meta.ui` + // block was present without recognized fields. + UI *MCPToolUI `json:"ui,omitempty"` +} + +// Normalized MCP Apps discovery metadata from a tool's `_meta.ui` block. +// Experimental: MCPToolUI is part of an experimental API and may change or be removed. +type MCPToolUI struct { + // URI of the tool's MCP App resource, typically a `ui://` resource identifier. Use + // `session.mcp.resources.read` to fetch its HTML and resource metadata. + ResourceURI *string `json:"resourceUri,omitempty"` + // Tool visibility advertised by the server. When absent, MCP Apps defaults apply. + Visibility []MCPToolUIVisibility `json:"visibility,omitzero"` } // Server name identifying the external client to remove. @@ -7958,10 +7992,21 @@ type SessionModelList struct { // (CAPI) models and any registry BYOK models; a BYOK model appears under its // provider-qualified selection id (`provider/id`). List []any `json:"list"` + // Cost categories for the full CAPI catalog, including picker-disabled models that Auto may + // select. Metadata only; entries absent from `list` are not manually selectable. + ModelPriceCategories []SessionModelPriceCategory `json:"modelPriceCategories,omitzero"` // Per-quota snapshots returned alongside the model list, keyed by quota type. QuotaSnapshots map[string]any `json:"quotaSnapshots,omitzero"` } +// Cost-category metadata for a CAPI model. +// Experimental: SessionModelPriceCategory is part of an experimental API and may change or +// be removed. +type SessionModelPriceCategory struct { + ID string `json:"id"` + PriceCategory ModelPickerPriceCategory `json:"priceCategory"` +} + // Experimental: SessionModeSetResult is part of an experimental API and may change or be // removed. type SessionModeSetResult struct { @@ -9061,6 +9106,8 @@ type SessionUpdateOptionsParams struct { // Experimental: SessionUpdateOptionsResult is part of an experimental API and may change or // be removed. type SessionUpdateOptionsResult struct { + // Number of hooks loaded from installed plugins, returned when installedPlugins is updated + PluginHookCount *int64 `json:"pluginHookCount,omitempty"` // Whether the operation succeeded Success bool `json:"success"` } @@ -11356,6 +11403,45 @@ const ( HMACAuthInfoHostHTTPSGitHubCom HMACAuthInfoHost = "https://github.com" ) +// Hook event name dispatched through the SDK callback transport. +// Experimental: HookType is part of an experimental API and may change or be removed. +type HookType string + +const ( + // Runs when the agent stops. + HookTypeAgentStop HookType = "agentStop" + // Runs when the agent encounters an error. + HookTypeErrorOccurred HookType = "errorOccurred" + // Runs when the agent emits a notification. + HookTypeNotification HookType = "notification" + // Runs when the agent requests permission. + HookTypePermissionRequest HookType = "permissionRequest" + // Runs after an agent result is produced. + HookTypePostResult HookType = "postResult" + // Runs after a tool completes successfully. + HookTypePostToolUse HookType = "postToolUse" + // Runs after a tool fails. + HookTypePostToolUseFailure HookType = "postToolUseFailure" + // Runs before conversation context is compacted. + HookTypePreCompact HookType = "preCompact" + // Runs before an MCP tool is invoked. + HookTypePreMCPToolCall HookType = "preMcpToolCall" + // Runs before a pull request description is generated. + HookTypePrePRDescription HookType = "prePRDescription" + // Runs before a tool is invoked. + HookTypePreToolUse HookType = "preToolUse" + // Runs when a session ends. + HookTypeSessionEnd HookType = "sessionEnd" + // Runs when a session starts. + HookTypeSessionStart HookType = "sessionStart" + // Runs when a subagent starts. + HookTypeSubagentStart HookType = "subagentStart" + // Runs when a subagent stops. + HookTypeSubagentStop HookType = "subagentStop" + // Runs after the user submits a prompt. + HookTypeUserPromptSubmitted HookType = "userPromptSubmitted" +) + // Constant value. Always "github". type InstalledPluginSourceGitHubSource string @@ -11703,6 +11789,18 @@ const ( MCPSetEnvValueModeDetailsIndirect MCPSetEnvValueModeDetails = "indirect" ) +// Consumer allowed to call an MCP tool. +// Experimental: MCPToolUIVisibility is part of an experimental API and may change or be +// removed. +type MCPToolUIVisibility string + +const ( + // An MCP App view may call the tool. + MCPToolUIVisibilityApp MCPToolUIVisibility = "app" + // The model may call the tool. + MCPToolUIVisibilityModel MCPToolUIVisibility = "model" +) + // The current agent mode for this session (e.g., 'interactive', 'plan', 'autopilot') // Experimental: MetadataSnapshotCurrentMode is part of an experimental API and may change // or be removed. @@ -15664,7 +15762,9 @@ func (a *MCPAPI) List(ctx context.Context) (*MCPServerList, error) { return &result, nil } -// ListTools lists the tools exposed by a connected MCP server on this session's host. +// ListTools lists the tools exposed by a connected MCP server on this session's host. This +// performs a live `tools/list` request. Tool UI metadata is returned independently of +// whether MCP Apps rendering is enabled for the session. // // RPC method: session.mcp.listTools. // @@ -19952,6 +20052,20 @@ type GitHubTelemetryHandler interface { Event(request *GitHubTelemetryNotification) error } +// Experimental: HooksHandler contains experimental APIs that may change or be removed. +type HooksHandler interface { + // Invoke dispatches one SDK callback hook from the runtime to the connection that + // registered it. Internal transport plumbing: clients opt in through session initialization + // and the Rust hook processor owns ordering, policy, timeout, and callback routing. + // + // RPC method: hooks.invoke. + // + // Parameters: Runtime-owned wire payload for a server-to-client hook callback invocation. + // + // Returns: Optional output returned by an SDK callback hook. + Invoke(request *HookInvokeRequest) (*HookInvokeResponse, error) +} + // Experimental: LlmInferenceHandler contains experimental APIs that may change or be // removed. type LlmInferenceHandler interface { @@ -19989,6 +20103,7 @@ type LlmInferenceHandler interface { // key; a single set of handlers serves the entire connection. type ClientGlobalAPIHandlers struct { GitHubTelemetry GitHubTelemetryHandler + Hooks HooksHandler LlmInference LlmInferenceHandler } @@ -20019,6 +20134,24 @@ func RegisterClientGlobalAPIHandlers(client *jsonrpc2.Client, handlers *ClientGl } return nil, nil }) + client.SetRequestHandler("hooks.invoke", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + var request HookInvokeRequest + if err := json.Unmarshal(params, &request); err != nil { + return nil, &jsonrpc2.Error{Code: -32602, Message: fmt.Sprintf("Invalid params: %v", err)} + } + if handlers == nil || handlers.Hooks == nil { + return nil, &jsonrpc2.Error{Code: -32603, Message: "No hooks client-global handler registered"} + } + result, err := handlers.Hooks.Invoke(&request) + if err != nil { + return nil, clientGlobalHandlerError(err) + } + raw, err := json.Marshal(result) + if err != nil { + return nil, &jsonrpc2.Error{Code: -32603, Message: fmt.Sprintf("Failed to marshal response: %v", err)} + } + return raw, nil + }) client.SetRequestHandler("llmInference.httpRequestChunk", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { var request LlmInferenceHTTPRequestChunkRequest if err := json.Unmarshal(params, &request); err != nil { diff --git a/go/rpc/zsession_encoding.go b/go/rpc/zsession_encoding.go index 7f0b1b3eae..2bd212d884 100644 --- a/go/rpc/zsession_encoding.go +++ b/go/rpc/zsession_encoding.go @@ -83,6 +83,12 @@ func (e *SessionEvent) UnmarshalJSON(data []byte) error { return err } e.Data = &d + case SessionEventTypeAssistantServerToolProgress: + var d AssistantServerToolProgressData + if err := json.Unmarshal(raw.Data, &d); err != nil { + return err + } + e.Data = &d case SessionEventTypeAssistantStreamingDelta: var d AssistantStreamingDeltaData if err := json.Unmarshal(raw.Data, &d); err != nil { @@ -431,6 +437,12 @@ func (e *SessionEvent) UnmarshalJSON(data []byte) error { return err } e.Data = &d + case SessionEventTypeSessionManagedSettingsResolved: + var d SessionManagedSettingsResolvedData + if err := json.Unmarshal(raw.Data, &d); err != nil { + return err + } + e.Data = &d case SessionEventTypeSessionMCPServersLoaded: var d SessionMCPServersLoadedData if err := json.Unmarshal(raw.Data, &d); err != nil { diff --git a/go/rpc/zsession_events.go b/go/rpc/zsession_events.go index 7b82a5704d..ec211132df 100644 --- a/go/rpc/zsession_events.go +++ b/go/rpc/zsession_events.go @@ -53,49 +53,50 @@ func (r RawSessionEventData) Type() SessionEventType { type SessionEventType string const ( - SessionEventTypeAbort SessionEventType = "abort" - SessionEventTypeAssistantIdle SessionEventType = "assistant.idle" - SessionEventTypeAssistantIntent SessionEventType = "assistant.intent" - SessionEventTypeAssistantMessage SessionEventType = "assistant.message" - SessionEventTypeAssistantMessageDelta SessionEventType = "assistant.message_delta" - SessionEventTypeAssistantMessageStart SessionEventType = "assistant.message_start" - SessionEventTypeAssistantReasoning SessionEventType = "assistant.reasoning" - SessionEventTypeAssistantReasoningDelta SessionEventType = "assistant.reasoning_delta" - SessionEventTypeAssistantStreamingDelta SessionEventType = "assistant.streaming_delta" - SessionEventTypeAssistantToolCallDelta SessionEventType = "assistant.tool_call_delta" - SessionEventTypeAssistantTurnEnd SessionEventType = "assistant.turn_end" - SessionEventTypeAssistantTurnStart SessionEventType = "assistant.turn_start" - SessionEventTypeAssistantUsage SessionEventType = "assistant.usage" - SessionEventTypeAutoModeSwitchCompleted SessionEventType = "auto_mode_switch.completed" - SessionEventTypeAutoModeSwitchRequested SessionEventType = "auto_mode_switch.requested" - SessionEventTypeCapabilitiesChanged SessionEventType = "capabilities.changed" - SessionEventTypeCommandCompleted SessionEventType = "command.completed" - SessionEventTypeCommandExecute SessionEventType = "command.execute" - SessionEventTypeCommandQueued SessionEventType = "command.queued" - SessionEventTypeCommandsChanged SessionEventType = "commands.changed" - SessionEventTypeElicitationCompleted SessionEventType = "elicitation.completed" - SessionEventTypeElicitationRequested SessionEventType = "elicitation.requested" - SessionEventTypeExitPlanModeCompleted SessionEventType = "exit_plan_mode.completed" - SessionEventTypeExitPlanModeRequested SessionEventType = "exit_plan_mode.requested" - SessionEventTypeExternalToolCompleted SessionEventType = "external_tool.completed" - SessionEventTypeExternalToolRequested SessionEventType = "external_tool.requested" - SessionEventTypeHookEnd SessionEventType = "hook.end" - SessionEventTypeHookProgress SessionEventType = "hook.progress" - SessionEventTypeHookStart SessionEventType = "hook.start" - SessionEventTypeMCPAppToolCallComplete SessionEventType = "mcp_app.tool_call_complete" - SessionEventTypeMCPHeadersRefreshCompleted SessionEventType = "mcp.headers_refresh_completed" - SessionEventTypeMCPHeadersRefreshRequired SessionEventType = "mcp.headers_refresh_required" - SessionEventTypeMCPOauthCompleted SessionEventType = "mcp.oauth_completed" - SessionEventTypeMCPOauthRequired SessionEventType = "mcp.oauth_required" - SessionEventTypeMCPPromptsListChanged SessionEventType = "mcp.prompts.list_changed" - SessionEventTypeMCPResourcesListChanged SessionEventType = "mcp.resources.list_changed" - SessionEventTypeMCPToolsListChanged SessionEventType = "mcp.tools.list_changed" - SessionEventTypeModelCallFailure SessionEventType = "model.call_failure" - SessionEventTypePendingMessagesModified SessionEventType = "pending_messages.modified" - SessionEventTypePermissionCompleted SessionEventType = "permission.completed" - SessionEventTypePermissionRequested SessionEventType = "permission.requested" - SessionEventTypeSamplingCompleted SessionEventType = "sampling.completed" - SessionEventTypeSamplingRequested SessionEventType = "sampling.requested" + SessionEventTypeAbort SessionEventType = "abort" + SessionEventTypeAssistantIdle SessionEventType = "assistant.idle" + SessionEventTypeAssistantIntent SessionEventType = "assistant.intent" + SessionEventTypeAssistantMessage SessionEventType = "assistant.message" + SessionEventTypeAssistantMessageDelta SessionEventType = "assistant.message_delta" + SessionEventTypeAssistantMessageStart SessionEventType = "assistant.message_start" + SessionEventTypeAssistantReasoning SessionEventType = "assistant.reasoning" + SessionEventTypeAssistantReasoningDelta SessionEventType = "assistant.reasoning_delta" + SessionEventTypeAssistantServerToolProgress SessionEventType = "assistant.server_tool_progress" + SessionEventTypeAssistantStreamingDelta SessionEventType = "assistant.streaming_delta" + SessionEventTypeAssistantToolCallDelta SessionEventType = "assistant.tool_call_delta" + SessionEventTypeAssistantTurnEnd SessionEventType = "assistant.turn_end" + SessionEventTypeAssistantTurnStart SessionEventType = "assistant.turn_start" + SessionEventTypeAssistantUsage SessionEventType = "assistant.usage" + SessionEventTypeAutoModeSwitchCompleted SessionEventType = "auto_mode_switch.completed" + SessionEventTypeAutoModeSwitchRequested SessionEventType = "auto_mode_switch.requested" + SessionEventTypeCapabilitiesChanged SessionEventType = "capabilities.changed" + SessionEventTypeCommandCompleted SessionEventType = "command.completed" + SessionEventTypeCommandExecute SessionEventType = "command.execute" + SessionEventTypeCommandQueued SessionEventType = "command.queued" + SessionEventTypeCommandsChanged SessionEventType = "commands.changed" + SessionEventTypeElicitationCompleted SessionEventType = "elicitation.completed" + SessionEventTypeElicitationRequested SessionEventType = "elicitation.requested" + SessionEventTypeExitPlanModeCompleted SessionEventType = "exit_plan_mode.completed" + SessionEventTypeExitPlanModeRequested SessionEventType = "exit_plan_mode.requested" + SessionEventTypeExternalToolCompleted SessionEventType = "external_tool.completed" + SessionEventTypeExternalToolRequested SessionEventType = "external_tool.requested" + SessionEventTypeHookEnd SessionEventType = "hook.end" + SessionEventTypeHookProgress SessionEventType = "hook.progress" + SessionEventTypeHookStart SessionEventType = "hook.start" + SessionEventTypeMCPAppToolCallComplete SessionEventType = "mcp_app.tool_call_complete" + SessionEventTypeMCPHeadersRefreshCompleted SessionEventType = "mcp.headers_refresh_completed" + SessionEventTypeMCPHeadersRefreshRequired SessionEventType = "mcp.headers_refresh_required" + SessionEventTypeMCPOauthCompleted SessionEventType = "mcp.oauth_completed" + SessionEventTypeMCPOauthRequired SessionEventType = "mcp.oauth_required" + SessionEventTypeMCPPromptsListChanged SessionEventType = "mcp.prompts.list_changed" + SessionEventTypeMCPResourcesListChanged SessionEventType = "mcp.resources.list_changed" + SessionEventTypeMCPToolsListChanged SessionEventType = "mcp.tools.list_changed" + SessionEventTypeModelCallFailure SessionEventType = "model.call_failure" + SessionEventTypePendingMessagesModified SessionEventType = "pending_messages.modified" + SessionEventTypePermissionCompleted SessionEventType = "permission.completed" + SessionEventTypePermissionRequested SessionEventType = "permission.requested" + SessionEventTypeSamplingCompleted SessionEventType = "sampling.completed" + SessionEventTypeSamplingRequested SessionEventType = "sampling.requested" // Experimental: SessionEventTypeSessionAutoModeResolved identifies an experimental event // that may change or be removed. SessionEventTypeSessionAutoModeResolved SessionEventType = "session.auto_mode_resolved" @@ -135,47 +136,50 @@ const ( SessionEventTypeSessionInfo SessionEventType = "session.info" SessionEventTypeSessionLimitsExhaustedCompleted SessionEventType = "session_limits_exhausted.completed" SessionEventTypeSessionLimitsExhaustedRequested SessionEventType = "session_limits_exhausted.requested" - SessionEventTypeSessionMCPServersLoaded SessionEventType = "session.mcp_servers_loaded" - SessionEventTypeSessionMCPServerStatusChanged SessionEventType = "session.mcp_server_status_changed" - SessionEventTypeSessionModeChanged SessionEventType = "session.mode_changed" - SessionEventTypeSessionModelChange SessionEventType = "session.model_change" - SessionEventTypeSessionPermissionsChanged SessionEventType = "session.permissions_changed" - SessionEventTypeSessionPlanChanged SessionEventType = "session.plan_changed" - SessionEventTypeSessionRemoteSteerableChanged SessionEventType = "session.remote_steerable_changed" - SessionEventTypeSessionResume SessionEventType = "session.resume" - SessionEventTypeSessionScheduleCancelled SessionEventType = "session.schedule_cancelled" - SessionEventTypeSessionScheduleCreated SessionEventType = "session.schedule_created" - SessionEventTypeSessionScheduleRearmed SessionEventType = "session.schedule_rearmed" - SessionEventTypeSessionSessionLimitsChanged SessionEventType = "session.session_limits_changed" - SessionEventTypeSessionShutdown SessionEventType = "session.shutdown" - SessionEventTypeSessionSkillsLoaded SessionEventType = "session.skills_loaded" - SessionEventTypeSessionSnapshotRewind SessionEventType = "session.snapshot_rewind" - SessionEventTypeSessionStart SessionEventType = "session.start" - SessionEventTypeSessionTaskComplete SessionEventType = "session.task_complete" - SessionEventTypeSessionTitleChanged SessionEventType = "session.title_changed" - SessionEventTypeSessionTodosChanged SessionEventType = "session.todos_changed" - SessionEventTypeSessionToolsUpdated SessionEventType = "session.tools_updated" - SessionEventTypeSessionTruncation SessionEventType = "session.truncation" - SessionEventTypeSessionUsageCheckpoint SessionEventType = "session.usage_checkpoint" - SessionEventTypeSessionUsageInfo SessionEventType = "session.usage_info" - SessionEventTypeSessionWarning SessionEventType = "session.warning" - SessionEventTypeSessionWorkspaceFileChanged SessionEventType = "session.workspace_file_changed" - SessionEventTypeSkillInvoked SessionEventType = "skill.invoked" - SessionEventTypeSubagentCompleted SessionEventType = "subagent.completed" - SessionEventTypeSubagentDeselected SessionEventType = "subagent.deselected" - SessionEventTypeSubagentFailed SessionEventType = "subagent.failed" - SessionEventTypeSubagentSelected SessionEventType = "subagent.selected" - SessionEventTypeSubagentStarted SessionEventType = "subagent.started" - SessionEventTypeSystemMessage SessionEventType = "system.message" - SessionEventTypeSystemNotification SessionEventType = "system.notification" - SessionEventTypeToolExecutionComplete SessionEventType = "tool.execution_complete" - SessionEventTypeToolExecutionPartialResult SessionEventType = "tool.execution_partial_result" - SessionEventTypeToolExecutionProgress SessionEventType = "tool.execution_progress" - SessionEventTypeToolExecutionStart SessionEventType = "tool.execution_start" - SessionEventTypeToolUserRequested SessionEventType = "tool.user_requested" - SessionEventTypeUserInputCompleted SessionEventType = "user_input.completed" - SessionEventTypeUserInputRequested SessionEventType = "user_input.requested" - SessionEventTypeUserMessage SessionEventType = "user.message" + // Experimental: SessionEventTypeSessionManagedSettingsResolved identifies an experimental + // event that may change or be removed. + SessionEventTypeSessionManagedSettingsResolved SessionEventType = "session.managed_settings_resolved" + SessionEventTypeSessionMCPServersLoaded SessionEventType = "session.mcp_servers_loaded" + SessionEventTypeSessionMCPServerStatusChanged SessionEventType = "session.mcp_server_status_changed" + SessionEventTypeSessionModeChanged SessionEventType = "session.mode_changed" + SessionEventTypeSessionModelChange SessionEventType = "session.model_change" + SessionEventTypeSessionPermissionsChanged SessionEventType = "session.permissions_changed" + SessionEventTypeSessionPlanChanged SessionEventType = "session.plan_changed" + SessionEventTypeSessionRemoteSteerableChanged SessionEventType = "session.remote_steerable_changed" + SessionEventTypeSessionResume SessionEventType = "session.resume" + SessionEventTypeSessionScheduleCancelled SessionEventType = "session.schedule_cancelled" + SessionEventTypeSessionScheduleCreated SessionEventType = "session.schedule_created" + SessionEventTypeSessionScheduleRearmed SessionEventType = "session.schedule_rearmed" + SessionEventTypeSessionSessionLimitsChanged SessionEventType = "session.session_limits_changed" + SessionEventTypeSessionShutdown SessionEventType = "session.shutdown" + SessionEventTypeSessionSkillsLoaded SessionEventType = "session.skills_loaded" + SessionEventTypeSessionSnapshotRewind SessionEventType = "session.snapshot_rewind" + SessionEventTypeSessionStart SessionEventType = "session.start" + SessionEventTypeSessionTaskComplete SessionEventType = "session.task_complete" + SessionEventTypeSessionTitleChanged SessionEventType = "session.title_changed" + SessionEventTypeSessionTodosChanged SessionEventType = "session.todos_changed" + SessionEventTypeSessionToolsUpdated SessionEventType = "session.tools_updated" + SessionEventTypeSessionTruncation SessionEventType = "session.truncation" + SessionEventTypeSessionUsageCheckpoint SessionEventType = "session.usage_checkpoint" + SessionEventTypeSessionUsageInfo SessionEventType = "session.usage_info" + SessionEventTypeSessionWarning SessionEventType = "session.warning" + SessionEventTypeSessionWorkspaceFileChanged SessionEventType = "session.workspace_file_changed" + SessionEventTypeSkillInvoked SessionEventType = "skill.invoked" + SessionEventTypeSubagentCompleted SessionEventType = "subagent.completed" + SessionEventTypeSubagentDeselected SessionEventType = "subagent.deselected" + SessionEventTypeSubagentFailed SessionEventType = "subagent.failed" + SessionEventTypeSubagentSelected SessionEventType = "subagent.selected" + SessionEventTypeSubagentStarted SessionEventType = "subagent.started" + SessionEventTypeSystemMessage SessionEventType = "system.message" + SessionEventTypeSystemNotification SessionEventType = "system.notification" + SessionEventTypeToolExecutionComplete SessionEventType = "tool.execution_complete" + SessionEventTypeToolExecutionPartialResult SessionEventType = "tool.execution_partial_result" + SessionEventTypeToolExecutionProgress SessionEventType = "tool.execution_progress" + SessionEventTypeToolExecutionStart SessionEventType = "tool.execution_start" + SessionEventTypeToolUserRequested SessionEventType = "tool.user_requested" + SessionEventTypeUserInputCompleted SessionEventType = "user_input.completed" + SessionEventTypeUserInputRequested SessionEventType = "user_input.requested" + SessionEventTypeUserMessage SessionEventType = "user.message" ) // Agent intent description for current activity or plan @@ -585,6 +589,30 @@ func (*PendingMessagesModifiedData) Type() SessionEventType { return SessionEventTypePendingMessagesModified } +// Enterprise managed-settings resolution: the effective managed settings the session applied and where they came from, so SDK clients can show users what is enterprise-managed and by which authority. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted; for a session-independent pull, use the SDK `getManagedSettings()` API, which returns the identical payload. Managed settings have a single authoritative source, so the highest-authority present layer (server > device) wins wholesale; `bypassPermissionsDisabled` is deny-wins across layers. Marked experimental while the managed-settings surface stabilizes. +// Experimental: SessionManagedSettingsResolvedData is part of an experimental API and may change or be removed. +type SessionManagedSettingsResolvedData struct { + // Whether enterprise policy disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers, and forced on when `failClosed` is true. + BypassPermissionsDisabled bool `json:"bypassPermissionsDisabled"` + // Whether the device (MDM/plist/registry/file) managed-settings layer was present + DeviceManaged bool `json:"deviceManaged"` + // Whether managed policy could not be determined (e.g. a failed server fetch) and the session fell back to the fail-closed restriction. When true, restrictions such as disabling bypass-permissions are enforced even though `settings` may be absent. + FailClosed bool `json:"failClosed"` + // The setting keys under enterprise management in the effective managed settings (e.g. `model`, `enabledPlugins`, `permissions`). Empty when no managed settings are in force. + ManagedKeys []string `json:"managedKeys"` + // Whether the server (account/org) managed-settings layer was present + ServerManaged bool `json:"serverManaged"` + // The effective (resolved) managed settings values, so clients can render exactly what is enforced. Absent when no managed policy is in force. + Settings any `json:"settings,omitempty"` + // Which channel supplied the effective managed settings (the winning layer), or `none` when no policy is in force + Source ManagedSettingsResolvedSource `json:"source"` +} + +func (*SessionManagedSettingsResolvedData) sessionEventData() {} +func (*SessionManagedSettingsResolvedData) Type() SessionEventType { + return SessionEventTypeSessionManagedSettingsResolved +} + // Ephemeral progress update from a running hook process type HookProgressData struct { // Human-readable progress message from the hook process @@ -790,6 +818,21 @@ type AssistantUsageData struct { func (*AssistantUsageData) sessionEventData() {} func (*AssistantUsageData) Type() SessionEventType { return SessionEventTypeAssistantUsage } +// Live progress signal for a provider-hosted server tool (e.g. hosted web search) while it runs, before the finalized serverTools envelope lands on the terminal assistant.message +type AssistantServerToolProgressData struct { + // Kind of hosted server tool that is running. Only `web_search` is emitted today. + Kind string `json:"kind"` + // Position of the hosted tool call in the response output. Stable across the call's lifecycle events (unlike the provider's per-event item id, which CAPI rotates), so the host keys the live in-progress row on it. + OutputIndex int64 `json:"outputIndex"` + // Lifecycle status of the hosted call: `in_progress`, `searching`, or `completed`. + Status string `json:"status"` +} + +func (*AssistantServerToolProgressData) sessionEventData() {} +func (*AssistantServerToolProgressData) Type() SessionEventType { + return SessionEventTypeAssistantServerToolProgress +} + // MCP App view called a tool on a connected MCP server (SEP-1865) type MCPAppToolCallCompleteData struct { // Arguments passed to the tool by the app view, if any @@ -879,6 +922,8 @@ func (*SessionRemoteSteerableChangedData) Type() SessionEventType { // OAuth authentication request for an MCP server type MCPOauthRequiredData struct { + // Raw HTTP response details from the OAuth auth challenge, as observed by the runtime. Header order and casing are transport-dependent, and duplicate header names may appear multiple times. + HTTPResponse *MCPOauthHTTPResponse `json:"httpResponse,omitempty"` // Why the runtime is requesting host-provided OAuth credentials. Reason MCPOauthRequestReason `json:"reason"` // Unique identifier for this OAuth request; used to respond via session.mcp.oauth.handlePendingRequest @@ -926,16 +971,7 @@ type AssistantIdleData struct { func (*AssistantIdleData) sessionEventData() {} func (*AssistantIdleData) Type() SessionEventType { return SessionEventTypeAssistantIdle } -// Payload indicating the session is idle with no background agents or attached shell commands in flight -type SessionIdleData struct { - // True when the preceding agentic loop was cancelled via abort signal - Aborted *bool `json:"aborted,omitempty"` -} - -func (*SessionIdleData) sessionEventData() {} -func (*SessionIdleData) Type() SessionEventType { return SessionEventTypeSessionIdle } - -// Payload of MCP `list_changed` notification events, emitted when an MCP server announces at runtime that one of its advertised lists changed. +// Payload identifying the MCP server associated with a list change. type MCPPromptsListChangedData struct { // Name of the MCP server whose list changed ServerName string `json:"serverName"` @@ -946,7 +982,7 @@ func (*MCPPromptsListChangedData) Type() SessionEventType { return SessionEventTypeMCPPromptsListChanged } -// Payload of MCP `list_changed` notification events, emitted when an MCP server announces at runtime that one of its advertised lists changed. +// Payload identifying the MCP server associated with a list change. type MCPResourcesListChangedData struct { // Name of the MCP server whose list changed ServerName string `json:"serverName"` @@ -957,7 +993,7 @@ func (*MCPResourcesListChangedData) Type() SessionEventType { return SessionEventTypeMCPResourcesListChanged } -// Payload of MCP `list_changed` notification events, emitted when an MCP server announces at runtime that one of its advertised lists changed. +// Payload identifying the MCP server associated with a list change. type MCPToolsListChangedData struct { // Name of the MCP server whose list changed ServerName string `json:"serverName"` @@ -966,6 +1002,15 @@ type MCPToolsListChangedData struct { func (*MCPToolsListChangedData) sessionEventData() {} func (*MCPToolsListChangedData) Type() SessionEventType { return SessionEventTypeMCPToolsListChanged } +// Payload indicating the session is idle with no background agents or attached shell commands in flight +type SessionIdleData struct { + // True when the preceding agentic loop was cancelled via abort signal + Aborted *bool `json:"aborted,omitempty"` +} + +func (*SessionIdleData) sessionEventData() {} +func (*SessionIdleData) Type() SessionEventType { return SessionEventTypeSessionIdle } + // Payload of `session.canvas.closed` with the closed canvas instance ID, provider ID, and canvas ID. // Experimental: SessionCanvasClosedData is part of an experimental API and may change or be removed. type SessionCanvasClosedData struct { @@ -2330,6 +2375,14 @@ type HandoffRepository struct { Owner string `json:"owner"` } +// Single HTTP header entry as a name/value pair. +type HeaderEntry struct { + // HTTP response header name as observed by the runtime. + Name string `json:"name"` + // HTTP response header value as observed by the runtime. + Value string `json:"value"` +} + // Error details when the hook failed type HookEndError struct { // Human-readable error message @@ -2360,6 +2413,16 @@ type MCPAppToolCallCompleteToolMetaUI struct { Visibility []string `json:"visibility,omitzero"` } +// Raw HTTP response details from the OAuth auth challenge, as observed by the runtime. +type MCPOauthHTTPResponse struct { + // Complete UTF-8 response body for host-specific challenge handling, including an empty string for an empty body. Omitted when the complete body is not valid UTF-8; body read failures fail the HTTP operation rather than exposing a partial response. + Body *string `json:"body,omitempty"` + // HTTP response headers as observed by the runtime. Order and casing are transport-dependent, and duplicate header names may appear multiple times. + Headers []HeaderEntry `json:"headers"` + // HTTP status code returned with the auth challenge. + StatusCode int32 `json:"statusCode"` +} + // Static OAuth client configuration, if the server specifies one type MCPOauthRequiredStaticClientConfig struct { // OAuth client ID for the server @@ -3840,6 +3903,18 @@ const ( HandoffSourceTypeRemote HandoffSourceType = "remote" ) +// Which channel supplied the effective enterprise managed settings (highest-authority present layer wins wholesale) +type ManagedSettingsResolvedSource string + +const ( + // Device-level MDM policy discovered from plist/registry/file (lower authority). + ManagedSettingsResolvedSourceDevice ManagedSettingsResolvedSource = "device" + // No managed policy is in force (no layer contributed). + ManagedSettingsResolvedSourceNone ManagedSettingsResolvedSource = "none" + // Account/org policy self-fetched from the GitHub managed-settings endpoint (higher authority). + ManagedSettingsResolvedSourceServer ManagedSettingsResolvedSource = "server" +) + // How the pending MCP headers refresh request resolved. type MCPHeadersRefreshCompletedOutcome string diff --git a/go/zsession_events.go b/go/zsession_events.go index 6052364599..b3dd66ef77 100644 --- a/go/zsession_events.go +++ b/go/zsession_events.go @@ -19,6 +19,7 @@ type ( AssistantMessageToolRequestType = rpc.AssistantMessageToolRequestType AssistantReasoningData = rpc.AssistantReasoningData AssistantReasoningDeltaData = rpc.AssistantReasoningDeltaData + AssistantServerToolProgressData = rpc.AssistantServerToolProgressData AssistantStreamingDeltaData = rpc.AssistantStreamingDeltaData AssistantToolCallDeltaData = rpc.AssistantToolCallDeltaData AssistantTurnEndData = rpc.AssistantTurnEndData @@ -104,10 +105,12 @@ type ( GitHubRepoRef = rpc.GitHubRepoRef HandoffRepository = rpc.HandoffRepository HandoffSourceType = rpc.HandoffSourceType + HeaderEntry = rpc.HeaderEntry HookEndData = rpc.HookEndData HookEndError = rpc.HookEndError HookProgressData = rpc.HookProgressData HookStartData = rpc.HookStartData + ManagedSettingsResolvedSource = rpc.ManagedSettingsResolvedSource MCPAppToolCallCompleteData = rpc.MCPAppToolCallCompleteData MCPAppToolCallCompleteError = rpc.MCPAppToolCallCompleteError MCPAppToolCallCompleteToolMeta = rpc.MCPAppToolCallCompleteToolMeta @@ -118,6 +121,7 @@ type ( MCPHeadersRefreshRequiredReason = rpc.MCPHeadersRefreshRequiredReason MCPOauthCompletedData = rpc.MCPOauthCompletedData MCPOauthCompletionOutcome = rpc.MCPOauthCompletionOutcome + MCPOauthHTTPResponse = rpc.MCPOauthHTTPResponse MCPOauthRequestReason = rpc.MCPOauthRequestReason MCPOauthRequiredData = rpc.MCPOauthRequiredData MCPOauthRequiredStaticClientConfig = rpc.MCPOauthRequiredStaticClientConfig @@ -231,6 +235,7 @@ type ( SessionLimitsExhaustedRequestedData = rpc.SessionLimitsExhaustedRequestedData SessionLimitsExhaustedResponse = rpc.SessionLimitsExhaustedResponse SessionLimitsExhaustedResponseAction = rpc.SessionLimitsExhaustedResponseAction + SessionManagedSettingsResolvedData = rpc.SessionManagedSettingsResolvedData SessionMCPServersLoadedData = rpc.SessionMCPServersLoadedData SessionMCPServerStatusChangedData = rpc.SessionMCPServerStatusChangedData SessionMode = rpc.SessionMode @@ -422,6 +427,9 @@ const ( ExtensionsLoadedExtensionStatusStarting = rpc.ExtensionsLoadedExtensionStatusStarting HandoffSourceTypeLocal = rpc.HandoffSourceTypeLocal HandoffSourceTypeRemote = rpc.HandoffSourceTypeRemote + ManagedSettingsResolvedSourceDevice = rpc.ManagedSettingsResolvedSourceDevice + ManagedSettingsResolvedSourceNone = rpc.ManagedSettingsResolvedSourceNone + ManagedSettingsResolvedSourceServer = rpc.ManagedSettingsResolvedSourceServer MCPHeadersRefreshCompletedOutcomeHeaders = rpc.MCPHeadersRefreshCompletedOutcomeHeaders MCPHeadersRefreshCompletedOutcomeNone = rpc.MCPHeadersRefreshCompletedOutcomeNone MCPHeadersRefreshCompletedOutcomeTimeout = rpc.MCPHeadersRefreshCompletedOutcomeTimeout @@ -516,6 +524,7 @@ const ( SessionEventTypeAssistantMessageStart = rpc.SessionEventTypeAssistantMessageStart SessionEventTypeAssistantReasoning = rpc.SessionEventTypeAssistantReasoning SessionEventTypeAssistantReasoningDelta = rpc.SessionEventTypeAssistantReasoningDelta + SessionEventTypeAssistantServerToolProgress = rpc.SessionEventTypeAssistantServerToolProgress SessionEventTypeAssistantStreamingDelta = rpc.SessionEventTypeAssistantStreamingDelta SessionEventTypeAssistantToolCallDelta = rpc.SessionEventTypeAssistantToolCallDelta SessionEventTypeAssistantTurnEnd = rpc.SessionEventTypeAssistantTurnEnd @@ -574,6 +583,7 @@ const ( SessionEventTypeSessionInfo = rpc.SessionEventTypeSessionInfo SessionEventTypeSessionLimitsExhaustedCompleted = rpc.SessionEventTypeSessionLimitsExhaustedCompleted SessionEventTypeSessionLimitsExhaustedRequested = rpc.SessionEventTypeSessionLimitsExhaustedRequested + SessionEventTypeSessionManagedSettingsResolved = rpc.SessionEventTypeSessionManagedSettingsResolved SessionEventTypeSessionMCPServersLoaded = rpc.SessionEventTypeSessionMCPServersLoaded SessionEventTypeSessionMCPServerStatusChanged = rpc.SessionEventTypeSessionMCPServerStatusChanged SessionEventTypeSessionModeChanged = rpc.SessionEventTypeSessionModeChanged diff --git a/java/pom.xml b/java/pom.xml index 81d01b0056..59e768a052 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -86,7 +86,7 @@ DO NOT EDIT MANUALLY. Updated by the update-copilot-dependency workflow. --> - ^1.0.71-2 + ^1.0.71 diff --git a/java/scripts/codegen/package-lock.json b/java/scripts/codegen/package-lock.json index 8bb61d50b8..35cb42191d 100644 --- a/java/scripts/codegen/package-lock.json +++ b/java/scripts/codegen/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "copilot-sdk-java-codegen", "dependencies": { - "@github/copilot": "^1.0.71-2", + "@github/copilot": "^1.0.71", "json-schema": "^0.4.0", "tsx": "^4.22.4" } @@ -428,9 +428,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.71-2", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.71-2.tgz", - "integrity": "sha512-En1onRCWjPy64wnjB9B6T6nXgPI7/myHksMotpKHzh8+CnVbaYQr2n/rBKM1BVScWgpA0zn19HmKZZlg82LW7A==", + "version": "1.0.71", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.71.tgz", + "integrity": "sha512-F3axBi+sXSLYDJbxCBW36bM6MYKNC2rlyAf3Ivo/MjiHKKJW7j5AmaR1IRYS9Gt8r9mxOwWFM1cJFA+CuLaR8g==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "detect-libc": "^2.1.2" @@ -439,20 +439,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.71-2", - "@github/copilot-darwin-x64": "1.0.71-2", - "@github/copilot-linux-arm64": "1.0.71-2", - "@github/copilot-linux-x64": "1.0.71-2", - "@github/copilot-linuxmusl-arm64": "1.0.71-2", - "@github/copilot-linuxmusl-x64": "1.0.71-2", - "@github/copilot-win32-arm64": "1.0.71-2", - "@github/copilot-win32-x64": "1.0.71-2" + "@github/copilot-darwin-arm64": "1.0.71", + "@github/copilot-darwin-x64": "1.0.71", + "@github/copilot-linux-arm64": "1.0.71", + "@github/copilot-linux-x64": "1.0.71", + "@github/copilot-linuxmusl-arm64": "1.0.71", + "@github/copilot-linuxmusl-x64": "1.0.71", + "@github/copilot-win32-arm64": "1.0.71", + "@github/copilot-win32-x64": "1.0.71" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.71-2", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.71-2.tgz", - "integrity": "sha512-6DA1W3RFbyDPL/MXPeAzM9HxS7hvZOnToJHupGf4QGGs4dG2dzmTyEv0LkD4rFtyc1dx6QtyOjRt5vUsWw39Xw==", + "version": "1.0.71", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.71.tgz", + "integrity": "sha512-mEWzyqbqRAWgyU7i2uuSRoVPx/TwaFQX0nZmw0bc30aJ0BnO7cy2kYQyCHw8ykmf/tfxT0xauZ6k0BOFmWizzQ==", "cpu": [ "arm64" ], @@ -466,9 +466,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.71-2", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.71-2.tgz", - "integrity": "sha512-u/DWMhTCFaGf9TE70IgXKk0/9kWiijiHfEMVRqedbzdUNxGQ5u4lsaquEirHj3sSegVw8MZzgfKAzrT9CTr0aA==", + "version": "1.0.71", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.71.tgz", + "integrity": "sha512-Md9yEg406OBVBx3w4PeEj62TubulVLBcHleqmCoOoUmPgUxPZotUbrqz3rtbzADbXfrrD7JWvVsbd2UiNL194w==", "cpu": [ "x64" ], @@ -482,9 +482,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.71-2", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.71-2.tgz", - "integrity": "sha512-xKKk5o+zFJZ3EcoDOiCOdn1sbc8N/tHkn2j2sFQahE2SDp18ZpA2lzZp6XZ32VgxQJx0qlhPWh5BRn32Sc9csw==", + "version": "1.0.71", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.71.tgz", + "integrity": "sha512-ykLJYOqBj3jRB5IJCDugLClAqbr7DmtTbUjlNY7+Jdq/n6i+d7xUQGclf1IWL5gnxbGQVAf+zkToD+sRM389Kg==", "cpu": [ "arm64" ], @@ -498,9 +498,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.71-2", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.71-2.tgz", - "integrity": "sha512-We/kZNlEAXxhm9vMBae63Yy07RXUsTlOLsg5WrG7aNm0kh5ocUFpf5EodvtBbTmVIlGCvvm/RM1cYic6lPtD+w==", + "version": "1.0.71", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.71.tgz", + "integrity": "sha512-pC0FNHG+BBwZd6yZlM85kkAGN+uJhM6o+THi76N2GnnSxmw7+remb1mvYxdgRVbdCm+LBUIbCKRWJLuMwrfb6A==", "cpu": [ "x64" ], @@ -514,9 +514,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.71-2", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.71-2.tgz", - "integrity": "sha512-Hnyz/C4uNAXZVt6/RMtQBI89W0fxvUizm0mlp/ZohSthN03fv/PppbEsY7U5WqbTuDBuOKIU4kf3fCDC2elMCQ==", + "version": "1.0.71", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.71.tgz", + "integrity": "sha512-hBmDljFTjacxqZTasCEy43H8EIzuXB/hHEBBCMFjhB9J00nIxsO6Dh0woTifKpx7knTYZdpTjjca3D0pAoZlUA==", "cpu": [ "arm64" ], @@ -530,9 +530,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.71-2", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.71-2.tgz", - "integrity": "sha512-CQDeUqq2wbM+a/Tec68O+6Gp8f3cUZ5DLAqcNwxzN+86b5Gsu9xyGMV2eIF/sEYxakhY4mpHhjwdZBySue7IBA==", + "version": "1.0.71", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.71.tgz", + "integrity": "sha512-CfTXU8pa5dxRz22xQzoi3TiG1PJo9+WR8PRDiPSdkIBSyPJ1NvX87DJmfXjTgeAfR+wkjt/p0keDCaBBVhNmUA==", "cpu": [ "x64" ], @@ -546,9 +546,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.71-2", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.71-2.tgz", - "integrity": "sha512-lb81urkKJ+yWIy62yw9NmyjX5eFiw0y2TVIkVje26ot+gCiCJPisqyRWwhkfq0g/YKRbS8uoX4r+KHAhRH1wlg==", + "version": "1.0.71", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.71.tgz", + "integrity": "sha512-+HI1DokixXhHUahj06Fw67ZAigBuXKC58BFma4UJOGrQsDgwOSbqeTQHCw6vuymzjKlg3sactfsCUTaefkjscQ==", "cpu": [ "arm64" ], @@ -562,9 +562,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.71-2", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.71-2.tgz", - "integrity": "sha512-j3mQO2OUVoO+ZGE5KGF3iiekTikQZDQTnZO9RquWPXLwQs6ZdgRw9pPhbpXh0eoM1osZAW1/tmafcyYNpdBgWA==", + "version": "1.0.71", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.71.tgz", + "integrity": "sha512-02kXOBd9CwBbCaztuf71WYWn+uGapCuiaasomN4tcMH3HBVZ4gi3J0ZUoRcgcS80xh81uQyeBHbnUKzb/RE/9A==", "cpu": [ "x64" ], diff --git a/java/scripts/codegen/package.json b/java/scripts/codegen/package.json index dac860deb4..20f742fdce 100644 --- a/java/scripts/codegen/package.json +++ b/java/scripts/codegen/package.json @@ -7,7 +7,7 @@ "generate:java": "tsx java.ts" }, "dependencies": { - "@github/copilot": "^1.0.71-2", + "@github/copilot": "^1.0.71", "json-schema": "^0.4.0", "tsx": "^4.22.4" } diff --git a/java/src/generated/java/com/github/copilot/generated/AssistantServerToolProgressEvent.java b/java/src/generated/java/com/github/copilot/generated/AssistantServerToolProgressEvent.java new file mode 100644 index 0000000000..462a573b3e --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/AssistantServerToolProgressEvent.java @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Session event "assistant.server_tool_progress". Live progress signal for a provider-hosted server tool (e.g. hosted web search) while it runs, before the finalized serverTools envelope lands on the terminal assistant.message + * @since 1.0.0 + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class AssistantServerToolProgressEvent extends SessionEvent { + + @Override + public String getType() { return "assistant.server_tool_progress"; } + + @JsonProperty("data") + private AssistantServerToolProgressEventData data; + + public AssistantServerToolProgressEventData getData() { return data; } + public void setData(AssistantServerToolProgressEventData data) { this.data = data; } + + /** Data payload for {@link AssistantServerToolProgressEvent}. */ + @JsonIgnoreProperties(ignoreUnknown = true) + @JsonInclude(JsonInclude.Include.NON_NULL) + public record AssistantServerToolProgressEventData( + /** Position of the hosted tool call in the response output. Stable across the call's lifecycle events (unlike the provider's per-event item id, which CAPI rotates), so the host keys the live in-progress row on it. */ + @JsonProperty("outputIndex") Long outputIndex, + /** Kind of hosted server tool that is running. Only `web_search` is emitted today. */ + @JsonProperty("kind") String kind, + /** Lifecycle status of the hosted call: `in_progress`, `searching`, or `completed`. */ + @JsonProperty("status") String status + ) { + } +} diff --git a/java/src/generated/java/com/github/copilot/generated/HeaderEntry.java b/java/src/generated/java/com/github/copilot/generated/HeaderEntry.java new file mode 100644 index 0000000000..14828d32e3 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/HeaderEntry.java @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Single HTTP header entry as a name/value pair. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record HeaderEntry( + /** HTTP response header name as observed by the runtime. */ + @JsonProperty("name") String name, + /** HTTP response header value as observed by the runtime. */ + @JsonProperty("value") String value +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/ManagedSettingsResolvedSource.java b/java/src/generated/java/com/github/copilot/generated/ManagedSettingsResolvedSource.java new file mode 100644 index 0000000000..1ffa100635 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/ManagedSettingsResolvedSource.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import javax.annotation.processing.Generated; + +/** + * Which channel supplied the effective enterprise managed settings (highest-authority present layer wins wholesale) + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ManagedSettingsResolvedSource { + /** The {@code server} variant. */ + SERVER("server"), + /** The {@code device} variant. */ + DEVICE("device"), + /** The {@code none} variant. */ + NONE("none"); + + private final String value; + ManagedSettingsResolvedSource(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ManagedSettingsResolvedSource fromValue(String value) { + for (ManagedSettingsResolvedSource v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ManagedSettingsResolvedSource value: " + value); + } +} diff --git a/java/src/generated/java/com/github/copilot/generated/McpOauthHttpResponse.java b/java/src/generated/java/com/github/copilot/generated/McpOauthHttpResponse.java new file mode 100644 index 0000000000..bed8e0ac62 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/McpOauthHttpResponse.java @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Raw HTTP response details from the OAuth auth challenge, as observed by the runtime. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record McpOauthHttpResponse( + /** HTTP status code returned with the auth challenge. */ + @JsonProperty("statusCode") Long statusCode, + /** HTTP response headers as observed by the runtime. Order and casing are transport-dependent, and duplicate header names may appear multiple times. */ + @JsonProperty("headers") List headers, + /** Complete UTF-8 response body for host-specific challenge handling, including an empty string for an empty body. Omitted when the complete body is not valid UTF-8; body read failures fail the HTTP operation rather than exposing a partial response. */ + @JsonProperty("body") String body +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/McpOauthRequiredEvent.java b/java/src/generated/java/com/github/copilot/generated/McpOauthRequiredEvent.java index 67413d382f..f21f84cd4b 100644 --- a/java/src/generated/java/com/github/copilot/generated/McpOauthRequiredEvent.java +++ b/java/src/generated/java/com/github/copilot/generated/McpOauthRequiredEvent.java @@ -44,6 +44,8 @@ public record McpOauthRequiredEventData( @JsonProperty("staticClientConfig") McpOauthRequiredStaticClientConfig staticClientConfig, /** OAuth WWW-Authenticate parameters parsed from the auth challenge, if available */ @JsonProperty("wwwAuthenticateParams") McpOauthWWWAuthenticateParams wwwAuthenticateParams, + /** Raw HTTP response details from the OAuth auth challenge, as observed by the runtime. Header order and casing are transport-dependent, and duplicate header names may appear multiple times. */ + @JsonProperty("httpResponse") McpOauthHttpResponse httpResponse, /** Raw OAuth protected-resource metadata document fetched for the MCP server, if available */ @JsonProperty("resourceMetadata") String resourceMetadata, /** Why the runtime is requesting host-provided OAuth credentials. */ diff --git a/java/src/generated/java/com/github/copilot/generated/McpPromptsListChangedEvent.java b/java/src/generated/java/com/github/copilot/generated/McpPromptsListChangedEvent.java index 3f572f087f..805328d3c1 100644 --- a/java/src/generated/java/com/github/copilot/generated/McpPromptsListChangedEvent.java +++ b/java/src/generated/java/com/github/copilot/generated/McpPromptsListChangedEvent.java @@ -13,7 +13,7 @@ import javax.annotation.processing.Generated; /** - * Session event "mcp.prompts.list_changed". Payload of MCP `list_changed` notification events, emitted when an MCP server announces at runtime that one of its advertised lists changed. + * Session event "mcp.prompts.list_changed". Payload identifying the MCP server associated with a list change. * @since 1.0.0 */ @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/java/src/generated/java/com/github/copilot/generated/McpResourcesListChangedEvent.java b/java/src/generated/java/com/github/copilot/generated/McpResourcesListChangedEvent.java index 5e23be776c..f1a613b6fa 100644 --- a/java/src/generated/java/com/github/copilot/generated/McpResourcesListChangedEvent.java +++ b/java/src/generated/java/com/github/copilot/generated/McpResourcesListChangedEvent.java @@ -13,7 +13,7 @@ import javax.annotation.processing.Generated; /** - * Session event "mcp.resources.list_changed". Payload of MCP `list_changed` notification events, emitted when an MCP server announces at runtime that one of its advertised lists changed. + * Session event "mcp.resources.list_changed". Payload identifying the MCP server associated with a list change. * @since 1.0.0 */ @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/java/src/generated/java/com/github/copilot/generated/McpToolsListChangedEvent.java b/java/src/generated/java/com/github/copilot/generated/McpToolsListChangedEvent.java index ae096303ca..4255b8544f 100644 --- a/java/src/generated/java/com/github/copilot/generated/McpToolsListChangedEvent.java +++ b/java/src/generated/java/com/github/copilot/generated/McpToolsListChangedEvent.java @@ -13,7 +13,7 @@ import javax.annotation.processing.Generated; /** - * Session event "mcp.tools.list_changed". Payload of MCP `list_changed` notification events, emitted when an MCP server announces at runtime that one of its advertised lists changed. + * Session event "mcp.tools.list_changed". Payload identifying the MCP server associated with a list change. * @since 1.0.0 */ @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/java/src/generated/java/com/github/copilot/generated/SessionEvent.java b/java/src/generated/java/com/github/copilot/generated/SessionEvent.java index b6fdc56e9e..d15da0c41c 100644 --- a/java/src/generated/java/com/github/copilot/generated/SessionEvent.java +++ b/java/src/generated/java/com/github/copilot/generated/SessionEvent.java @@ -58,6 +58,7 @@ @JsonSubTypes.Type(value = PendingMessagesModifiedEvent.class, name = "pending_messages.modified"), @JsonSubTypes.Type(value = AssistantTurnStartEvent.class, name = "assistant.turn_start"), @JsonSubTypes.Type(value = AssistantIntentEvent.class, name = "assistant.intent"), + @JsonSubTypes.Type(value = AssistantServerToolProgressEvent.class, name = "assistant.server_tool_progress"), @JsonSubTypes.Type(value = AssistantReasoningEvent.class, name = "assistant.reasoning"), @JsonSubTypes.Type(value = AssistantReasoningDeltaEvent.class, name = "assistant.reasoning_delta"), @JsonSubTypes.Type(value = AssistantToolCallDeltaEvent.class, name = "assistant.tool_call_delta"), @@ -110,6 +111,7 @@ @JsonSubTypes.Type(value = SessionLimitsExhaustedRequestedEvent.class, name = "session_limits_exhausted.requested"), @JsonSubTypes.Type(value = SessionLimitsExhaustedCompletedEvent.class, name = "session_limits_exhausted.completed"), @JsonSubTypes.Type(value = SessionAutoModeResolvedEvent.class, name = "session.auto_mode_resolved"), + @JsonSubTypes.Type(value = SessionManagedSettingsResolvedEvent.class, name = "session.managed_settings_resolved"), @JsonSubTypes.Type(value = CommandsChangedEvent.class, name = "commands.changed"), @JsonSubTypes.Type(value = CapabilitiesChangedEvent.class, name = "capabilities.changed"), @JsonSubTypes.Type(value = ExitPlanModeRequestedEvent.class, name = "exit_plan_mode.requested"), @@ -168,6 +170,7 @@ public abstract sealed class SessionEvent permits PendingMessagesModifiedEvent, AssistantTurnStartEvent, AssistantIntentEvent, + AssistantServerToolProgressEvent, AssistantReasoningEvent, AssistantReasoningDeltaEvent, AssistantToolCallDeltaEvent, @@ -220,6 +223,7 @@ public abstract sealed class SessionEvent permits SessionLimitsExhaustedRequestedEvent, SessionLimitsExhaustedCompletedEvent, SessionAutoModeResolvedEvent, + SessionManagedSettingsResolvedEvent, CommandsChangedEvent, CapabilitiesChangedEvent, ExitPlanModeRequestedEvent, diff --git a/java/src/generated/java/com/github/copilot/generated/SessionManagedSettingsResolvedEvent.java b/java/src/generated/java/com/github/copilot/generated/SessionManagedSettingsResolvedEvent.java new file mode 100644 index 0000000000..7cc495269f --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/SessionManagedSettingsResolvedEvent.java @@ -0,0 +1,54 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Session event "session.managed_settings_resolved". Enterprise managed-settings resolution: the effective managed settings the session applied and where they came from, so SDK clients can show users what is enterprise-managed and by which authority. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted; for a session-independent pull, use the SDK `getManagedSettings()` API, which returns the identical payload. Managed settings have a single authoritative source, so the highest-authority present layer (server > device) wins wholesale; `bypassPermissionsDisabled` is deny-wins across layers. Marked experimental while the managed-settings surface stabilizes. + * @since 1.0.0 + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class SessionManagedSettingsResolvedEvent extends SessionEvent { + + @Override + public String getType() { return "session.managed_settings_resolved"; } + + @JsonProperty("data") + private SessionManagedSettingsResolvedEventData data; + + public SessionManagedSettingsResolvedEventData getData() { return data; } + public void setData(SessionManagedSettingsResolvedEventData data) { this.data = data; } + + /** Data payload for {@link SessionManagedSettingsResolvedEvent}. */ + @JsonIgnoreProperties(ignoreUnknown = true) + @JsonInclude(JsonInclude.Include.NON_NULL) + public record SessionManagedSettingsResolvedEventData( + /** Which channel supplied the effective managed settings (the winning layer), or `none` when no policy is in force */ + @JsonProperty("source") ManagedSettingsResolvedSource source, + /** Whether the server (account/org) managed-settings layer was present */ + @JsonProperty("serverManaged") Boolean serverManaged, + /** Whether the device (MDM/plist/registry/file) managed-settings layer was present */ + @JsonProperty("deviceManaged") Boolean deviceManaged, + /** Whether managed policy could not be determined (e.g. a failed server fetch) and the session fell back to the fail-closed restriction. When true, restrictions such as disabling bypass-permissions are enforced even though `settings` may be absent. */ + @JsonProperty("failClosed") Boolean failClosed, + /** Whether enterprise policy disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers, and forced on when `failClosed` is true. */ + @JsonProperty("bypassPermissionsDisabled") Boolean bypassPermissionsDisabled, + /** The setting keys under enterprise management in the effective managed settings (e.g. `model`, `enabledPlugins`, `permissions`). Empty when no managed settings are in force. */ + @JsonProperty("managedKeys") List managedKeys, + /** The effective (resolved) managed settings values, so clients can render exactly what is enforced. Absent when no managed policy is in force. */ + @JsonProperty("settings") Object settings + ) { + } +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/HookInvokeRequest.java b/java/src/generated/java/com/github/copilot/generated/rpc/HookInvokeRequest.java new file mode 100644 index 0000000000..9ed02d28b0 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/HookInvokeRequest.java @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Runtime-owned wire payload for a server-to-client hook callback invocation. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record HookInvokeRequest( + @JsonProperty("sessionId") String sessionId, + @JsonProperty("hookType") HookType hookType, + @JsonProperty("input") Object input +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/HookType.java b/java/src/generated/java/com/github/copilot/generated/rpc/HookType.java new file mode 100644 index 0000000000..006f8a7c1f --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/HookType.java @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * Hook event name dispatched through the SDK callback transport. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum HookType { + /** The {@code preToolUse} variant. */ + PRETOOLUSE("preToolUse"), + /** The {@code preMcpToolCall} variant. */ + PREMCPTOOLCALL("preMcpToolCall"), + /** The {@code postToolUse} variant. */ + POSTTOOLUSE("postToolUse"), + /** The {@code postToolUseFailure} variant. */ + POSTTOOLUSEFAILURE("postToolUseFailure"), + /** The {@code userPromptSubmitted} variant. */ + USERPROMPTSUBMITTED("userPromptSubmitted"), + /** The {@code sessionStart} variant. */ + SESSIONSTART("sessionStart"), + /** The {@code sessionEnd} variant. */ + SESSIONEND("sessionEnd"), + /** The {@code postResult} variant. */ + POSTRESULT("postResult"), + /** The {@code prePRDescription} variant. */ + PREPRDESCRIPTION("prePRDescription"), + /** The {@code errorOccurred} variant. */ + ERROROCCURRED("errorOccurred"), + /** The {@code agentStop} variant. */ + AGENTSTOP("agentStop"), + /** The {@code subagentStart} variant. */ + SUBAGENTSTART("subagentStart"), + /** The {@code subagentStop} variant. */ + SUBAGENTSTOP("subagentStop"), + /** The {@code preCompact} variant. */ + PRECOMPACT("preCompact"), + /** The {@code permissionRequest} variant. */ + PERMISSIONREQUEST("permissionRequest"), + /** The {@code notification} variant. */ + NOTIFICATION("notification"); + + private final String value; + HookType(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static HookType fromValue(String value) { + for (HookType v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown HookType value: " + value); + } +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/HooksInvokeResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/HooksInvokeResult.java new file mode 100644 index 0000000000..a111b7af4e --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/HooksInvokeResult.java @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Optional output returned by an SDK callback hook. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record HooksInvokeResult( + @JsonProperty("output") Object output +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpToolUi.java b/java/src/generated/java/com/github/copilot/generated/rpc/McpToolUi.java new file mode 100644 index 0000000000..2f4436ca42 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/McpToolUi.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Normalized MCP Apps discovery metadata from a tool's `_meta.ui` block. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record McpToolUi( + /** URI of the tool's MCP App resource, typically a `ui://` resource identifier. Use `session.mcp.resources.read` to fetch its HTML and resource metadata. */ + @JsonProperty("resourceUri") String resourceUri, + /** Tool visibility advertised by the server. When absent, MCP Apps defaults apply. */ + @JsonProperty("visibility") List visibility +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpToolUiVisibility.java b/java/src/generated/java/com/github/copilot/generated/rpc/McpToolUiVisibility.java new file mode 100644 index 0000000000..9e73f0c90c --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/McpToolUiVisibility.java @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * Consumer allowed to call an MCP tool. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum McpToolUiVisibility { + /** The {@code model} variant. */ + MODEL("model"), + /** The {@code app} variant. */ + APP("app"); + + private final String value; + McpToolUiVisibility(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static McpToolUiVisibility fromValue(String value) { + for (McpToolUiVisibility v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown McpToolUiVisibility value: " + value); + } +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpTools.java b/java/src/generated/java/com/github/copilot/generated/rpc/McpTools.java index 04d6881266..37782f6d31 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/McpTools.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/McpTools.java @@ -13,7 +13,7 @@ import javax.annotation.processing.Generated; /** - * MCP tool metadata with tool name and optional description. + * MCP tool metadata with tool name, optional description, and normalized MCP Apps discovery metadata. * * @since 1.0.0 */ @@ -24,6 +24,8 @@ public record McpTools( /** Tool name. */ @JsonProperty("name") String name, /** Tool description, when provided. */ - @JsonProperty("description") String description + @JsonProperty("description") String description, + /** Normalized MCP Apps discovery metadata. An empty object indicates that a valid `_meta.ui` block was present without recognized fields. */ + @JsonProperty("ui") McpToolUi ui ) { } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionModelListResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionModelListResult.java index f3fd591f62..8951499ef4 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SessionModelListResult.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionModelListResult.java @@ -28,6 +28,8 @@ public record SessionModelListResult( /** Available models, ordered with the most preferred default first. Includes both Copilot (CAPI) models and any registry BYOK models; a BYOK model appears under its provider-qualified selection id (`provider/id`). */ @JsonProperty("list") List list, + /** Cost categories for the full CAPI catalog, including picker-disabled models that Auto may select. Metadata only; entries absent from `list` are not manually selectable. */ + @JsonProperty("modelPriceCategories") List modelPriceCategories, /** Per-quota snapshots returned alongside the model list, keyed by quota type. */ @JsonProperty("quotaSnapshots") Map quotaSnapshots ) { diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionModelPriceCategory.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionModelPriceCategory.java new file mode 100644 index 0000000000..295f6a01f0 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionModelPriceCategory.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Cost-category metadata for a CAPI model. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SessionModelPriceCategory( + @JsonProperty("id") String id, + @JsonProperty("priceCategory") ModelPickerPriceCategory priceCategory +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsUpdateResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsUpdateResult.java index a13e0d0d5e..3d7d274610 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsUpdateResult.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsUpdateResult.java @@ -25,6 +25,8 @@ @JsonIgnoreProperties(ignoreUnknown = true) public record SessionOptionsUpdateResult( /** Whether the operation succeeded */ - @JsonProperty("success") Boolean success + @JsonProperty("success") Boolean success, + /** Number of hooks loaded from installed plugins, returned when installedPlugins is updated */ + @JsonProperty("pluginHookCount") Long pluginHookCount ) { } diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index c867ee4b14..799053c0fa 100644 --- a/nodejs/package-lock.json +++ b/nodejs/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0-dev", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.71-2", + "@github/copilot": "^1.0.71", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" @@ -700,9 +700,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.71-2", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.71-2.tgz", - "integrity": "sha512-En1onRCWjPy64wnjB9B6T6nXgPI7/myHksMotpKHzh8+CnVbaYQr2n/rBKM1BVScWgpA0zn19HmKZZlg82LW7A==", + "version": "1.0.71", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.71.tgz", + "integrity": "sha512-F3axBi+sXSLYDJbxCBW36bM6MYKNC2rlyAf3Ivo/MjiHKKJW7j5AmaR1IRYS9Gt8r9mxOwWFM1cJFA+CuLaR8g==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "detect-libc": "^2.1.2" @@ -711,20 +711,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.71-2", - "@github/copilot-darwin-x64": "1.0.71-2", - "@github/copilot-linux-arm64": "1.0.71-2", - "@github/copilot-linux-x64": "1.0.71-2", - "@github/copilot-linuxmusl-arm64": "1.0.71-2", - "@github/copilot-linuxmusl-x64": "1.0.71-2", - "@github/copilot-win32-arm64": "1.0.71-2", - "@github/copilot-win32-x64": "1.0.71-2" + "@github/copilot-darwin-arm64": "1.0.71", + "@github/copilot-darwin-x64": "1.0.71", + "@github/copilot-linux-arm64": "1.0.71", + "@github/copilot-linux-x64": "1.0.71", + "@github/copilot-linuxmusl-arm64": "1.0.71", + "@github/copilot-linuxmusl-x64": "1.0.71", + "@github/copilot-win32-arm64": "1.0.71", + "@github/copilot-win32-x64": "1.0.71" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.71-2", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.71-2.tgz", - "integrity": "sha512-6DA1W3RFbyDPL/MXPeAzM9HxS7hvZOnToJHupGf4QGGs4dG2dzmTyEv0LkD4rFtyc1dx6QtyOjRt5vUsWw39Xw==", + "version": "1.0.71", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.71.tgz", + "integrity": "sha512-mEWzyqbqRAWgyU7i2uuSRoVPx/TwaFQX0nZmw0bc30aJ0BnO7cy2kYQyCHw8ykmf/tfxT0xauZ6k0BOFmWizzQ==", "cpu": [ "arm64" ], @@ -738,9 +738,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.71-2", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.71-2.tgz", - "integrity": "sha512-u/DWMhTCFaGf9TE70IgXKk0/9kWiijiHfEMVRqedbzdUNxGQ5u4lsaquEirHj3sSegVw8MZzgfKAzrT9CTr0aA==", + "version": "1.0.71", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.71.tgz", + "integrity": "sha512-Md9yEg406OBVBx3w4PeEj62TubulVLBcHleqmCoOoUmPgUxPZotUbrqz3rtbzADbXfrrD7JWvVsbd2UiNL194w==", "cpu": [ "x64" ], @@ -754,9 +754,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.71-2", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.71-2.tgz", - "integrity": "sha512-xKKk5o+zFJZ3EcoDOiCOdn1sbc8N/tHkn2j2sFQahE2SDp18ZpA2lzZp6XZ32VgxQJx0qlhPWh5BRn32Sc9csw==", + "version": "1.0.71", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.71.tgz", + "integrity": "sha512-ykLJYOqBj3jRB5IJCDugLClAqbr7DmtTbUjlNY7+Jdq/n6i+d7xUQGclf1IWL5gnxbGQVAf+zkToD+sRM389Kg==", "cpu": [ "arm64" ], @@ -770,9 +770,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.71-2", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.71-2.tgz", - "integrity": "sha512-We/kZNlEAXxhm9vMBae63Yy07RXUsTlOLsg5WrG7aNm0kh5ocUFpf5EodvtBbTmVIlGCvvm/RM1cYic6lPtD+w==", + "version": "1.0.71", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.71.tgz", + "integrity": "sha512-pC0FNHG+BBwZd6yZlM85kkAGN+uJhM6o+THi76N2GnnSxmw7+remb1mvYxdgRVbdCm+LBUIbCKRWJLuMwrfb6A==", "cpu": [ "x64" ], @@ -786,9 +786,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.71-2", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.71-2.tgz", - "integrity": "sha512-Hnyz/C4uNAXZVt6/RMtQBI89W0fxvUizm0mlp/ZohSthN03fv/PppbEsY7U5WqbTuDBuOKIU4kf3fCDC2elMCQ==", + "version": "1.0.71", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.71.tgz", + "integrity": "sha512-hBmDljFTjacxqZTasCEy43H8EIzuXB/hHEBBCMFjhB9J00nIxsO6Dh0woTifKpx7knTYZdpTjjca3D0pAoZlUA==", "cpu": [ "arm64" ], @@ -802,9 +802,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.71-2", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.71-2.tgz", - "integrity": "sha512-CQDeUqq2wbM+a/Tec68O+6Gp8f3cUZ5DLAqcNwxzN+86b5Gsu9xyGMV2eIF/sEYxakhY4mpHhjwdZBySue7IBA==", + "version": "1.0.71", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.71.tgz", + "integrity": "sha512-CfTXU8pa5dxRz22xQzoi3TiG1PJo9+WR8PRDiPSdkIBSyPJ1NvX87DJmfXjTgeAfR+wkjt/p0keDCaBBVhNmUA==", "cpu": [ "x64" ], @@ -818,9 +818,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.71-2", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.71-2.tgz", - "integrity": "sha512-lb81urkKJ+yWIy62yw9NmyjX5eFiw0y2TVIkVje26ot+gCiCJPisqyRWwhkfq0g/YKRbS8uoX4r+KHAhRH1wlg==", + "version": "1.0.71", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.71.tgz", + "integrity": "sha512-+HI1DokixXhHUahj06Fw67ZAigBuXKC58BFma4UJOGrQsDgwOSbqeTQHCw6vuymzjKlg3sactfsCUTaefkjscQ==", "cpu": [ "arm64" ], @@ -834,9 +834,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.71-2", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.71-2.tgz", - "integrity": "sha512-j3mQO2OUVoO+ZGE5KGF3iiekTikQZDQTnZO9RquWPXLwQs6ZdgRw9pPhbpXh0eoM1osZAW1/tmafcyYNpdBgWA==", + "version": "1.0.71", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.71.tgz", + "integrity": "sha512-02kXOBd9CwBbCaztuf71WYWn+uGapCuiaasomN4tcMH3HBVZ4gi3J0ZUoRcgcS80xh81uQyeBHbnUKzb/RE/9A==", "cpu": [ "x64" ], diff --git a/nodejs/package.json b/nodejs/package.json index 072dd79ac8..4f588640a8 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -56,7 +56,7 @@ "author": "GitHub", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.71-2", + "@github/copilot": "^1.0.71", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" diff --git a/nodejs/samples/package-lock.json b/nodejs/samples/package-lock.json index 1560ada99e..89c74c1535 100644 --- a/nodejs/samples/package-lock.json +++ b/nodejs/samples/package-lock.json @@ -18,7 +18,7 @@ "version": "0.0.0-dev", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.71-2", + "@github/copilot": "^1.0.71", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index 002836ad33..61f4a99416 100644 --- a/nodejs/src/client.ts +++ b/nodejs/src/client.ts @@ -830,6 +830,11 @@ export class CopilotClient { private setupClientGlobalHandlers(): void { const handlers: import("./generated/rpc.js").ClientGlobalApiHandlers = {}; + // `hooks.invoke` is a client-global RPC method whose payload carries a + // `sessionId`; route each invocation to the matching session's dispatcher. + handlers.hooks = { + invoke: async (params) => await this.handleHooksInvoke(params), + }; if (this.requestHandler) { handlers.llmInference = createCopilotRequestAdapter(this.requestHandler, () => { if (!this.connection) { @@ -2796,15 +2801,6 @@ export class CopilotClient { await this.handleAutoModeSwitchRequest(params) ); - this.connection.onRequest( - "hooks.invoke", - async (params: { - sessionId: string; - hookType: string; - input: unknown; - }): Promise<{ output?: unknown }> => await this.handleHooksInvoke(params) - ); - this.connection.onRequest( "systemMessage.transform", async (params: { diff --git a/nodejs/src/generated/rpc.ts b/nodejs/src/generated/rpc.ts index 2abba23d5d..255a769d55 100644 --- a/nodejs/src/generated/rpc.ts +++ b/nodejs/src/generated/rpc.ts @@ -521,6 +521,47 @@ export type FilterMapping = [k: string]: ContentFilterMode; } | ContentFilterMode; +/** + * Hook event name dispatched through the SDK callback transport. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "HookType". + */ +/** @experimental */ +/** @internal */ +export type HookType = + /** Runs before a tool is invoked. */ + | "preToolUse" + /** Runs before an MCP tool is invoked. */ + | "preMcpToolCall" + /** Runs after a tool completes successfully. */ + | "postToolUse" + /** Runs after a tool fails. */ + | "postToolUseFailure" + /** Runs after the user submits a prompt. */ + | "userPromptSubmitted" + /** Runs when a session starts. */ + | "sessionStart" + /** Runs when a session ends. */ + | "sessionEnd" + /** Runs after an agent result is produced. */ + | "postResult" + /** Runs before a pull request description is generated. */ + | "prePRDescription" + /** Runs when the agent encounters an error. */ + | "errorOccurred" + /** Runs when the agent stops. */ + | "agentStop" + /** Runs when a subagent starts. */ + | "subagentStart" + /** Runs when a subagent stops. */ + | "subagentStop" + /** Runs before conversation context is compacted. */ + | "preCompact" + /** Runs when the agent requests permission. */ + | "permissionRequest" + /** Runs when the agent emits a notification. */ + | "notification"; /** * Source for direct repo installs (when marketplace is empty) * @@ -817,6 +858,18 @@ export type McpHeadersHandlePendingHeadersRefreshRequest = | { kind: "none"; }; +/** + * Consumer allowed to call an MCP tool. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "McpToolUiVisibility". + */ +/** @experimental */ +export type McpToolUiVisibility = + /** The model may call the tool. */ + | "model" + /** An MCP App view may call the tool. */ + | "app"; /** * Host response to the pending OAuth request. * @@ -5130,6 +5183,30 @@ export interface HistoryTruncateResult { */ eventsRemoved: number; } +/** + * Runtime-owned wire payload for a server-to-client hook callback invocation. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "HookInvokeRequest". + */ +/** @experimental */ +/** @internal */ +export interface HookInvokeRequest { + sessionId: string; + hookType: HookType; + input: unknown; +} +/** + * Optional output returned by an SDK callback hook. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "HookInvokeResponse". + */ +/** @experimental */ +/** @internal */ +export interface HookInvokeResponse { + output?: unknown; +} /** * Installed plugin record from global state, with marketplace, version, install time, enabled state, cache path, and source. * @@ -6628,7 +6705,7 @@ export interface McpListToolsResult { tools: McpTools[]; } /** - * MCP tool metadata with tool name and optional description. + * MCP tool metadata with tool name, optional description, and normalized MCP Apps discovery metadata. * * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "McpTools". @@ -6643,6 +6720,24 @@ export interface McpTools { * Tool description, when provided. */ description?: string; + ui?: McpToolUi; +} +/** + * Normalized MCP Apps discovery metadata from a tool's `_meta.ui` block. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "McpToolUi". + */ +/** @experimental */ +export interface McpToolUi { + /** + * URI of the tool's MCP App resource, typically a `ui://` resource identifier. Use `session.mcp.resources.read` to fetch its HTML and resource metadata. + */ + resourceUri?: string; + /** + * Tool visibility advertised by the server. When absent, MCP Apps defaults apply. + */ + visibility?: McpToolUiVisibility[]; } /** * Pending MCP OAuth request ID and host-provided token or cancellation response. @@ -12136,6 +12231,10 @@ export interface SessionModelList { * Available models, ordered with the most preferred default first. Includes both Copilot (CAPI) models and any registry BYOK models; a BYOK model appears under its provider-qualified selection id (`provider/id`). */ list: unknown[]; + /** + * Cost categories for the full CAPI catalog, including picker-disabled models that Auto may select. Metadata only; entries absent from `list` are not manually selectable. + */ + modelPriceCategories?: SessionModelPriceCategory[]; /** * Per-quota snapshots returned alongside the model list, keyed by quota type. */ @@ -12143,6 +12242,17 @@ export interface SessionModelList { [k: string]: unknown | undefined; }; } +/** + * Cost-category metadata for a CAPI model. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SessionModelPriceCategory". + */ +/** @experimental */ +export interface SessionModelPriceCategory { + id: string; + priceCategory: ModelPickerPriceCategory; +} /** * Session construction options. * @@ -13522,6 +13632,10 @@ export interface SessionUpdateOptionsResult { * Whether the operation succeeded */ success: boolean; + /** + * Number of hooks loaded from installed plugins, returned when installedPlugins is updated + */ + pluginHookCount?: number; } /** * User-requested shell execution cancellation handle. @@ -16961,7 +17075,7 @@ export function createSessionRpc(connection: MessageConnection, sessionId: strin list: async (): Promise => connection.sendRequest("session.mcp.list", { sessionId }), /** - * Lists the tools exposed by a connected MCP server on this session's host. + * Lists the tools exposed by a connected MCP server on this session's host. This performs a live `tools/list` request. Tool UI metadata is returned independently of whether MCP Apps rendering is enabled for the session. * * @param params Server name whose tool list should be returned. * @@ -18269,6 +18383,19 @@ export function registerClientSessionApiHandlers( }); } +/** Handler for `hooks` client global API methods. */ +/** @experimental */ +export interface HooksHandler { + /** + * Dispatches one SDK callback hook from the runtime to the connection that registered it. Internal transport plumbing: clients opt in through session initialization and the Rust hook processor owns ordering, policy, timeout, and callback routing. + * + * @param params Runtime-owned wire payload for a server-to-client hook callback invocation. + * + * @returns Optional output returned by an SDK callback hook. + */ + invoke(params: HookInvokeRequest): Promise; +} + /** Handler for `llmInference` client global API methods. */ /** @experimental */ export interface LlmInferenceHandler { @@ -18303,6 +18430,7 @@ export interface GitHubTelemetryHandler { /** All client global API handler groups. */ export interface ClientGlobalApiHandlers { + hooks?: HooksHandler; llmInference?: LlmInferenceHandler; gitHubTelemetry?: GitHubTelemetryHandler; } @@ -18318,6 +18446,11 @@ export function registerClientGlobalApiHandlers( connection: MessageConnection, handlers: ClientGlobalApiHandlers, ): void { + connection.onRequest("hooks.invoke", async (params: HookInvokeRequest) => { + const handler = handlers.hooks; + if (!handler) throw new Error("No hooks client-global handler registered"); + return handler.invoke(params); + }); connection.onRequest("llmInference.httpRequestStart", async (params: LlmInferenceHttpRequestStartRequest) => { const handler = handlers.llmInference; if (!handler) throw new Error("No llmInference client-global handler registered"); diff --git a/nodejs/src/generated/session-events.ts b/nodejs/src/generated/session-events.ts index 70e23d2874..7581545a8e 100644 --- a/nodejs/src/generated/session-events.ts +++ b/nodejs/src/generated/session-events.ts @@ -40,6 +40,7 @@ export type SessionEvent = | PendingMessagesModifiedEvent | AssistantTurnStartEvent | AssistantIntentEvent + | AssistantServerToolProgressEvent | AssistantReasoningEvent | AssistantReasoningDeltaEvent | AssistantToolCallDeltaEvent @@ -92,6 +93,7 @@ export type SessionEvent = | SessionLimitsExhaustedRequestedEvent | SessionLimitsExhaustedCompletedEvent | AutoModeResolvedEvent + | ManagedSettingsResolvedEvent | CommandsChangedEvent | CapabilitiesChangedEvent | ExitPlanModeRequestedEvent @@ -645,6 +647,16 @@ export type AutoModeResolvedReasoningBucket = | "medium" /** The request looks high-reasoning; a stronger model is appropriate. */ | "high"; +/** + * Which channel supplied the effective enterprise managed settings (highest-authority present layer wins wholesale) + */ +export type ManagedSettingsResolvedSource = + /** Account/org policy self-fetched from the GitHub managed-settings endpoint (higher authority). */ + | "server" + /** Device-level MDM policy discovered from plist/registry/file (lower authority). */ + | "device" + /** No managed policy is in force (no layer contributed). */ + | "none"; /** * Exit plan mode action */ @@ -3150,6 +3162,53 @@ export interface AssistantIntentData { */ intent: string; } +/** + * Session event "assistant.server_tool_progress". Live progress signal for a provider-hosted server tool (e.g. hosted web search) while it runs, before the finalized serverTools envelope lands on the terminal assistant.message + */ +export interface AssistantServerToolProgressEvent { + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; + data: AssistantServerToolProgressData; + /** + * Always true for events that are transient and not persisted to the session event log on disk. + */ + ephemeral: true; + /** + * Unique event identifier (UUID v4), generated when the event is emitted + */ + id: string; + /** + * ID of the chronologically preceding event in the session, forming a linked chain. Null for the first event. + */ + parentId: string | null; + /** + * ISO 8601 timestamp when the event was created + */ + timestamp: string; + /** + * Type discriminator. Always "assistant.server_tool_progress". + */ + type: "assistant.server_tool_progress"; +} +/** + * Live progress signal for a provider-hosted server tool (e.g. hosted web search) while it runs, before the finalized serverTools envelope lands on the terminal assistant.message + */ +export interface AssistantServerToolProgressData { + /** + * Kind of hosted server tool that is running. Only `web_search` is emitted today. + */ + kind: string; + /** + * Position of the hosted tool call in the response output. Stable across the call's lifecycle events (unlike the provider's per-event item id, which CAPI rotates), so the host keys the live in-progress row on it. + */ + outputIndex: number; + /** + * Lifecycle status of the hosted call: `in_progress`, `searching`, or `completed`. + */ + status: string; +} /** * Session event "assistant.reasoning". Assistant reasoning content for timeline display with complete thinking text */ @@ -7147,6 +7206,7 @@ export interface McpOauthRequiredEvent { * OAuth authentication request for an MCP server */ export interface McpOauthRequiredData { + httpResponse?: McpOauthHttpResponse; reason: McpOauthRequestReason; /** * Unique identifier for this OAuth request; used to respond via session.mcp.oauth.handlePendingRequest @@ -7167,6 +7227,36 @@ export interface McpOauthRequiredData { staticClientConfig?: McpOauthRequiredStaticClientConfig; wwwAuthenticateParams?: McpOauthWWWAuthenticateParams; } +/** + * Raw HTTP response details from the OAuth auth challenge, as observed by the runtime. + */ +export interface McpOauthHttpResponse { + /** + * Complete UTF-8 response body for host-specific challenge handling, including an empty string for an empty body. Omitted when the complete body is not valid UTF-8; body read failures fail the HTTP operation rather than exposing a partial response. + */ + body?: string; + /** + * HTTP response headers as observed by the runtime. Order and casing are transport-dependent, and duplicate header names may appear multiple times. + */ + headers: HeaderEntry[]; + /** + * HTTP status code returned with the auth challenge. + */ + statusCode: number; +} +/** + * Single HTTP header entry as a name/value pair. + */ +export interface HeaderEntry { + /** + * HTTP response header name as observed by the runtime. + */ + name: string; + /** + * HTTP response header value as observed by the runtime. + */ + value: string; +} /** * Static OAuth client configuration, if the server specifies one */ @@ -7883,6 +7973,70 @@ export interface AutoModeResolvedData { predictedLabel?: string; reasoningBucket?: AutoModeResolvedReasoningBucket; } +/** + * Session event "session.managed_settings_resolved". Enterprise managed-settings resolution: the effective managed settings the session applied and where they came from, so SDK clients can show users what is enterprise-managed and by which authority. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted; for a session-independent pull, use the SDK `getManagedSettings()` API, which returns the identical payload. Managed settings have a single authoritative source, so the highest-authority present layer (server > device) wins wholesale; `bypassPermissionsDisabled` is deny-wins across layers. Marked experimental while the managed-settings surface stabilizes. + */ +/** @experimental */ +export interface ManagedSettingsResolvedEvent { + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; + data: ManagedSettingsResolvedData; + /** + * Always true for events that are transient and not persisted to the session event log on disk. + */ + ephemeral: true; + /** + * Unique event identifier (UUID v4), generated when the event is emitted + */ + id: string; + /** + * ID of the chronologically preceding event in the session, forming a linked chain. Null for the first event. + */ + parentId: string | null; + /** + * ISO 8601 timestamp when the event was created + */ + timestamp: string; + /** + * Type discriminator. Always "session.managed_settings_resolved". + */ + type: "session.managed_settings_resolved"; +} +/** + * Enterprise managed-settings resolution: the effective managed settings the session applied and where they came from, so SDK clients can show users what is enterprise-managed and by which authority. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted; for a session-independent pull, use the SDK `getManagedSettings()` API, which returns the identical payload. Managed settings have a single authoritative source, so the highest-authority present layer (server > device) wins wholesale; `bypassPermissionsDisabled` is deny-wins across layers. Marked experimental while the managed-settings surface stabilizes. + */ +/** @experimental */ +export interface ManagedSettingsResolvedData { + /** + * Whether enterprise policy disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers, and forced on when `failClosed` is true. + */ + bypassPermissionsDisabled: boolean; + /** + * Whether the device (MDM/plist/registry/file) managed-settings layer was present + */ + deviceManaged: boolean; + /** + * Whether managed policy could not be determined (e.g. a failed server fetch) and the session fell back to the fail-closed restriction. When true, restrictions such as disabling bypass-permissions are enforced even though `settings` may be absent. + */ + failClosed: boolean; + /** + * The setting keys under enterprise management in the effective managed settings (e.g. `model`, `enabledPlugins`, `permissions`). Empty when no managed settings are in force. + */ + managedKeys: string[]; + /** + * Whether the server (account/org) managed-settings layer was present + */ + serverManaged: boolean; + /** + * The effective (resolved) managed settings values, so clients can render exactly what is enforced. Absent when no managed policy is in force. + */ + settings?: { + [k: string]: unknown | undefined; + }; + source: ManagedSettingsResolvedSource; +} /** * Session event "commands.changed". SDK command registration change notification */ @@ -8426,7 +8580,7 @@ export interface McpServerStatusChangedData { status: McpServerStatus; } /** - * Session event "mcp.tools.list_changed". Payload of MCP `list_changed` notification events, emitted when an MCP server announces at runtime that one of its advertised lists changed. + * Session event "mcp.tools.list_changed". Payload identifying the MCP server associated with a list change. */ export interface McpToolsListChangedEvent { /** @@ -8456,7 +8610,7 @@ export interface McpToolsListChangedEvent { type: "mcp.tools.list_changed"; } /** - * Payload of MCP `list_changed` notification events, emitted when an MCP server announces at runtime that one of its advertised lists changed. + * Payload identifying the MCP server associated with a list change. */ export interface McpListChangedData { /** @@ -8465,7 +8619,7 @@ export interface McpListChangedData { serverName: string; } /** - * Session event "mcp.resources.list_changed". Payload of MCP `list_changed` notification events, emitted when an MCP server announces at runtime that one of its advertised lists changed. + * Session event "mcp.resources.list_changed". Payload identifying the MCP server associated with a list change. */ export interface McpResourcesListChangedEvent { /** @@ -8495,7 +8649,7 @@ export interface McpResourcesListChangedEvent { type: "mcp.resources.list_changed"; } /** - * Session event "mcp.prompts.list_changed". Payload of MCP `list_changed` notification events, emitted when an MCP server announces at runtime that one of its advertised lists changed. + * Session event "mcp.prompts.list_changed". Payload identifying the MCP server associated with a list change. */ export interface McpPromptsListChangedEvent { /** diff --git a/nodejs/test/client.test.ts b/nodejs/test/client.test.ts index 2d93e9e075..2585542b4b 100644 --- a/nodejs/test/client.test.ts +++ b/nodejs/test/client.test.ts @@ -3133,7 +3133,7 @@ describe("CopilotClient", () => { it("routes hooks.invoke JSON-RPC requests to the SessionHooks handler", async () => { // Validates the full JSON-RPC entry point used by the CLI: - // CopilotClient.handleHooksInvoke({sessionId, hookType, input}) + // clientGlobalHandlers.hooks.invoke({sessionId, hookType, input}) // → CopilotSession._handleHooksInvoke(hookType, input) // → SessionHooks.onPostToolUseFailure(normalizedInput, {sessionId}) // @@ -3164,7 +3164,7 @@ describe("CopilotClient", () => { cwd: "/tmp", }; - const response = await (client as any).handleHooksInvoke({ + const response = await (client as any).clientGlobalHandlers.hooks.invoke({ sessionId: session.sessionId, hookType: "postToolUseFailure", input: failureInput, diff --git a/python/copilot/client.py b/python/copilot/client.py index a56748478d..bb0d486d8b 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -73,6 +73,8 @@ RemoteSessionMode, ServerRpc, _ConnectResult, + _HookInvokeRequest, + _HookInvokeResponse, from_datetime, register_client_global_api_handlers, register_client_session_api_handlers, @@ -479,6 +481,25 @@ async def event(self, params: GitHubTelemetryNotification) -> None: logger.warning("Error handling gitHubTelemetry.event notification", exc_info=True) +class _HooksAdapter: + """Adapts session-scoped hook dispatch to the generated ``HooksHandler`` protocol. + + ``hooks.invoke`` is a client-global RPC method whose payload carries a + ``sessionId``. This adapter routes each invocation to the matching session's + registered hook handlers. + """ + + def __init__(self, get_session: Callable[[str], CopilotSession | None]) -> None: + self._get_session = get_session + + async def invoke(self, params: _HookInvokeRequest) -> _HookInvokeResponse: + session = self._get_session(params.session_id) + if session is None: + raise ValueError(f"unknown session {params.session_id}") + output = await session._handle_hooks_invoke(params.hook_type.value, params.input) + return _HookInvokeResponse(output=output) + + @dataclass class _CopilotClientOptions: """Internal configuration carrier used by :class:`CopilotClient`. @@ -4072,7 +4093,6 @@ def handle_notification(method: str, params: dict): self._client.set_request_handler( "autoModeSwitch.request", self._handle_auto_mode_switch_request ) - self._client.set_request_handler("hooks.invoke", self._handle_hooks_invoke) self._client.set_request_handler( "systemMessage.transform", self._handle_system_message_transform ) @@ -4192,7 +4212,6 @@ def handle_notification(method: str, params: dict): self._client.set_request_handler( "autoModeSwitch.request", self._handle_auto_mode_switch_request ) - self._client.set_request_handler("hooks.invoke", self._handle_hooks_invoke) self._client.set_request_handler( "systemMessage.transform", self._handle_system_message_transform ) @@ -4282,16 +4301,19 @@ def _register_client_global_handlers(self) -> None: github_telemetry_adapter = None if self._on_github_telemetry is not None: github_telemetry_adapter = _GitHubTelemetryAdapter(self._on_github_telemetry) - if llm_inference_adapter is None and github_telemetry_adapter is None: - return register_client_global_api_handlers( self._client, ClientGlobalApiHandlers( + hooks=_HooksAdapter(self._get_session), llm_inference=llm_inference_adapter, git_hub_telemetry=github_telemetry_adapter, ), ) + def _get_session(self, session_id: str) -> CopilotSession | None: + with self._sessions_lock: + return self._sessions.get(session_id) + async def _set_llm_inference_provider(self) -> None: if self._request_handler is None or self._rpc is None: return @@ -4364,34 +4386,6 @@ async def _handle_auto_mode_switch_request(self, params: dict) -> dict: response = await session._handle_auto_mode_switch_request(params) return {"response": response} - async def _handle_hooks_invoke(self, params: dict) -> dict: - """ - Handle a hooks invocation from the CLI server. - - Args: - params: The hooks invocation parameters from the server. - - Returns: - A dict containing the hook output. - - Raises: - ValueError: If the request payload is invalid. - """ - session_id = params.get("sessionId") - hook_type = params.get("hookType") - input_data = params.get("input") - - if not session_id or not hook_type: - raise ValueError("invalid hooks invoke payload") - - with self._sessions_lock: - session = self._sessions.get(session_id) - if not session: - raise ValueError(f"unknown session {session_id}") - - output = await session._handle_hooks_invoke(hook_type, input_data) - return {"output": output} - async def _handle_system_message_transform(self, params: dict) -> dict: """Handle a systemMessage.transform request from the CLI server.""" session_id = params.get("sessionId") diff --git a/python/copilot/generated/rpc.py b/python/copilot/generated/rpc.py index f6b54b0b51..828eaa3ce4 100644 --- a/python/copilot/generated/rpc.py +++ b/python/copilot/generated/rpc.py @@ -2446,6 +2446,46 @@ def to_dict(self) -> dict: class HMACAuthInfoType(Enum): HMAC = "hmac" +# Internal: this type is an internal SDK API and is not part of the public surface. +class _HookType(Enum): + """Hook event name dispatched through the SDK callback transport.""" + + AGENT_STOP = "agentStop" + ERROR_OCCURRED = "errorOccurred" + NOTIFICATION = "notification" + PERMISSION_REQUEST = "permissionRequest" + POST_RESULT = "postResult" + POST_TOOL_USE = "postToolUse" + POST_TOOL_USE_FAILURE = "postToolUseFailure" + PRE_COMPACT = "preCompact" + PRE_MCP_TOOL_CALL = "preMcpToolCall" + PRE_PR_DESCRIPTION = "prePRDescription" + PRE_TOOL_USE = "preToolUse" + SESSION_END = "sessionEnd" + SESSION_START = "sessionStart" + SUBAGENT_START = "subagentStart" + SUBAGENT_STOP = "subagentStop" + USER_PROMPT_SUBMITTED = "userPromptSubmitted" + +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _HookInvokeResponse: + """Optional output returned by an SDK callback hook.""" + + output: Any = None + + @staticmethod + def from_dict(obj: Any) -> '_HookInvokeResponse': + assert isinstance(obj, dict) + output = obj.get("output") + return _HookInvokeResponse(output) + + def to_dict(self) -> dict: + result: dict = {} + if self.output is not None: + result["output"] = self.output + return result + class PurpleSource(Enum): GITHUB = "github" LOCAL = "local" @@ -3586,6 +3626,13 @@ def to_dict(self) -> dict: result["serverName"] = from_str(self.server_name) return result +# Experimental: this type is part of an experimental API and may change or be removed. +class MCPToolUIVisibility(Enum): + """Consumer allowed to call an MCP tool.""" + + APP = "app" + MODEL = "model" + class MCPOauthPendingRequestResponseKind(Enum): CANCELLED = "cancelled" TOKEN = "token" @@ -7535,33 +7582,6 @@ def to_dict(self) -> dict: result["startupPrompts"] = from_list(from_str, self.startup_prompts) return result -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class SessionModelList: - """The list of models available to this session.""" - - list: list[Any] - """Available models, ordered with the most preferred default first. Includes both Copilot - (CAPI) models and any registry BYOK models; a BYOK model appears under its - provider-qualified selection id (`provider/id`). - """ - quota_snapshots: dict[str, Any] | None = None - """Per-quota snapshots returned alongside the model list, keyed by quota type.""" - - @staticmethod - def from_dict(obj: Any) -> 'SessionModelList': - assert isinstance(obj, dict) - list = from_list(lambda x: x, obj.get("list")) - quota_snapshots = from_union([lambda x: from_dict(lambda x: x, x), from_none], obj.get("quotaSnapshots")) - return SessionModelList(list, quota_snapshots) - - def to_dict(self) -> dict: - result: dict = {} - result["list"] = from_list(lambda x: x, self.list) - if self.quota_snapshots is not None: - result["quotaSnapshots"] = from_union([lambda x: from_dict(lambda x: x, x), from_none], self.quota_snapshots) - return result - # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class SessionOpenOptionsAdditionalContentExclusionPolicyRuleSource: @@ -8013,15 +8033,21 @@ class SessionUpdateOptionsResult: success: bool """Whether the operation succeeded""" + plugin_hook_count: int | None = None + """Number of hooks loaded from installed plugins, returned when installedPlugins is updated""" + @staticmethod def from_dict(obj: Any) -> 'SessionUpdateOptionsResult': assert isinstance(obj, dict) success = from_bool(obj.get("success")) - return SessionUpdateOptionsResult(success) + plugin_hook_count = from_union([from_int, from_none], obj.get("pluginHookCount")) + return SessionUpdateOptionsResult(success, plugin_hook_count) def to_dict(self) -> dict: result: dict = {} result["success"] = from_bool(self.success) + if self.plugin_hook_count is not None: + result["pluginHookCount"] = from_union([from_int, from_none], self.plugin_hook_count) return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -11621,6 +11647,30 @@ def to_dict(self) -> dict: result["summaryContent"] = from_union([from_str, from_none], self.summary_content) return result +# Internal: this type is an internal SDK API and is not part of the public surface. +@dataclass +class _HookInvokeRequest: + """Runtime-owned wire payload for a server-to-client hook callback invocation.""" + + hook_type: _HookType + input: Any + session_id: str + + @staticmethod + def from_dict(obj: Any) -> '_HookInvokeRequest': + assert isinstance(obj, dict) + hook_type = _HookType(obj.get("hookType")) + input = obj.get("input") + session_id = from_str(obj.get("sessionId")) + return _HookInvokeRequest(hook_type, input, session_id) + + def to_dict(self) -> dict: + result: dict = {} + result["hookType"] = to_enum(_HookType, self.hook_type) + result["input"] = self.input + result["sessionId"] = from_str(self.session_id) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class InstalledPluginSource: @@ -13366,6 +13416,27 @@ def to_dict(self) -> dict: result["vision"] = from_union([lambda x: to_class(ModelCapabilitiesLimitsVision, x), from_none], self.vision) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class SessionModelPriceCategory: + """Cost-category metadata for a CAPI model.""" + + id: str + price_category: ModelPickerPriceCategory + + @staticmethod + def from_dict(obj: Any) -> 'SessionModelPriceCategory': + assert isinstance(obj, dict) + id = from_str(obj.get("id")) + price_category = ModelPickerPriceCategory(obj.get("priceCategory")) + return SessionModelPriceCategory(id, price_category) + + def to_dict(self) -> dict: + result: dict = {} + result["id"] = from_str(self.id) + result["priceCategory"] = to_enum(ModelPickerPriceCategory, self.price_category) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ModelPolicy: @@ -17155,27 +17226,32 @@ def to_dict(self) -> dict: # Experimental: this type is part of an experimental API and may change or be removed. @dataclass -class MCPTools: - """MCP tool metadata with tool name and optional description.""" - - name: str - """Tool name.""" +class MCPToolUI: + """Normalized MCP Apps discovery metadata. An empty object indicates that a valid `_meta.ui` + block was present without recognized fields. - description: str | None = None - """Tool description, when provided.""" + Normalized MCP Apps discovery metadata from a tool's `_meta.ui` block. + """ + resource_uri: str | None = None + """URI of the tool's MCP App resource, typically a `ui://` resource identifier. Use + `session.mcp.resources.read` to fetch its HTML and resource metadata. + """ + visibility: list[MCPToolUIVisibility] | None = None + """Tool visibility advertised by the server. When absent, MCP Apps defaults apply.""" @staticmethod - def from_dict(obj: Any) -> 'MCPTools': + def from_dict(obj: Any) -> 'MCPToolUI': assert isinstance(obj, dict) - name = from_str(obj.get("name")) - description = from_union([from_str, from_none], obj.get("description")) - return MCPTools(name, description) + resource_uri = from_union([from_str, from_none], obj.get("resourceUri")) + visibility = from_union([lambda x: from_list(MCPToolUIVisibility, x), from_none], obj.get("visibility")) + return MCPToolUI(resource_uri, visibility) def to_dict(self) -> dict: result: dict = {} - result["name"] = from_str(self.name) - if self.description is not None: - result["description"] = from_union([from_str, from_none], self.description) + if self.resource_uri is not None: + result["resourceUri"] = from_union([from_str, from_none], self.resource_uri) + if self.visibility is not None: + result["visibility"] = from_union([lambda x: from_list(lambda x: to_enum(MCPToolUIVisibility, x), x), from_none], self.visibility) return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -19489,6 +19565,40 @@ def to_dict(self) -> dict: result["tokenPrices"] = from_union([lambda x: to_class(ModelBillingTokenPrices, x), from_none], self.token_prices) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class SessionModelList: + """The list of models available to this session.""" + + list: list[Any] + """Available models, ordered with the most preferred default first. Includes both Copilot + (CAPI) models and any registry BYOK models; a BYOK model appears under its + provider-qualified selection id (`provider/id`). + """ + model_price_categories: list[SessionModelPriceCategory] | None = None + """Cost categories for the full CAPI catalog, including picker-disabled models that Auto may + select. Metadata only; entries absent from `list` are not manually selectable. + """ + quota_snapshots: dict[str, Any] | None = None + """Per-quota snapshots returned alongside the model list, keyed by quota type.""" + + @staticmethod + def from_dict(obj: Any) -> 'SessionModelList': + assert isinstance(obj, dict) + list = from_list(lambda x: x, obj.get("list")) + model_price_categories = from_union([lambda x: from_list(SessionModelPriceCategory.from_dict, x), from_none], obj.get("modelPriceCategories")) + quota_snapshots = from_union([lambda x: from_dict(lambda x: x, x), from_none], obj.get("quotaSnapshots")) + return SessionModelList(list, model_price_categories, quota_snapshots) + + def to_dict(self) -> dict: + result: dict = {} + result["list"] = from_list(lambda x: x, self.list) + if self.model_price_categories is not None: + result["modelPriceCategories"] = from_union([lambda x: from_list(lambda x: to_class(SessionModelPriceCategory, x), x), from_none], self.model_price_categories) + if self.quota_snapshots is not None: + result["quotaSnapshots"] = from_union([lambda x: from_dict(lambda x: x, x), from_none], self.quota_snapshots) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ModelCapabilitiesOverride: @@ -20306,21 +20416,36 @@ def to_dict(self) -> dict: # Experimental: this type is part of an experimental API and may change or be removed. @dataclass -class MCPListToolsResult: - """Tools exposed by the connected MCP server. Throws when the server is not connected.""" +class MCPTools: + """MCP tool metadata with tool name, optional description, and normalized MCP Apps discovery + metadata. + """ + name: str + """Tool name.""" - tools: list[MCPTools] - """Tools exposed by the server.""" + description: str | None = None + """Tool description, when provided.""" + + ui: MCPToolUI | None = None + """Normalized MCP Apps discovery metadata. An empty object indicates that a valid `_meta.ui` + block was present without recognized fields. + """ @staticmethod - def from_dict(obj: Any) -> 'MCPListToolsResult': + def from_dict(obj: Any) -> 'MCPTools': assert isinstance(obj, dict) - tools = from_list(MCPTools.from_dict, obj.get("tools")) - return MCPListToolsResult(tools) + name = from_str(obj.get("name")) + description = from_union([from_str, from_none], obj.get("description")) + ui = from_union([MCPToolUI.from_dict, from_none], obj.get("ui")) + return MCPTools(name, description, ui) def to_dict(self) -> dict: result: dict = {} - result["tools"] = from_list(lambda x: to_class(MCPTools, x), self.tools) + result["name"] = from_str(self.name) + if self.description is not None: + result["description"] = from_union([from_str, from_none], self.description) + if self.ui is not None: + result["ui"] = from_union([lambda x: to_class(MCPToolUI, x), from_none], self.ui) return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -21443,6 +21568,25 @@ def to_dict(self) -> dict: result["userPolicy"] = from_union([lambda x: to_class(SandboxConfigUserPolicy, x), from_none], self.user_policy) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class MCPListToolsResult: + """Tools exposed by the connected MCP server. Throws when the server is not connected.""" + + tools: list[MCPTools] + """Tools exposed by the server.""" + + @staticmethod + def from_dict(obj: Any) -> 'MCPListToolsResult': + assert isinstance(obj, dict) + tools = from_list(MCPTools.from_dict, obj.get("tools")) + return MCPListToolsResult(tools) + + def to_dict(self) -> dict: + result: dict = {} + result["tools"] = from_list(lambda x: to_class(MCPTools, x), self.tools) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class UIElicitationSchema: @@ -24366,6 +24510,9 @@ class RPC: history_truncate_request: HistoryTruncateRequest history_truncate_result: HistoryTruncateResult hmac_auth_info: HMACAuthInfo + hook_invoke_request: _HookInvokeRequest + hook_invoke_response: _HookInvokeResponse + hook_type: _HookType installed_plugin: InstalledPlugin installed_plugin_info: InstalledPluginInfo installed_plugin_source: InstalledPluginSource | str @@ -24497,6 +24644,8 @@ class RPC: mcp_start_servers_result: MCPStartServersResult mcp_stop_server_request: MCPStopServerRequest mcp_tools: MCPTools + mcp_tool_ui: MCPToolUI + mcp_tool_ui_visibility: MCPToolUIVisibility mcp_unregister_external_client_request: MCPUnregisterExternalClientRequest memory_configuration: MemoryConfiguration metadata_context_attribution_result: MetadataContextAttributionResult @@ -24821,6 +24970,7 @@ class RPC: session_metadata_snapshot: SessionMetadataSnapshot session_mode: SessionMode session_model_list: SessionModelList + session_model_price_category: SessionModelPriceCategory session_open_options: SessionOpenOptions session_open_options_additional_content_exclusion_policy: SessionOpenOptionsAdditionalContentExclusionPolicy session_open_options_additional_content_exclusion_policy_rule: SessionOpenOptionsAdditionalContentExclusionPolicyRule @@ -25211,6 +25361,9 @@ def from_dict(obj: Any) -> 'RPC': history_truncate_request = HistoryTruncateRequest.from_dict(obj.get("HistoryTruncateRequest")) history_truncate_result = HistoryTruncateResult.from_dict(obj.get("HistoryTruncateResult")) hmac_auth_info = HMACAuthInfo.from_dict(obj.get("HMACAuthInfo")) + hook_invoke_request = _HookInvokeRequest.from_dict(obj.get("HookInvokeRequest")) + hook_invoke_response = _HookInvokeResponse.from_dict(obj.get("HookInvokeResponse")) + hook_type = _HookType(obj.get("HookType")) installed_plugin = InstalledPlugin.from_dict(obj.get("InstalledPlugin")) installed_plugin_info = InstalledPluginInfo.from_dict(obj.get("InstalledPluginInfo")) installed_plugin_source = from_union([InstalledPluginSource.from_dict, from_str], obj.get("InstalledPluginSource")) @@ -25342,6 +25495,8 @@ def from_dict(obj: Any) -> 'RPC': mcp_start_servers_result = MCPStartServersResult.from_dict(obj.get("McpStartServersResult")) mcp_stop_server_request = MCPStopServerRequest.from_dict(obj.get("McpStopServerRequest")) mcp_tools = MCPTools.from_dict(obj.get("McpTools")) + mcp_tool_ui = MCPToolUI.from_dict(obj.get("McpToolUi")) + mcp_tool_ui_visibility = MCPToolUIVisibility(obj.get("McpToolUiVisibility")) mcp_unregister_external_client_request = MCPUnregisterExternalClientRequest.from_dict(obj.get("McpUnregisterExternalClientRequest")) memory_configuration = MemoryConfiguration.from_dict(obj.get("MemoryConfiguration")) metadata_context_attribution_result = MetadataContextAttributionResult.from_dict(obj.get("MetadataContextAttributionResult")) @@ -25666,6 +25821,7 @@ def from_dict(obj: Any) -> 'RPC': session_metadata_snapshot = SessionMetadataSnapshot.from_dict(obj.get("SessionMetadataSnapshot")) session_mode = SessionMode(obj.get("SessionMode")) session_model_list = SessionModelList.from_dict(obj.get("SessionModelList")) + session_model_price_category = SessionModelPriceCategory.from_dict(obj.get("SessionModelPriceCategory")) session_open_options = SessionOpenOptions.from_dict(obj.get("SessionOpenOptions")) session_open_options_additional_content_exclusion_policy = SessionOpenOptionsAdditionalContentExclusionPolicy.from_dict(obj.get("SessionOpenOptionsAdditionalContentExclusionPolicy")) session_open_options_additional_content_exclusion_policy_rule = SessionOpenOptionsAdditionalContentExclusionPolicyRule.from_dict(obj.get("SessionOpenOptionsAdditionalContentExclusionPolicyRule")) @@ -25892,7 +26048,7 @@ def from_dict(obj: Any) -> 'RPC': subagent_settings = from_union([SubagentSettings.from_dict, from_none], obj.get("SubagentSettings")) task_progress = from_union([TaskProgress.from_dict, from_none], obj.get("TaskProgress")) workspace_summary = from_union([WorkspaceSummary.from_dict, from_none], obj.get("WorkspaceSummary")) - return RPC(abort_request, abort_result, account_all_users, account_get_all_users_result, account_get_current_auth_result, account_get_quota_request, account_get_quota_result, account_login_request, account_login_result, account_logout_request, account_logout_result, account_quota_snapshot, adaptive_thinking_support, agent_discovery_path, agent_discovery_path_list, agent_discovery_path_scope, agent_get_current_result, agent_info, agent_info_source, agent_list, agent_registry_live_target_entry, agent_registry_live_target_entry_attention_kind, agent_registry_live_target_entry_kind, agent_registry_live_target_entry_last_terminal_event, agent_registry_live_target_entry_status, agent_registry_log_capture, agent_registry_log_capture_open_error_reason, agent_registry_spawn_error, agent_registry_spawn_permission_mode, agent_registry_spawn_registry_timeout, agent_registry_spawn_request, agent_registry_spawn_result, agent_registry_spawn_spawned, agent_registry_spawn_validation_error, agent_registry_spawn_validation_error_field, agent_registry_spawn_validation_error_reason, agent_reload_result, agents_discover_request, agent_select_request, agent_select_result, agents_get_discovery_paths_request, allow_all_permission_set_result, allow_all_permission_state, api_key_auth_info, auth_info, auth_info_type, cancel_user_requested_shell_command_result, canvas_action, canvas_action_invoke_request, canvas_action_invoke_result, canvas_close_request, canvas_host_context, canvas_host_context_capabilities, canvas_json_schema, canvas_list, canvas_list_open_result, canvas_open_request, canvas_provider_close_request, canvas_provider_invoke_action_request, canvas_provider_open_request, canvas_provider_open_result, canvas_session_context, capi_session_options, command_list, commands_handle_pending_command_request, commands_handle_pending_command_result, commands_invoke_request, commands_list_request, commands_respond_to_queued_command_request, commands_respond_to_queued_command_result, completions_get_trigger_characters_result, completions_request_request, completions_request_result, configure_session_extensions_params, connected_remote_session_metadata, connected_remote_session_metadata_kind, connected_remote_session_metadata_repository, connect_remote_session_params, connect_request, connect_result, content_filter_mode, context_heaviest_message, copilot_api_token_auth_info, copilot_user_response, copilot_user_response_endpoints, copilot_user_response_quota_snapshots, copilot_user_response_quota_snapshots_chat, copilot_user_response_quota_snapshots_completions, copilot_user_response_quota_snapshots_premium_interactions, current_model, current_tool_metadata, debug_collect_logs_collected_entry, debug_collect_logs_destination, debug_collect_logs_entry, debug_collect_logs_entry_kind, debug_collect_logs_include, debug_collect_logs_redaction, debug_collect_logs_request, debug_collect_logs_result, debug_collect_logs_result_kind, debug_collect_logs_skipped_entry, debug_collect_logs_source, discovered_canvas, discovered_mcp_server, discovered_mcp_server_type, enqueue_command_params, enqueue_command_result, env_auth_info, event_log_read_request, event_log_release_interest_result, event_log_tail_result, event_log_types, events_agent_scope, events_cursor_status, events_read_result, execute_command_params, execute_command_result, extension, extension_context_push_input, extension_list, extensions_disable_request, extensions_enable_request, extension_source, extension_status, external_tool_result, external_tool_text_result_for_llm, external_tool_text_result_for_llm_binary_results_for_llm, external_tool_text_result_for_llm_binary_results_for_llm_type, external_tool_text_result_for_llm_content, external_tool_text_result_for_llm_content_audio, external_tool_text_result_for_llm_content_image, external_tool_text_result_for_llm_content_resource, external_tool_text_result_for_llm_content_resource_details, external_tool_text_result_for_llm_content_resource_link, external_tool_text_result_for_llm_content_resource_link_icon, external_tool_text_result_for_llm_content_resource_link_icon_theme, external_tool_text_result_for_llm_content_shell_exit, external_tool_text_result_for_llm_content_terminal, external_tool_text_result_for_llm_content_text, filter_mapping, fleet_start_request, fleet_start_result, folder_trust_add_params, folder_trust_check_params, folder_trust_check_result, gh_cli_auth_info, git_hub_telemetry_client_info, git_hub_telemetry_event, git_hub_telemetry_notification, handle_pending_tool_call_request, handle_pending_tool_call_result, history_abort_manual_compaction_result, history_cancel_background_compaction_result, history_compact_context_window, history_compact_request, history_compact_result, history_summarize_for_handoff_result, history_truncate_request, history_truncate_result, hmac_auth_info, installed_plugin, installed_plugin_info, installed_plugin_source, installed_plugin_source_git_hub, installed_plugin_source_local, installed_plugin_source_url, instruction_discovery_path, instruction_discovery_path_kind, instruction_discovery_path_list, instruction_discovery_path_location, instructions_discover_request, instructions_get_discovery_paths_request, instructions_get_sources_result, instruction_source, instruction_source_location, instruction_source_type, llm_inference_headers, llm_inference_http_request_chunk_request, llm_inference_http_request_chunk_result, llm_inference_http_request_start_request, llm_inference_http_request_start_result, llm_inference_http_request_start_transport, llm_inference_http_response_chunk_error, llm_inference_http_response_chunk_request, llm_inference_http_response_chunk_result, llm_inference_http_response_start_request, llm_inference_http_response_start_result, llm_inference_set_provider_result, local_session_metadata_value, log_request, log_result, lsp_initialize_request, marketplace_add_result, marketplace_browse_result, marketplace_info, marketplace_list_result, marketplace_plugin_info, marketplace_refresh_entry, marketplace_refresh_result, marketplace_remove_result, mcp_allowed_server, mcp_apps_call_tool_request, mcp_apps_diagnose_capability, mcp_apps_diagnose_request, mcp_apps_diagnose_result, mcp_apps_diagnose_server, mcp_apps_host_context, mcp_apps_host_context_details, mcp_apps_host_context_details_available_display_mode, mcp_apps_host_context_details_display_mode, mcp_apps_host_context_details_platform, mcp_apps_host_context_details_theme, mcp_apps_list_tools_request, mcp_apps_list_tools_result, mcp_apps_read_resource_request, mcp_apps_read_resource_result, mcp_apps_resource_content, mcp_apps_set_host_context_details, mcp_apps_set_host_context_details_available_display_mode, mcp_apps_set_host_context_details_display_mode, mcp_apps_set_host_context_details_platform, mcp_apps_set_host_context_details_theme, mcp_apps_set_host_context_request, mcp_cancel_sampling_execution_params, mcp_cancel_sampling_execution_result, mcp_config_add_request, mcp_config_disable_request, mcp_config_enable_request, mcp_config_list, mcp_config_remove_request, mcp_config_update_request, mcp_configure_git_hub_request, mcp_configure_git_hub_result, mcp_disable_request, mcp_discover_request, mcp_discover_result, mcp_enable_request, mcp_execute_sampling_params, mcp_execute_sampling_request, mcp_execute_sampling_result, mcp_filtered_server, mcp_headers_handle_pending_headers_refresh_request, mcp_headers_handle_pending_headers_refresh_request_request, mcp_headers_handle_pending_headers_refresh_request_result, mcp_host_state, mcp_is_server_running_request, mcp_is_server_running_result, mcp_list_tools_request, mcp_list_tools_result, mcp_oauth_handle_pending_request, mcp_oauth_handle_pending_result, mcp_oauth_login_grant_type, mcp_oauth_login_request, mcp_oauth_login_result, mcp_oauth_pending_request_response, mcp_register_external_client_request, mcp_reload_with_config_request, mcp_remove_git_hub_result, mcp_resource, mcp_resource_annotations, mcp_resource_content, mcp_resource_icon, mcp_resources_list_request, mcp_resources_list_result, mcp_resources_list_templates_request, mcp_resources_list_templates_result, mcp_resources_read_request, mcp_resources_read_result, mcp_resource_template, mcp_restart_server_request, mcp_sampling_execution_action, mcp_sampling_execution_result, mcp_server, mcp_server_auth_config, mcp_server_auth_config_redirect_port, mcp_server_config, mcp_server_config_defer_tools, mcp_server_config_http, mcp_server_config_http_oauth_grant_type, mcp_server_config_http_type, mcp_server_config_stdio, mcp_server_failure_info, mcp_server_list, mcp_server_needs_auth_info, mcp_set_env_value_mode_details, mcp_set_env_value_mode_params, mcp_set_env_value_mode_result, mcp_start_server_request, mcp_start_servers_result, mcp_stop_server_request, mcp_tools, mcp_unregister_external_client_request, memory_configuration, metadata_context_attribution_result, metadata_context_heaviest_messages_request, metadata_context_heaviest_messages_result, metadata_context_info_request, metadata_context_info_result, metadata_is_processing_result, metadata_recompute_context_tokens_request, metadata_recompute_context_tokens_result, metadata_record_context_change_request, metadata_record_context_change_result, metadata_set_working_directory_request, metadata_set_working_directory_result, metadata_snapshot_current_mode, metadata_snapshot_remote_metadata, metadata_snapshot_remote_metadata_repository, metadata_snapshot_remote_metadata_task_type, model, model_billing, model_billing_promo, model_billing_token_prices, model_billing_token_prices_long_context, model_capabilities, model_capabilities_limits, model_capabilities_limits_vision, model_capabilities_override, model_capabilities_override_limits, model_capabilities_override_limits_vision, model_capabilities_override_supports, model_capabilities_supports, model_list, model_list_request, model_picker_category, model_picker_price_category, model_policy, model_policy_state, model_set_reasoning_effort_request, model_set_reasoning_effort_result, models_list_request, model_switch_to_request, model_switch_to_result, mode_set_request, named_provider_config, name_get_result, name_set_auto_request, name_set_auto_result, name_set_request, open_canvas_instance, options_update_additional_content_exclusion_policy, options_update_additional_content_exclusion_policy_rule, options_update_additional_content_exclusion_policy_rule_source, options_update_additional_content_exclusion_policy_scope, options_update_context_tier, options_update_env_value_mode, options_update_reasoning_summary, options_update_tool_filter_precedence, pending_permission_request, pending_permission_request_list, permission_decision, permission_decision_approved, permission_decision_approved_for_location, permission_decision_approved_for_session, permission_decision_approve_for_location, permission_decision_approve_for_location_approval, permission_decision_approve_for_location_approval_commands, permission_decision_approve_for_location_approval_custom_tool, permission_decision_approve_for_location_approval_extension_management, permission_decision_approve_for_location_approval_extension_permission_access, permission_decision_approve_for_location_approval_mcp, permission_decision_approve_for_location_approval_mcp_sampling, permission_decision_approve_for_location_approval_memory, permission_decision_approve_for_location_approval_read, permission_decision_approve_for_location_approval_write, permission_decision_approve_for_session, permission_decision_approve_for_session_approval, permission_decision_approve_for_session_approval_commands, permission_decision_approve_for_session_approval_custom_tool, permission_decision_approve_for_session_approval_extension_management, permission_decision_approve_for_session_approval_extension_permission_access, permission_decision_approve_for_session_approval_mcp, permission_decision_approve_for_session_approval_mcp_sampling, permission_decision_approve_for_session_approval_memory, permission_decision_approve_for_session_approval_read, permission_decision_approve_for_session_approval_write, permission_decision_approve_once, permission_decision_approve_permanently, permission_decision_cancelled, permission_decision_denied_by_content_exclusion_policy, permission_decision_denied_by_permission_request_hook, permission_decision_denied_by_rules, permission_decision_denied_interactively_by_user, permission_decision_denied_no_approval_rule_and_could_not_request_from_user, permission_decision_reject, permission_decision_request, permission_decision_user_not_available, permission_location_add_tool_approval_params, permission_location_apply_params, permission_location_apply_result, permission_location_resolve_params, permission_location_resolve_result, permission_location_type, permission_paths_add_params, permission_paths_allowed_check_params, permission_paths_allowed_check_result, permission_paths_config, permission_paths_list, permission_paths_update_primary_params, permission_paths_workspace_check_params, permission_paths_workspace_check_result, permission_prompt_shown_notification, permission_request_result, permission_rules_set, permissions_allow_all_mode, permissions_configure_additional_content_exclusion_policy, permissions_configure_additional_content_exclusion_policy_rule, permissions_configure_additional_content_exclusion_policy_rule_source, permissions_configure_additional_content_exclusion_policy_scope, permissions_configure_params, permissions_configure_result, permissions_folder_trust_add_trusted_result, permissions_get_allow_all_request, permissions_locations_add_tool_approval_details, permissions_locations_add_tool_approval_details_commands, permissions_locations_add_tool_approval_details_custom_tool, permissions_locations_add_tool_approval_details_extension_management, permissions_locations_add_tool_approval_details_extension_permission_access, permissions_locations_add_tool_approval_details_mcp, permissions_locations_add_tool_approval_details_mcp_sampling, permissions_locations_add_tool_approval_details_memory, permissions_locations_add_tool_approval_details_read, permissions_locations_add_tool_approval_details_write, permissions_locations_add_tool_approval_result, permissions_modify_rules_params, permissions_modify_rules_result, permissions_modify_rules_scope, permissions_notify_prompt_shown_result, permissions_paths_add_result, permissions_paths_list_request, permissions_paths_update_primary_result, permissions_pending_requests_request, permissions_reset_session_approvals_request, permissions_reset_session_approvals_result, permissions_set_allow_all_request, permissions_set_allow_all_source, permissions_set_approve_all_request, permissions_set_approve_all_result, permissions_set_approve_all_source, permissions_set_required_request, permissions_set_required_result, permissions_urls_set_unrestricted_mode_result, permission_urls_config, permission_urls_set_unrestricted_mode_params, ping_request, ping_result, plan_read_result, plan_read_sql_todos_result, plan_read_sql_todos_with_dependencies_result, plan_sql_todo_dependency, plan_sql_todos_row, plan_update_request, plugin, plugin_install_result, plugin_list, plugin_list_result, plugins_disable_request, plugins_enable_request, plugins_install_request, plugins_marketplaces_add_request, plugins_marketplaces_browse_request, plugins_marketplaces_refresh_request, plugins_marketplaces_remove_request, plugins_reload_request, plugins_uninstall_request, plugins_update_request, plugin_update_all_entry, plugin_update_all_result, plugin_update_result, provider_add_request, provider_add_result, provider_config, provider_config_azure, provider_config_transport, provider_config_type, provider_config_wire_api, provider_endpoint, provider_endpoint_transport, provider_endpoint_type, provider_endpoint_wire_api, provider_get_endpoint_request, provider_model_config, provider_session_token, provider_token_acquire_request, provider_token_acquire_result, push_attachment, push_attachment_blob, push_attachment_directory, push_attachment_file, push_attachment_file_line_range, push_attachment_git_hub_actions_job, push_attachment_git_hub_commit, push_attachment_git_hub_file, push_attachment_git_hub_file_diff, push_attachment_git_hub_file_diff_side, push_attachment_git_hub_reference, push_attachment_git_hub_reference_type, push_attachment_git_hub_release, push_attachment_git_hub_repository, push_attachment_git_hub_snippet, push_attachment_git_hub_tree_comparison, push_attachment_git_hub_tree_comparison_side, push_attachment_git_hub_url, push_attachment_selection, push_attachment_selection_details, push_attachment_selection_details_end, push_attachment_selection_details_start, push_git_hub_repo_ref, queued_command_handled, queued_command_not_handled, queued_command_result, queue_pending_items, queue_pending_items_kind, queue_pending_items_result, queue_remove_most_recent_result, register_event_interest_params, register_event_interest_result, register_extension_tools_params, register_extension_tools_result, release_event_interest_params, remote_control_config, remote_control_config_existing_mc_session, remote_control_status, remote_control_status_active, remote_control_status_connecting, remote_control_status_error, remote_control_status_off, remote_control_status_result, remote_control_stop_result, remote_control_transfer_result, remote_enable_request, remote_enable_result, remote_notify_steerable_changed_request, remote_notify_steerable_changed_result, remote_session_connection_result, remote_session_metadata_repository, remote_session_metadata_task_type, remote_session_metadata_value, remote_session_mode, remote_session_repository, sandbox_config, sandbox_config_user_policy, sandbox_config_user_policy_experimental, sandbox_config_user_policy_experimental_seatbelt, sandbox_config_user_policy_filesystem, sandbox_config_user_policy_network, sandbox_config_user_policy_seatbelt, schedule_entry, schedule_list, schedule_stop_request, schedule_stop_result, secrets_add_filter_values_request, secrets_add_filter_values_result, send_agent_mode, send_attachments_to_message_params, send_message_item, send_messages_request, send_messages_result, send_mode, send_request, send_result, server_agent_list, server_instruction_source_list, server_skill, server_skill_list, session_activity, session_auth_status, session_bulk_delete_result, session_capability, session_completion_item, session_context, session_context_host_type, session_enrich_metadata_result, session_fs_append_file_request, session_fs_error, session_fs_error_code, session_fs_exists_request, session_fs_exists_result, session_fs_mkdir_request, session_fs_readdir_request, session_fs_readdir_result, session_fs_readdir_with_types_entry, session_fs_readdir_with_types_entry_type, session_fs_readdir_with_types_request, session_fs_readdir_with_types_result, session_fs_read_file_request, session_fs_read_file_result, session_fs_rename_request, session_fs_rm_request, session_fs_set_provider_capabilities, session_fs_set_provider_conventions, session_fs_set_provider_request, session_fs_set_provider_result, session_fs_sqlite_exists_request, session_fs_sqlite_exists_result, session_fs_sqlite_query_request, session_fs_sqlite_query_result, session_fs_sqlite_query_type, session_fs_stat_request, session_fs_stat_result, session_fs_write_file_request, session_installed_plugin, session_installed_plugin_source, session_installed_plugin_source_git_hub, session_installed_plugin_source_local, session_installed_plugin_source_url, session_list, session_list_entry, session_list_filter, session_load_deferred_repo_hooks_result, session_log_level, session_mcp_apps_call_tool_result, session_metadata_snapshot, session_mode, session_model_list, session_open_options, session_open_options_additional_content_exclusion_policy, session_open_options_additional_content_exclusion_policy_rule, session_open_options_additional_content_exclusion_policy_rule_source, session_open_options_additional_content_exclusion_policy_scope, session_open_options_env_value_mode, session_open_options_reasoning_summary, session_open_params, session_open_result, session_prune_result, sessions_bulk_delete_request, sessions_check_in_use_request, sessions_check_in_use_result, sessions_close_request, sessions_close_result, sessions_enrich_metadata_request, session_set_credentials_params, session_set_credentials_result, session_settings_built_in_tool_availability_snapshot, session_settings_evaluate_predicate_request, session_settings_evaluate_predicate_result, session_settings_job_snapshot, session_settings_model_snapshot, session_settings_online_evaluation_snapshot, session_settings_predicate_name, session_settings_repo_snapshot, session_settings_snapshot, session_settings_validation_snapshot, sessions_find_by_prefix_request, sessions_find_by_prefix_result, sessions_find_by_task_id_request, sessions_find_by_task_id_result, sessions_fork_request, sessions_fork_result, sessions_get_board_entry_count_request, sessions_get_board_entry_count_result, sessions_get_event_file_path_request, sessions_get_event_file_path_result, sessions_get_last_for_context_request, sessions_get_last_for_context_result, sessions_get_persisted_remote_steerable_request, sessions_get_persisted_remote_steerable_result, session_sizes, sessions_list_request, sessions_load_deferred_repo_hooks_request, sessions_open_attach, sessions_open_cloud, sessions_open_create, sessions_open_handoff, sessions_open_handoff_task_type, sessions_open_progress, sessions_open_progress_status, sessions_open_progress_step, sessions_open_remote, sessions_open_resume, sessions_open_resume_last, sessions_open_status, session_source, sessions_prune_old_request, sessions_register_extension_tools_on_session_options, sessions_release_lock_request, sessions_release_lock_result, sessions_reload_plugin_hooks_request, sessions_reload_plugin_hooks_result, sessions_save_request, sessions_save_result, sessions_set_additional_plugins_request, sessions_set_additional_plugins_result, sessions_set_remote_control_steering_request, sessions_start_remote_control_request, sessions_stop_remote_control_request, sessions_transfer_remote_control_request, session_telemetry_engagement, session_update_options_params, session_update_options_result, session_visibility_status, session_working_directory_context, session_working_directory_context_host_type, shell_cancel_user_requested_request, shell_exec_request, shell_exec_result, shell_execute_user_requested_request, shell_kill_request, shell_kill_result, shell_kill_signal, shutdown_request, skill, skill_discovery_path, skill_discovery_path_list, skill_discovery_scope, skill_list, skills_config_set_disabled_skills_request, skills_disable_request, skills_discover_request, skills_enable_request, skills_get_discovery_paths_request, skills_get_invoked_result, skills_invoked_skill, skills_load_diagnostics, slash_command_agent_prompt_result, slash_command_completed_result, slash_command_info, slash_command_input, slash_command_input_choice, slash_command_input_completion, slash_command_invocation_result, slash_command_kind, slash_command_select_subcommand_option, slash_command_select_subcommand_result, slash_command_text_result, subagent_settings_entry, subagent_settings_entry_context_tier, task_agent_info, task_agent_progress, task_execution_mode, task_info, task_list, task_progress_line, tasks_cancel_request, tasks_cancel_result, tasks_get_current_promotable_result, tasks_get_progress_request, tasks_get_progress_result, task_shell_info, task_shell_info_attachment_mode, task_shell_progress, tasks_promote_current_to_background_result, tasks_promote_to_background_request, tasks_promote_to_background_result, tasks_refresh_result, tasks_remove_request, tasks_remove_result, tasks_send_message_request, tasks_send_message_result, tasks_start_agent_request, tasks_start_agent_result, task_status, tasks_wait_for_pending_result, telemetry_set_feature_overrides_request, token_auth_info, tool, tool_list, tools_get_current_metadata_result, tools_initialize_and_validate_result, tools_list_request, tools_update_subagent_settings_result, ui_auto_mode_switch_response, ui_elicitation_array_any_of_field, ui_elicitation_array_any_of_field_items, ui_elicitation_array_any_of_field_items_any_of, ui_elicitation_array_enum_field, ui_elicitation_array_enum_field_items, ui_elicitation_field_value, ui_elicitation_request, ui_elicitation_response, ui_elicitation_response_action, ui_elicitation_response_content, ui_elicitation_result, ui_elicitation_schema, ui_elicitation_schema_property, ui_elicitation_schema_property_boolean, ui_elicitation_schema_property_number, ui_elicitation_schema_property_number_type, ui_elicitation_schema_property_string, ui_elicitation_schema_property_string_format, ui_elicitation_string_enum_field, ui_elicitation_string_one_of_field, ui_elicitation_string_one_of_field_one_of, ui_ephemeral_query_request, ui_ephemeral_query_result, ui_exit_plan_mode_action, ui_exit_plan_mode_response, ui_handle_pending_auto_mode_switch_request, ui_handle_pending_elicitation_request, ui_handle_pending_exit_plan_mode_request, ui_handle_pending_result, ui_handle_pending_sampling_request, ui_handle_pending_sampling_response, ui_handle_pending_session_limits_exhausted_request, ui_handle_pending_user_input_request, ui_register_direct_auto_mode_switch_handler_result, ui_session_limits_exhausted_response, ui_session_limits_exhausted_response_action, ui_unregister_direct_auto_mode_switch_handler_request, ui_unregister_direct_auto_mode_switch_handler_result, ui_user_input_response, update_subagent_settings_request, usage_get_metrics_result, usage_metrics_code_changes, usage_metrics_model_metric, usage_metrics_model_metric_requests, usage_metrics_model_metric_token_detail, usage_metrics_model_metric_usage, usage_metrics_token_detail, user_auth_info, user_requested_shell_command_result, user_setting_metadata, user_settings_get_result, user_settings_set_request, user_settings_set_result, visibility_get_result, visibility_set_request, visibility_set_result, workspace_diff_file_change, workspace_diff_file_change_type, workspace_diff_mode, workspace_diff_result, workspaces_checkpoints, workspaces_create_file_request, workspaces_diff_request, workspaces_get_workspace_result, workspaces_list_checkpoints_result, workspaces_list_files_result, workspaces_read_checkpoint_request, workspaces_read_checkpoint_result, workspaces_read_file_request, workspaces_read_file_result, workspaces_save_large_paste_request, workspaces_save_large_paste_result, workspace_summary_host_type, workspaces_workspace_details_host_type, session_context_attribution, session_context_info, subagent_settings, task_progress, workspace_summary) + return RPC(abort_request, abort_result, account_all_users, account_get_all_users_result, account_get_current_auth_result, account_get_quota_request, account_get_quota_result, account_login_request, account_login_result, account_logout_request, account_logout_result, account_quota_snapshot, adaptive_thinking_support, agent_discovery_path, agent_discovery_path_list, agent_discovery_path_scope, agent_get_current_result, agent_info, agent_info_source, agent_list, agent_registry_live_target_entry, agent_registry_live_target_entry_attention_kind, agent_registry_live_target_entry_kind, agent_registry_live_target_entry_last_terminal_event, agent_registry_live_target_entry_status, agent_registry_log_capture, agent_registry_log_capture_open_error_reason, agent_registry_spawn_error, agent_registry_spawn_permission_mode, agent_registry_spawn_registry_timeout, agent_registry_spawn_request, agent_registry_spawn_result, agent_registry_spawn_spawned, agent_registry_spawn_validation_error, agent_registry_spawn_validation_error_field, agent_registry_spawn_validation_error_reason, agent_reload_result, agents_discover_request, agent_select_request, agent_select_result, agents_get_discovery_paths_request, allow_all_permission_set_result, allow_all_permission_state, api_key_auth_info, auth_info, auth_info_type, cancel_user_requested_shell_command_result, canvas_action, canvas_action_invoke_request, canvas_action_invoke_result, canvas_close_request, canvas_host_context, canvas_host_context_capabilities, canvas_json_schema, canvas_list, canvas_list_open_result, canvas_open_request, canvas_provider_close_request, canvas_provider_invoke_action_request, canvas_provider_open_request, canvas_provider_open_result, canvas_session_context, capi_session_options, command_list, commands_handle_pending_command_request, commands_handle_pending_command_result, commands_invoke_request, commands_list_request, commands_respond_to_queued_command_request, commands_respond_to_queued_command_result, completions_get_trigger_characters_result, completions_request_request, completions_request_result, configure_session_extensions_params, connected_remote_session_metadata, connected_remote_session_metadata_kind, connected_remote_session_metadata_repository, connect_remote_session_params, connect_request, connect_result, content_filter_mode, context_heaviest_message, copilot_api_token_auth_info, copilot_user_response, copilot_user_response_endpoints, copilot_user_response_quota_snapshots, copilot_user_response_quota_snapshots_chat, copilot_user_response_quota_snapshots_completions, copilot_user_response_quota_snapshots_premium_interactions, current_model, current_tool_metadata, debug_collect_logs_collected_entry, debug_collect_logs_destination, debug_collect_logs_entry, debug_collect_logs_entry_kind, debug_collect_logs_include, debug_collect_logs_redaction, debug_collect_logs_request, debug_collect_logs_result, debug_collect_logs_result_kind, debug_collect_logs_skipped_entry, debug_collect_logs_source, discovered_canvas, discovered_mcp_server, discovered_mcp_server_type, enqueue_command_params, enqueue_command_result, env_auth_info, event_log_read_request, event_log_release_interest_result, event_log_tail_result, event_log_types, events_agent_scope, events_cursor_status, events_read_result, execute_command_params, execute_command_result, extension, extension_context_push_input, extension_list, extensions_disable_request, extensions_enable_request, extension_source, extension_status, external_tool_result, external_tool_text_result_for_llm, external_tool_text_result_for_llm_binary_results_for_llm, external_tool_text_result_for_llm_binary_results_for_llm_type, external_tool_text_result_for_llm_content, external_tool_text_result_for_llm_content_audio, external_tool_text_result_for_llm_content_image, external_tool_text_result_for_llm_content_resource, external_tool_text_result_for_llm_content_resource_details, external_tool_text_result_for_llm_content_resource_link, external_tool_text_result_for_llm_content_resource_link_icon, external_tool_text_result_for_llm_content_resource_link_icon_theme, external_tool_text_result_for_llm_content_shell_exit, external_tool_text_result_for_llm_content_terminal, external_tool_text_result_for_llm_content_text, filter_mapping, fleet_start_request, fleet_start_result, folder_trust_add_params, folder_trust_check_params, folder_trust_check_result, gh_cli_auth_info, git_hub_telemetry_client_info, git_hub_telemetry_event, git_hub_telemetry_notification, handle_pending_tool_call_request, handle_pending_tool_call_result, history_abort_manual_compaction_result, history_cancel_background_compaction_result, history_compact_context_window, history_compact_request, history_compact_result, history_summarize_for_handoff_result, history_truncate_request, history_truncate_result, hmac_auth_info, hook_invoke_request, hook_invoke_response, hook_type, installed_plugin, installed_plugin_info, installed_plugin_source, installed_plugin_source_git_hub, installed_plugin_source_local, installed_plugin_source_url, instruction_discovery_path, instruction_discovery_path_kind, instruction_discovery_path_list, instruction_discovery_path_location, instructions_discover_request, instructions_get_discovery_paths_request, instructions_get_sources_result, instruction_source, instruction_source_location, instruction_source_type, llm_inference_headers, llm_inference_http_request_chunk_request, llm_inference_http_request_chunk_result, llm_inference_http_request_start_request, llm_inference_http_request_start_result, llm_inference_http_request_start_transport, llm_inference_http_response_chunk_error, llm_inference_http_response_chunk_request, llm_inference_http_response_chunk_result, llm_inference_http_response_start_request, llm_inference_http_response_start_result, llm_inference_set_provider_result, local_session_metadata_value, log_request, log_result, lsp_initialize_request, marketplace_add_result, marketplace_browse_result, marketplace_info, marketplace_list_result, marketplace_plugin_info, marketplace_refresh_entry, marketplace_refresh_result, marketplace_remove_result, mcp_allowed_server, mcp_apps_call_tool_request, mcp_apps_diagnose_capability, mcp_apps_diagnose_request, mcp_apps_diagnose_result, mcp_apps_diagnose_server, mcp_apps_host_context, mcp_apps_host_context_details, mcp_apps_host_context_details_available_display_mode, mcp_apps_host_context_details_display_mode, mcp_apps_host_context_details_platform, mcp_apps_host_context_details_theme, mcp_apps_list_tools_request, mcp_apps_list_tools_result, mcp_apps_read_resource_request, mcp_apps_read_resource_result, mcp_apps_resource_content, mcp_apps_set_host_context_details, mcp_apps_set_host_context_details_available_display_mode, mcp_apps_set_host_context_details_display_mode, mcp_apps_set_host_context_details_platform, mcp_apps_set_host_context_details_theme, mcp_apps_set_host_context_request, mcp_cancel_sampling_execution_params, mcp_cancel_sampling_execution_result, mcp_config_add_request, mcp_config_disable_request, mcp_config_enable_request, mcp_config_list, mcp_config_remove_request, mcp_config_update_request, mcp_configure_git_hub_request, mcp_configure_git_hub_result, mcp_disable_request, mcp_discover_request, mcp_discover_result, mcp_enable_request, mcp_execute_sampling_params, mcp_execute_sampling_request, mcp_execute_sampling_result, mcp_filtered_server, mcp_headers_handle_pending_headers_refresh_request, mcp_headers_handle_pending_headers_refresh_request_request, mcp_headers_handle_pending_headers_refresh_request_result, mcp_host_state, mcp_is_server_running_request, mcp_is_server_running_result, mcp_list_tools_request, mcp_list_tools_result, mcp_oauth_handle_pending_request, mcp_oauth_handle_pending_result, mcp_oauth_login_grant_type, mcp_oauth_login_request, mcp_oauth_login_result, mcp_oauth_pending_request_response, mcp_register_external_client_request, mcp_reload_with_config_request, mcp_remove_git_hub_result, mcp_resource, mcp_resource_annotations, mcp_resource_content, mcp_resource_icon, mcp_resources_list_request, mcp_resources_list_result, mcp_resources_list_templates_request, mcp_resources_list_templates_result, mcp_resources_read_request, mcp_resources_read_result, mcp_resource_template, mcp_restart_server_request, mcp_sampling_execution_action, mcp_sampling_execution_result, mcp_server, mcp_server_auth_config, mcp_server_auth_config_redirect_port, mcp_server_config, mcp_server_config_defer_tools, mcp_server_config_http, mcp_server_config_http_oauth_grant_type, mcp_server_config_http_type, mcp_server_config_stdio, mcp_server_failure_info, mcp_server_list, mcp_server_needs_auth_info, mcp_set_env_value_mode_details, mcp_set_env_value_mode_params, mcp_set_env_value_mode_result, mcp_start_server_request, mcp_start_servers_result, mcp_stop_server_request, mcp_tools, mcp_tool_ui, mcp_tool_ui_visibility, mcp_unregister_external_client_request, memory_configuration, metadata_context_attribution_result, metadata_context_heaviest_messages_request, metadata_context_heaviest_messages_result, metadata_context_info_request, metadata_context_info_result, metadata_is_processing_result, metadata_recompute_context_tokens_request, metadata_recompute_context_tokens_result, metadata_record_context_change_request, metadata_record_context_change_result, metadata_set_working_directory_request, metadata_set_working_directory_result, metadata_snapshot_current_mode, metadata_snapshot_remote_metadata, metadata_snapshot_remote_metadata_repository, metadata_snapshot_remote_metadata_task_type, model, model_billing, model_billing_promo, model_billing_token_prices, model_billing_token_prices_long_context, model_capabilities, model_capabilities_limits, model_capabilities_limits_vision, model_capabilities_override, model_capabilities_override_limits, model_capabilities_override_limits_vision, model_capabilities_override_supports, model_capabilities_supports, model_list, model_list_request, model_picker_category, model_picker_price_category, model_policy, model_policy_state, model_set_reasoning_effort_request, model_set_reasoning_effort_result, models_list_request, model_switch_to_request, model_switch_to_result, mode_set_request, named_provider_config, name_get_result, name_set_auto_request, name_set_auto_result, name_set_request, open_canvas_instance, options_update_additional_content_exclusion_policy, options_update_additional_content_exclusion_policy_rule, options_update_additional_content_exclusion_policy_rule_source, options_update_additional_content_exclusion_policy_scope, options_update_context_tier, options_update_env_value_mode, options_update_reasoning_summary, options_update_tool_filter_precedence, pending_permission_request, pending_permission_request_list, permission_decision, permission_decision_approved, permission_decision_approved_for_location, permission_decision_approved_for_session, permission_decision_approve_for_location, permission_decision_approve_for_location_approval, permission_decision_approve_for_location_approval_commands, permission_decision_approve_for_location_approval_custom_tool, permission_decision_approve_for_location_approval_extension_management, permission_decision_approve_for_location_approval_extension_permission_access, permission_decision_approve_for_location_approval_mcp, permission_decision_approve_for_location_approval_mcp_sampling, permission_decision_approve_for_location_approval_memory, permission_decision_approve_for_location_approval_read, permission_decision_approve_for_location_approval_write, permission_decision_approve_for_session, permission_decision_approve_for_session_approval, permission_decision_approve_for_session_approval_commands, permission_decision_approve_for_session_approval_custom_tool, permission_decision_approve_for_session_approval_extension_management, permission_decision_approve_for_session_approval_extension_permission_access, permission_decision_approve_for_session_approval_mcp, permission_decision_approve_for_session_approval_mcp_sampling, permission_decision_approve_for_session_approval_memory, permission_decision_approve_for_session_approval_read, permission_decision_approve_for_session_approval_write, permission_decision_approve_once, permission_decision_approve_permanently, permission_decision_cancelled, permission_decision_denied_by_content_exclusion_policy, permission_decision_denied_by_permission_request_hook, permission_decision_denied_by_rules, permission_decision_denied_interactively_by_user, permission_decision_denied_no_approval_rule_and_could_not_request_from_user, permission_decision_reject, permission_decision_request, permission_decision_user_not_available, permission_location_add_tool_approval_params, permission_location_apply_params, permission_location_apply_result, permission_location_resolve_params, permission_location_resolve_result, permission_location_type, permission_paths_add_params, permission_paths_allowed_check_params, permission_paths_allowed_check_result, permission_paths_config, permission_paths_list, permission_paths_update_primary_params, permission_paths_workspace_check_params, permission_paths_workspace_check_result, permission_prompt_shown_notification, permission_request_result, permission_rules_set, permissions_allow_all_mode, permissions_configure_additional_content_exclusion_policy, permissions_configure_additional_content_exclusion_policy_rule, permissions_configure_additional_content_exclusion_policy_rule_source, permissions_configure_additional_content_exclusion_policy_scope, permissions_configure_params, permissions_configure_result, permissions_folder_trust_add_trusted_result, permissions_get_allow_all_request, permissions_locations_add_tool_approval_details, permissions_locations_add_tool_approval_details_commands, permissions_locations_add_tool_approval_details_custom_tool, permissions_locations_add_tool_approval_details_extension_management, permissions_locations_add_tool_approval_details_extension_permission_access, permissions_locations_add_tool_approval_details_mcp, permissions_locations_add_tool_approval_details_mcp_sampling, permissions_locations_add_tool_approval_details_memory, permissions_locations_add_tool_approval_details_read, permissions_locations_add_tool_approval_details_write, permissions_locations_add_tool_approval_result, permissions_modify_rules_params, permissions_modify_rules_result, permissions_modify_rules_scope, permissions_notify_prompt_shown_result, permissions_paths_add_result, permissions_paths_list_request, permissions_paths_update_primary_result, permissions_pending_requests_request, permissions_reset_session_approvals_request, permissions_reset_session_approvals_result, permissions_set_allow_all_request, permissions_set_allow_all_source, permissions_set_approve_all_request, permissions_set_approve_all_result, permissions_set_approve_all_source, permissions_set_required_request, permissions_set_required_result, permissions_urls_set_unrestricted_mode_result, permission_urls_config, permission_urls_set_unrestricted_mode_params, ping_request, ping_result, plan_read_result, plan_read_sql_todos_result, plan_read_sql_todos_with_dependencies_result, plan_sql_todo_dependency, plan_sql_todos_row, plan_update_request, plugin, plugin_install_result, plugin_list, plugin_list_result, plugins_disable_request, plugins_enable_request, plugins_install_request, plugins_marketplaces_add_request, plugins_marketplaces_browse_request, plugins_marketplaces_refresh_request, plugins_marketplaces_remove_request, plugins_reload_request, plugins_uninstall_request, plugins_update_request, plugin_update_all_entry, plugin_update_all_result, plugin_update_result, provider_add_request, provider_add_result, provider_config, provider_config_azure, provider_config_transport, provider_config_type, provider_config_wire_api, provider_endpoint, provider_endpoint_transport, provider_endpoint_type, provider_endpoint_wire_api, provider_get_endpoint_request, provider_model_config, provider_session_token, provider_token_acquire_request, provider_token_acquire_result, push_attachment, push_attachment_blob, push_attachment_directory, push_attachment_file, push_attachment_file_line_range, push_attachment_git_hub_actions_job, push_attachment_git_hub_commit, push_attachment_git_hub_file, push_attachment_git_hub_file_diff, push_attachment_git_hub_file_diff_side, push_attachment_git_hub_reference, push_attachment_git_hub_reference_type, push_attachment_git_hub_release, push_attachment_git_hub_repository, push_attachment_git_hub_snippet, push_attachment_git_hub_tree_comparison, push_attachment_git_hub_tree_comparison_side, push_attachment_git_hub_url, push_attachment_selection, push_attachment_selection_details, push_attachment_selection_details_end, push_attachment_selection_details_start, push_git_hub_repo_ref, queued_command_handled, queued_command_not_handled, queued_command_result, queue_pending_items, queue_pending_items_kind, queue_pending_items_result, queue_remove_most_recent_result, register_event_interest_params, register_event_interest_result, register_extension_tools_params, register_extension_tools_result, release_event_interest_params, remote_control_config, remote_control_config_existing_mc_session, remote_control_status, remote_control_status_active, remote_control_status_connecting, remote_control_status_error, remote_control_status_off, remote_control_status_result, remote_control_stop_result, remote_control_transfer_result, remote_enable_request, remote_enable_result, remote_notify_steerable_changed_request, remote_notify_steerable_changed_result, remote_session_connection_result, remote_session_metadata_repository, remote_session_metadata_task_type, remote_session_metadata_value, remote_session_mode, remote_session_repository, sandbox_config, sandbox_config_user_policy, sandbox_config_user_policy_experimental, sandbox_config_user_policy_experimental_seatbelt, sandbox_config_user_policy_filesystem, sandbox_config_user_policy_network, sandbox_config_user_policy_seatbelt, schedule_entry, schedule_list, schedule_stop_request, schedule_stop_result, secrets_add_filter_values_request, secrets_add_filter_values_result, send_agent_mode, send_attachments_to_message_params, send_message_item, send_messages_request, send_messages_result, send_mode, send_request, send_result, server_agent_list, server_instruction_source_list, server_skill, server_skill_list, session_activity, session_auth_status, session_bulk_delete_result, session_capability, session_completion_item, session_context, session_context_host_type, session_enrich_metadata_result, session_fs_append_file_request, session_fs_error, session_fs_error_code, session_fs_exists_request, session_fs_exists_result, session_fs_mkdir_request, session_fs_readdir_request, session_fs_readdir_result, session_fs_readdir_with_types_entry, session_fs_readdir_with_types_entry_type, session_fs_readdir_with_types_request, session_fs_readdir_with_types_result, session_fs_read_file_request, session_fs_read_file_result, session_fs_rename_request, session_fs_rm_request, session_fs_set_provider_capabilities, session_fs_set_provider_conventions, session_fs_set_provider_request, session_fs_set_provider_result, session_fs_sqlite_exists_request, session_fs_sqlite_exists_result, session_fs_sqlite_query_request, session_fs_sqlite_query_result, session_fs_sqlite_query_type, session_fs_stat_request, session_fs_stat_result, session_fs_write_file_request, session_installed_plugin, session_installed_plugin_source, session_installed_plugin_source_git_hub, session_installed_plugin_source_local, session_installed_plugin_source_url, session_list, session_list_entry, session_list_filter, session_load_deferred_repo_hooks_result, session_log_level, session_mcp_apps_call_tool_result, session_metadata_snapshot, session_mode, session_model_list, session_model_price_category, session_open_options, session_open_options_additional_content_exclusion_policy, session_open_options_additional_content_exclusion_policy_rule, session_open_options_additional_content_exclusion_policy_rule_source, session_open_options_additional_content_exclusion_policy_scope, session_open_options_env_value_mode, session_open_options_reasoning_summary, session_open_params, session_open_result, session_prune_result, sessions_bulk_delete_request, sessions_check_in_use_request, sessions_check_in_use_result, sessions_close_request, sessions_close_result, sessions_enrich_metadata_request, session_set_credentials_params, session_set_credentials_result, session_settings_built_in_tool_availability_snapshot, session_settings_evaluate_predicate_request, session_settings_evaluate_predicate_result, session_settings_job_snapshot, session_settings_model_snapshot, session_settings_online_evaluation_snapshot, session_settings_predicate_name, session_settings_repo_snapshot, session_settings_snapshot, session_settings_validation_snapshot, sessions_find_by_prefix_request, sessions_find_by_prefix_result, sessions_find_by_task_id_request, sessions_find_by_task_id_result, sessions_fork_request, sessions_fork_result, sessions_get_board_entry_count_request, sessions_get_board_entry_count_result, sessions_get_event_file_path_request, sessions_get_event_file_path_result, sessions_get_last_for_context_request, sessions_get_last_for_context_result, sessions_get_persisted_remote_steerable_request, sessions_get_persisted_remote_steerable_result, session_sizes, sessions_list_request, sessions_load_deferred_repo_hooks_request, sessions_open_attach, sessions_open_cloud, sessions_open_create, sessions_open_handoff, sessions_open_handoff_task_type, sessions_open_progress, sessions_open_progress_status, sessions_open_progress_step, sessions_open_remote, sessions_open_resume, sessions_open_resume_last, sessions_open_status, session_source, sessions_prune_old_request, sessions_register_extension_tools_on_session_options, sessions_release_lock_request, sessions_release_lock_result, sessions_reload_plugin_hooks_request, sessions_reload_plugin_hooks_result, sessions_save_request, sessions_save_result, sessions_set_additional_plugins_request, sessions_set_additional_plugins_result, sessions_set_remote_control_steering_request, sessions_start_remote_control_request, sessions_stop_remote_control_request, sessions_transfer_remote_control_request, session_telemetry_engagement, session_update_options_params, session_update_options_result, session_visibility_status, session_working_directory_context, session_working_directory_context_host_type, shell_cancel_user_requested_request, shell_exec_request, shell_exec_result, shell_execute_user_requested_request, shell_kill_request, shell_kill_result, shell_kill_signal, shutdown_request, skill, skill_discovery_path, skill_discovery_path_list, skill_discovery_scope, skill_list, skills_config_set_disabled_skills_request, skills_disable_request, skills_discover_request, skills_enable_request, skills_get_discovery_paths_request, skills_get_invoked_result, skills_invoked_skill, skills_load_diagnostics, slash_command_agent_prompt_result, slash_command_completed_result, slash_command_info, slash_command_input, slash_command_input_choice, slash_command_input_completion, slash_command_invocation_result, slash_command_kind, slash_command_select_subcommand_option, slash_command_select_subcommand_result, slash_command_text_result, subagent_settings_entry, subagent_settings_entry_context_tier, task_agent_info, task_agent_progress, task_execution_mode, task_info, task_list, task_progress_line, tasks_cancel_request, tasks_cancel_result, tasks_get_current_promotable_result, tasks_get_progress_request, tasks_get_progress_result, task_shell_info, task_shell_info_attachment_mode, task_shell_progress, tasks_promote_current_to_background_result, tasks_promote_to_background_request, tasks_promote_to_background_result, tasks_refresh_result, tasks_remove_request, tasks_remove_result, tasks_send_message_request, tasks_send_message_result, tasks_start_agent_request, tasks_start_agent_result, task_status, tasks_wait_for_pending_result, telemetry_set_feature_overrides_request, token_auth_info, tool, tool_list, tools_get_current_metadata_result, tools_initialize_and_validate_result, tools_list_request, tools_update_subagent_settings_result, ui_auto_mode_switch_response, ui_elicitation_array_any_of_field, ui_elicitation_array_any_of_field_items, ui_elicitation_array_any_of_field_items_any_of, ui_elicitation_array_enum_field, ui_elicitation_array_enum_field_items, ui_elicitation_field_value, ui_elicitation_request, ui_elicitation_response, ui_elicitation_response_action, ui_elicitation_response_content, ui_elicitation_result, ui_elicitation_schema, ui_elicitation_schema_property, ui_elicitation_schema_property_boolean, ui_elicitation_schema_property_number, ui_elicitation_schema_property_number_type, ui_elicitation_schema_property_string, ui_elicitation_schema_property_string_format, ui_elicitation_string_enum_field, ui_elicitation_string_one_of_field, ui_elicitation_string_one_of_field_one_of, ui_ephemeral_query_request, ui_ephemeral_query_result, ui_exit_plan_mode_action, ui_exit_plan_mode_response, ui_handle_pending_auto_mode_switch_request, ui_handle_pending_elicitation_request, ui_handle_pending_exit_plan_mode_request, ui_handle_pending_result, ui_handle_pending_sampling_request, ui_handle_pending_sampling_response, ui_handle_pending_session_limits_exhausted_request, ui_handle_pending_user_input_request, ui_register_direct_auto_mode_switch_handler_result, ui_session_limits_exhausted_response, ui_session_limits_exhausted_response_action, ui_unregister_direct_auto_mode_switch_handler_request, ui_unregister_direct_auto_mode_switch_handler_result, ui_user_input_response, update_subagent_settings_request, usage_get_metrics_result, usage_metrics_code_changes, usage_metrics_model_metric, usage_metrics_model_metric_requests, usage_metrics_model_metric_token_detail, usage_metrics_model_metric_usage, usage_metrics_token_detail, user_auth_info, user_requested_shell_command_result, user_setting_metadata, user_settings_get_result, user_settings_set_request, user_settings_set_result, visibility_get_result, visibility_set_request, visibility_set_result, workspace_diff_file_change, workspace_diff_file_change_type, workspace_diff_mode, workspace_diff_result, workspaces_checkpoints, workspaces_create_file_request, workspaces_diff_request, workspaces_get_workspace_result, workspaces_list_checkpoints_result, workspaces_list_files_result, workspaces_read_checkpoint_request, workspaces_read_checkpoint_result, workspaces_read_file_request, workspaces_read_file_result, workspaces_save_large_paste_request, workspaces_save_large_paste_result, workspace_summary_host_type, workspaces_workspace_details_host_type, session_context_attribution, session_context_info, subagent_settings, task_progress, workspace_summary) def to_dict(self) -> dict: result: dict = {} @@ -26056,6 +26212,9 @@ def to_dict(self) -> dict: result["HistoryTruncateRequest"] = to_class(HistoryTruncateRequest, self.history_truncate_request) result["HistoryTruncateResult"] = to_class(HistoryTruncateResult, self.history_truncate_result) result["HMACAuthInfo"] = to_class(HMACAuthInfo, self.hmac_auth_info) + result["HookInvokeRequest"] = to_class(_HookInvokeRequest, self.hook_invoke_request) + result["HookInvokeResponse"] = to_class(_HookInvokeResponse, self.hook_invoke_response) + result["HookType"] = to_enum(_HookType, self.hook_type) result["InstalledPlugin"] = to_class(InstalledPlugin, self.installed_plugin) result["InstalledPluginInfo"] = to_class(InstalledPluginInfo, self.installed_plugin_info) result["InstalledPluginSource"] = from_union([lambda x: to_class(InstalledPluginSource, x), from_str], self.installed_plugin_source) @@ -26187,6 +26346,8 @@ def to_dict(self) -> dict: result["McpStartServersResult"] = to_class(MCPStartServersResult, self.mcp_start_servers_result) result["McpStopServerRequest"] = to_class(MCPStopServerRequest, self.mcp_stop_server_request) result["McpTools"] = to_class(MCPTools, self.mcp_tools) + result["McpToolUi"] = to_class(MCPToolUI, self.mcp_tool_ui) + result["McpToolUiVisibility"] = to_enum(MCPToolUIVisibility, self.mcp_tool_ui_visibility) result["McpUnregisterExternalClientRequest"] = to_class(MCPUnregisterExternalClientRequest, self.mcp_unregister_external_client_request) result["MemoryConfiguration"] = to_class(MemoryConfiguration, self.memory_configuration) result["MetadataContextAttributionResult"] = to_class(MetadataContextAttributionResult, self.metadata_context_attribution_result) @@ -26511,6 +26672,7 @@ def to_dict(self) -> dict: result["SessionMetadataSnapshot"] = to_class(SessionMetadataSnapshot, self.session_metadata_snapshot) result["SessionMode"] = to_enum(SessionMode, self.session_mode) result["SessionModelList"] = to_class(SessionModelList, self.session_model_list) + result["SessionModelPriceCategory"] = to_class(SessionModelPriceCategory, self.session_model_price_category) result["SessionOpenOptions"] = to_class(SessionOpenOptions, self.session_open_options) result["SessionOpenOptionsAdditionalContentExclusionPolicy"] = to_class(SessionOpenOptionsAdditionalContentExclusionPolicy, self.session_open_options_additional_content_exclusion_policy) result["SessionOpenOptionsAdditionalContentExclusionPolicyRule"] = to_class(SessionOpenOptionsAdditionalContentExclusionPolicyRule, self.session_open_options_additional_content_exclusion_policy_rule) @@ -28064,7 +28226,7 @@ async def list(self, *, timeout: float | None = None) -> MCPServerList: return MCPServerList.from_dict(await self._client.request("session.mcp.list", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) async def list_tools(self, params: MCPListToolsRequest, *, timeout: float | None = None) -> MCPListToolsResult: - "Lists the tools exposed by a connected MCP server on this session's host.\n\nArgs:\n params: Server name whose tool list should be returned.\n\nReturns:\n Tools exposed by the connected MCP server. Throws when the server is not connected." + "Lists the tools exposed by a connected MCP server on this session's host. This performs a live `tools/list` request. Tool UI metadata is returned independently of whether MCP Apps rendering is enabled for the session.\n\nArgs:\n params: Server name whose tool list should be returned.\n\nReturns:\n Tools exposed by the connected MCP server. Throws when the server is not connected." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return MCPListToolsResult.from_dict(await self._client.request("session.mcp.listTools", params_dict, **_timeout_kwargs(timeout))) @@ -29080,6 +29242,12 @@ async def handle_canvas_action_invoke(params: dict) -> dict | None: return result.value if hasattr(result, 'value') else result client.set_request_handler("canvas.action.invoke", handle_canvas_action_invoke) +# Experimental: this API group is experimental and may change or be removed. +class HooksHandler(Protocol): + async def invoke(self, params: _HookInvokeRequest) -> _HookInvokeResponse: + "Dispatches one SDK callback hook from the runtime to the connection that registered it. Internal transport plumbing: clients opt in through session initialization and the Rust hook processor owns ordering, policy, timeout, and callback routing.\n\nArgs:\n params: Runtime-owned wire payload for a server-to-client hook callback invocation.\n\nReturns:\n Optional output returned by an SDK callback hook." + pass + # Experimental: this API group is experimental and may change or be removed. class LlmInferenceHandler(Protocol): async def http_request_start(self, params: LlmInferenceHTTPRequestStartRequest) -> LlmInferenceHTTPRequestStartResult: @@ -29097,6 +29265,7 @@ async def event(self, params: GitHubTelemetryNotification) -> None: @dataclass class ClientGlobalApiHandlers: + hooks: HooksHandler | None = None llm_inference: LlmInferenceHandler | None = None git_hub_telemetry: GitHubTelemetryHandler | None = None @@ -29110,6 +29279,13 @@ def register_client_global_api_handlers( session_id dispatch key; a single set of handlers serves the entire connection. """ + async def handle_hooks_invoke(params: dict) -> dict | None: + request = _HookInvokeRequest.from_dict(params) + handler = handlers.hooks + if handler is None: raise RuntimeError("No hooks client-global handler registered") + result = await handler.invoke(request) + return result.to_dict() + client.set_request_handler("hooks.invoke", handle_hooks_invoke) async def handle_llm_inference_http_request_start(params: dict) -> dict | None: request = LlmInferenceHTTPRequestStartRequest.from_dict(params) handler = handlers.llm_inference @@ -29328,6 +29504,7 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "HistorySummarizeForHandoffResult", "HistoryTruncateRequest", "HistoryTruncateResult", + "HooksHandler", "Host", "HostType", "InstalledPlugin", @@ -29449,6 +29626,8 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "MCPStartServerRequest", "MCPStartServersResult", "MCPStopServerRequest", + "MCPToolUI", + "MCPToolUIVisibility", "MCPTools", "MCPUnregisterExternalClientRequest", "MarketplaceAddResult", @@ -29890,6 +30069,7 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "SessionMcpAppsCallToolResult", "SessionMetadataSnapshot", "SessionModelList", + "SessionModelPriceCategory", "SessionOpenOptions", "SessionOpenOptionsAdditionalContentExclusionPolicy", "SessionOpenOptionsAdditionalContentExclusionPolicyRule", diff --git a/python/copilot/generated/session_events.py b/python/copilot/generated/session_events.py index fc18ea387b..a7c990e169 100644 --- a/python/copilot/generated/session_events.py +++ b/python/copilot/generated/session_events.py @@ -155,6 +155,7 @@ class SessionEventType(Enum): PENDING_MESSAGES_MODIFIED = "pending_messages.modified" ASSISTANT_TURN_START = "assistant.turn_start" ASSISTANT_INTENT = "assistant.intent" + ASSISTANT_SERVER_TOOL_PROGRESS = "assistant.server_tool_progress" ASSISTANT_REASONING = "assistant.reasoning" ASSISTANT_REASONING_DELTA = "assistant.reasoning_delta" ASSISTANT_TOOL_CALL_DELTA = "assistant.tool_call_delta" @@ -209,6 +210,8 @@ class SessionEventType(Enum): SESSION_LIMITS_EXHAUSTED_COMPLETED = "session_limits_exhausted.completed" # Experimental: this event is part of an experimental API and may change or be removed. SESSION_AUTO_MODE_RESOLVED = "session.auto_mode_resolved" + # Experimental: this event is part of an experimental API and may change or be removed. + SESSION_MANAGED_SETTINGS_RESOLVED = "session.managed_settings_resolved" COMMANDS_CHANGED = "commands.changed" CAPABILITIES_CHANGED = "capabilities.changed" EXIT_PLAN_MODE_REQUESTED = "exit_plan_mode.requested" @@ -1078,6 +1081,51 @@ def to_dict(self) -> dict: return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class SessionManagedSettingsResolvedData: + "Enterprise managed-settings resolution: the effective managed settings the session applied and where they came from, so SDK clients can show users what is enterprise-managed and by which authority. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted; for a session-independent pull, use the SDK `getManagedSettings()` API, which returns the identical payload. Managed settings have a single authoritative source, so the highest-authority present layer (server > device) wins wholesale; `bypassPermissionsDisabled` is deny-wins across layers. Marked experimental while the managed-settings surface stabilizes." + bypass_permissions_disabled: bool + device_managed: bool + fail_closed: bool + managed_keys: list[str] + server_managed: bool + source: ManagedSettingsResolvedSource + settings: Any = None + + @staticmethod + def from_dict(obj: Any) -> "SessionManagedSettingsResolvedData": + assert isinstance(obj, dict) + bypass_permissions_disabled = from_bool(obj.get("bypassPermissionsDisabled")) + device_managed = from_bool(obj.get("deviceManaged")) + fail_closed = from_bool(obj.get("failClosed")) + managed_keys = from_list(from_str, obj.get("managedKeys")) + server_managed = from_bool(obj.get("serverManaged")) + source = parse_enum(ManagedSettingsResolvedSource, obj.get("source")) + settings = obj.get("settings") + return SessionManagedSettingsResolvedData( + bypass_permissions_disabled=bypass_permissions_disabled, + device_managed=device_managed, + fail_closed=fail_closed, + managed_keys=managed_keys, + server_managed=server_managed, + source=source, + settings=settings, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["bypassPermissionsDisabled"] = from_bool(self.bypass_permissions_disabled) + result["deviceManaged"] = from_bool(self.device_managed) + result["failClosed"] = from_bool(self.fail_closed) + result["managedKeys"] = from_list(from_str, self.managed_keys) + result["serverManaged"] = from_bool(self.server_managed) + result["source"] = to_enum(ManagedSettingsResolvedSource, self.source) + if self.settings is not None: + result["settings"] = self.settings + return result + + @dataclass class AbortData: "Turn abort information including the reason for termination" @@ -1398,6 +1446,33 @@ def to_dict(self) -> dict: return result +@dataclass +class AssistantServerToolProgressData: + "Live progress signal for a provider-hosted server tool (e.g. hosted web search) while it runs, before the finalized serverTools envelope lands on the terminal assistant.message" + kind: str + output_index: int + status: str + + @staticmethod + def from_dict(obj: Any) -> "AssistantServerToolProgressData": + assert isinstance(obj, dict) + kind = from_str(obj.get("kind")) + output_index = from_int(obj.get("outputIndex")) + status = from_str(obj.get("status")) + return AssistantServerToolProgressData( + kind=kind, + output_index=output_index, + status=status, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = from_str(self.kind) + result["outputIndex"] = to_int(self.output_index) + result["status"] = from_str(self.status) + return result + + @dataclass class AssistantStreamingDeltaData: "Streaming response progress with cumulative byte count" @@ -3201,6 +3276,29 @@ def to_dict(self) -> dict: return result +@dataclass +class HeaderEntry: + "Single HTTP header entry as a name/value pair." + name: str + value: str + + @staticmethod + def from_dict(obj: Any) -> "HeaderEntry": + assert isinstance(obj, dict) + name = from_str(obj.get("name")) + value = from_str(obj.get("value")) + return HeaderEntry( + name=name, + value=value, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["name"] = from_str(self.name) + result["value"] = from_str(self.value) + return result + + @dataclass class HookEndData: "Hook invocation completion details including output, success status, and error information" @@ -3511,6 +3609,34 @@ def to_dict(self) -> dict: return result +@dataclass +class McpOauthHttpResponse: + "Raw HTTP response details from the OAuth auth challenge, as observed by the runtime." + headers: list[HeaderEntry] + status_code: int + body: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "McpOauthHttpResponse": + assert isinstance(obj, dict) + headers = from_list(HeaderEntry.from_dict, obj.get("headers")) + status_code = from_int(obj.get("statusCode")) + body = from_union([from_none, from_str], obj.get("body")) + return McpOauthHttpResponse( + headers=headers, + status_code=status_code, + body=body, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["headers"] = from_list(lambda x: to_class(HeaderEntry, x), self.headers) + result["statusCode"] = to_int(self.status_code) + if self.body is not None: + result["body"] = from_union([from_none, from_str], self.body) + return result + + @dataclass class McpOauthRequiredData: "OAuth authentication request for an MCP server" @@ -3518,6 +3644,7 @@ class McpOauthRequiredData: request_id: str server_name: str server_url: str + http_response: McpOauthHttpResponse | None = None resource_metadata: str | None = None static_client_config: McpOauthRequiredStaticClientConfig | None = None www_authenticate_params: McpOauthWWWAuthenticateParams | None = None @@ -3529,6 +3656,7 @@ def from_dict(obj: Any) -> "McpOauthRequiredData": request_id = from_str(obj.get("requestId")) server_name = from_str(obj.get("serverName")) server_url = from_str(obj.get("serverUrl")) + http_response = from_union([from_none, McpOauthHttpResponse.from_dict], obj.get("httpResponse")) resource_metadata = from_union([from_none, from_str], obj.get("resourceMetadata")) static_client_config = from_union([from_none, McpOauthRequiredStaticClientConfig.from_dict], obj.get("staticClientConfig")) www_authenticate_params = from_union([from_none, McpOauthWWWAuthenticateParams.from_dict], obj.get("wwwAuthenticateParams")) @@ -3537,6 +3665,7 @@ def from_dict(obj: Any) -> "McpOauthRequiredData": request_id=request_id, server_name=server_name, server_url=server_url, + http_response=http_response, resource_metadata=resource_metadata, static_client_config=static_client_config, www_authenticate_params=www_authenticate_params, @@ -3548,6 +3677,8 @@ def to_dict(self) -> dict: result["requestId"] = from_str(self.request_id) result["serverName"] = from_str(self.server_name) result["serverUrl"] = from_str(self.server_url) + if self.http_response is not None: + result["httpResponse"] = from_union([from_none, lambda x: to_class(McpOauthHttpResponse, x)], self.http_response) if self.resource_metadata is not None: result["resourceMetadata"] = from_union([from_none, from_str], self.resource_metadata) if self.static_client_config is not None: @@ -3623,7 +3754,7 @@ def to_dict(self) -> dict: @dataclass class McpPromptsListChangedData: - "Payload of MCP `list_changed` notification events, emitted when an MCP server announces at runtime that one of its advertised lists changed." + "Payload identifying the MCP server associated with a list change." server_name: str @staticmethod @@ -3642,7 +3773,7 @@ def to_dict(self) -> dict: @dataclass class McpResourcesListChangedData: - "Payload of MCP `list_changed` notification events, emitted when an MCP server announces at runtime that one of its advertised lists changed." + "Payload identifying the MCP server associated with a list change." server_name: str @staticmethod @@ -3709,7 +3840,7 @@ def to_dict(self) -> dict: @dataclass class McpToolsListChangedData: - "Payload of MCP `list_changed` notification events, emitted when an MCP server announces at runtime that one of its advertised lists changed." + "Payload identifying the MCP server associated with a list change." server_name: str @staticmethod @@ -9020,6 +9151,16 @@ class HandoffSourceType(Enum): LOCAL = "local" +class ManagedSettingsResolvedSource(Enum): + "Which channel supplied the effective enterprise managed settings (highest-authority present layer wins wholesale)" + # Account/org policy self-fetched from the GitHub managed-settings endpoint (higher authority). + SERVER = "server" + # Device-level MDM policy discovered from plist/registry/file (lower authority). + DEVICE = "device" + # No managed policy is in force (no layer contributed). + NONE = "none" + + class McpHeadersRefreshCompletedOutcome(Enum): "How the pending MCP headers refresh request resolved." # The host supplied dynamic headers. @@ -9334,7 +9475,7 @@ class WorkspaceFileChangedOperation(Enum): UPDATE = "update" -SessionEventData = SessionStartData | SessionResumeData | SessionRemoteSteerableChangedData | SessionErrorData | SessionIdleData | SessionTitleChangedData | SessionScheduleCreatedData | SessionScheduleCancelledData | SessionScheduleRearmedData | SessionAutopilotObjectiveChangedData | SessionInfoData | SessionWarningData | SessionModelChangeData | SessionModeChangedData | SessionSessionLimitsChangedData | SessionPermissionsChangedData | SessionPlanChangedData | SessionTodosChangedData | SessionWorkspaceFileChangedData | SessionHandoffData | SessionTruncationData | SessionSnapshotRewindData | SessionShutdownData | SessionUsageCheckpointData | SessionContextChangedData | SessionUsageInfoData | SessionCompactionStartData | SessionCompactionCompleteData | SessionTaskCompleteData | UserMessageData | PendingMessagesModifiedData | AssistantTurnStartData | AssistantIntentData | AssistantReasoningData | AssistantReasoningDeltaData | AssistantToolCallDeltaData | AssistantStreamingDeltaData | AssistantMessageData | AssistantMessageStartData | AssistantMessageDeltaData | AssistantTurnEndData | AssistantIdleData | AssistantUsageData | ModelCallFailureData | AbortData | ToolUserRequestedData | ToolExecutionStartData | ToolExecutionPartialResultData | ToolExecutionProgressData | ToolExecutionCompleteData | SkillInvokedData | SubagentStartedData | SubagentCompletedData | SubagentFailedData | SubagentSelectedData | SubagentDeselectedData | HookStartData | HookEndData | HookProgressData | SessionBinaryAssetData | SystemMessageData | SystemNotificationData | PermissionRequestedData | PermissionCompletedData | UserInputRequestedData | UserInputCompletedData | ElicitationRequestedData | ElicitationCompletedData | SamplingRequestedData | SamplingCompletedData | McpOauthRequiredData | McpOauthCompletedData | McpHeadersRefreshRequiredData | McpHeadersRefreshCompletedData | SessionCustomNotificationData | ExternalToolRequestedData | ExternalToolCompletedData | CommandQueuedData | CommandExecuteData | CommandCompletedData | AutoModeSwitchRequestedData | AutoModeSwitchCompletedData | SessionLimitsExhaustedRequestedData | SessionLimitsExhaustedCompletedData | SessionAutoModeResolvedData | CommandsChangedData | CapabilitiesChangedData | ExitPlanModeRequestedData | ExitPlanModeCompletedData | SessionToolsUpdatedData | SessionBackgroundTasksChangedData | SessionSkillsLoadedData | SessionCustomAgentsUpdatedData | SessionMcpServersLoadedData | SessionMcpServerStatusChangedData | McpToolsListChangedData | McpResourcesListChangedData | McpPromptsListChangedData | SessionExtensionsLoadedData | SessionCanvasOpenedData | SessionCanvasRegistryChangedData | SessionCanvasClosedData | SessionCanvasUnavailableData | SessionCanvasRecordedData | SessionCanvasRemovedData | SessionExtensionsAttachmentsPushedData | McpAppToolCallCompleteData | RawSessionEventData | Data +SessionEventData = SessionStartData | SessionResumeData | SessionRemoteSteerableChangedData | SessionErrorData | SessionIdleData | SessionTitleChangedData | SessionScheduleCreatedData | SessionScheduleCancelledData | SessionScheduleRearmedData | SessionAutopilotObjectiveChangedData | SessionInfoData | SessionWarningData | SessionModelChangeData | SessionModeChangedData | SessionSessionLimitsChangedData | SessionPermissionsChangedData | SessionPlanChangedData | SessionTodosChangedData | SessionWorkspaceFileChangedData | SessionHandoffData | SessionTruncationData | SessionSnapshotRewindData | SessionShutdownData | SessionUsageCheckpointData | SessionContextChangedData | SessionUsageInfoData | SessionCompactionStartData | SessionCompactionCompleteData | SessionTaskCompleteData | UserMessageData | PendingMessagesModifiedData | AssistantTurnStartData | AssistantIntentData | AssistantServerToolProgressData | AssistantReasoningData | AssistantReasoningDeltaData | AssistantToolCallDeltaData | AssistantStreamingDeltaData | AssistantMessageData | AssistantMessageStartData | AssistantMessageDeltaData | AssistantTurnEndData | AssistantIdleData | AssistantUsageData | ModelCallFailureData | AbortData | ToolUserRequestedData | ToolExecutionStartData | ToolExecutionPartialResultData | ToolExecutionProgressData | ToolExecutionCompleteData | SkillInvokedData | SubagentStartedData | SubagentCompletedData | SubagentFailedData | SubagentSelectedData | SubagentDeselectedData | HookStartData | HookEndData | HookProgressData | SessionBinaryAssetData | SystemMessageData | SystemNotificationData | PermissionRequestedData | PermissionCompletedData | UserInputRequestedData | UserInputCompletedData | ElicitationRequestedData | ElicitationCompletedData | SamplingRequestedData | SamplingCompletedData | McpOauthRequiredData | McpOauthCompletedData | McpHeadersRefreshRequiredData | McpHeadersRefreshCompletedData | SessionCustomNotificationData | ExternalToolRequestedData | ExternalToolCompletedData | CommandQueuedData | CommandExecuteData | CommandCompletedData | AutoModeSwitchRequestedData | AutoModeSwitchCompletedData | SessionLimitsExhaustedRequestedData | SessionLimitsExhaustedCompletedData | SessionAutoModeResolvedData | SessionManagedSettingsResolvedData | CommandsChangedData | CapabilitiesChangedData | ExitPlanModeRequestedData | ExitPlanModeCompletedData | SessionToolsUpdatedData | SessionBackgroundTasksChangedData | SessionSkillsLoadedData | SessionCustomAgentsUpdatedData | SessionMcpServersLoadedData | SessionMcpServerStatusChangedData | McpToolsListChangedData | McpResourcesListChangedData | McpPromptsListChangedData | SessionExtensionsLoadedData | SessionCanvasOpenedData | SessionCanvasRegistryChangedData | SessionCanvasClosedData | SessionCanvasUnavailableData | SessionCanvasRecordedData | SessionCanvasRemovedData | SessionExtensionsAttachmentsPushedData | McpAppToolCallCompleteData | RawSessionEventData | Data @dataclass @@ -9393,6 +9534,7 @@ def from_dict(obj: Any) -> "SessionEvent": case SessionEventType.PENDING_MESSAGES_MODIFIED: data = PendingMessagesModifiedData.from_dict(data_obj) case SessionEventType.ASSISTANT_TURN_START: data = AssistantTurnStartData.from_dict(data_obj) case SessionEventType.ASSISTANT_INTENT: data = AssistantIntentData.from_dict(data_obj) + case SessionEventType.ASSISTANT_SERVER_TOOL_PROGRESS: data = AssistantServerToolProgressData.from_dict(data_obj) case SessionEventType.ASSISTANT_REASONING: data = AssistantReasoningData.from_dict(data_obj) case SessionEventType.ASSISTANT_REASONING_DELTA: data = AssistantReasoningDeltaData.from_dict(data_obj) case SessionEventType.ASSISTANT_TOOL_CALL_DELTA: data = AssistantToolCallDeltaData.from_dict(data_obj) @@ -9445,6 +9587,7 @@ def from_dict(obj: Any) -> "SessionEvent": case SessionEventType.SESSION_LIMITS_EXHAUSTED_REQUESTED: data = SessionLimitsExhaustedRequestedData.from_dict(data_obj) case SessionEventType.SESSION_LIMITS_EXHAUSTED_COMPLETED: data = SessionLimitsExhaustedCompletedData.from_dict(data_obj) case SessionEventType.SESSION_AUTO_MODE_RESOLVED: data = SessionAutoModeResolvedData.from_dict(data_obj) + case SessionEventType.SESSION_MANAGED_SETTINGS_RESOLVED: data = SessionManagedSettingsResolvedData.from_dict(data_obj) case SessionEventType.COMMANDS_CHANGED: data = CommandsChangedData.from_dict(data_obj) case SessionEventType.CAPABILITIES_CHANGED: data = CapabilitiesChangedData.from_dict(data_obj) case SessionEventType.EXIT_PLAN_MODE_REQUESTED: data = ExitPlanModeRequestedData.from_dict(data_obj) @@ -9513,6 +9656,7 @@ def session_event_to_dict(x: SessionEvent) -> Any: "AssistantMessageToolRequestType", "AssistantReasoningData", "AssistantReasoningDeltaData", + "AssistantServerToolProgressData", "AssistantStreamingDeltaData", "AssistantToolCallDeltaData", "AssistantTurnEndData", @@ -9596,10 +9740,12 @@ def session_event_to_dict(x: SessionEvent) -> Any: "GitHubRepoRef", "HandoffRepository", "HandoffSourceType", + "HeaderEntry", "HookEndData", "HookEndError", "HookProgressData", "HookStartData", + "ManagedSettingsResolvedSource", "McpAppToolCallCompleteData", "McpAppToolCallCompleteError", "McpAppToolCallCompleteToolMeta", @@ -9610,6 +9756,7 @@ def session_event_to_dict(x: SessionEvent) -> Any: "McpHeadersRefreshRequiredReason", "McpOauthCompletedData", "McpOauthCompletionOutcome", + "McpOauthHttpResponse", "McpOauthRequestReason", "McpOauthRequiredData", "McpOauthRequiredStaticClientConfig", @@ -9709,6 +9856,7 @@ def session_event_to_dict(x: SessionEvent) -> Any: "SessionLimitsExhaustedRequestedData", "SessionLimitsExhaustedResponse", "SessionLimitsExhaustedResponseAction", + "SessionManagedSettingsResolvedData", "SessionMcpServerStatusChangedData", "SessionMcpServersLoadedData", "SessionMode", diff --git a/python/test_client.py b/python/test_client.py index aac334cd4a..66941f289d 100644 --- a/python/test_client.py +++ b/python/test_client.py @@ -2616,12 +2616,33 @@ async def on_telemetry(notification): await client.force_stop() @pytest.mark.asyncio - async def test_event_handler_not_registered_without_option(self): + async def test_event_not_forwarded_without_option(self): + # Client-global handlers are always registered (so that hooks.invoke works), + # but without the on_github_telemetry option the telemetry adapter is inert: + # incoming events must not be forwarded to any callback. client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: - assert "gitHubTelemetry.event" not in client._client.notification_method_handlers - assert "gitHubTelemetry.event" not in client._client.request_handlers + assert client._on_github_telemetry is None + + # Dispatching a telemetry event is a harmless no-op when not opted in. + client._client._handle_message( + { + "jsonrpc": "2.0", + "method": "gitHubTelemetry.event", + "params": { + "sessionId": "sess-no-telemetry", + "restricted": False, + "event": { + "kind": "tool_call_executed", + "metrics": {"duration_ms": 1.0}, + "properties": {"tool": "shell"}, + "session_id": "sess-no-telemetry", + }, + }, + } + ) + await asyncio.sleep(0) finally: await client.force_stop() diff --git a/rust/src/generated/api_types.rs b/rust/src/generated/api_types.rs index 7ae4020cab..e243ec1dc2 100644 --- a/rust/src/generated/api_types.rs +++ b/rust/src/generated/api_types.rs @@ -4055,6 +4055,24 @@ pub struct HMACAuthInfo { pub r#type: HMACAuthInfoType, } +/// Runtime-owned wire payload for a server-to-client hook callback invocation. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct HookInvokeRequest { + #[doc(hidden)] + pub(crate) hook_type: HookType, + pub input: serde_json::Value, + pub session_id: SessionId, +} + +/// Optional output returned by an SDK callback hook. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct HookInvokeResponse { + #[serde(skip_serializing_if = "Option::is_none")] + pub output: Option, +} + /// Installed plugin record from global state, with marketplace, version, install time, enabled state, cache path, and source. /// ///
@@ -5458,7 +5476,26 @@ pub struct McpListToolsRequest { pub server_name: String, } -/// MCP tool metadata with tool name and optional description. +/// Normalized MCP Apps discovery metadata from a tool's `_meta.ui` block. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct McpToolUi { + /// URI of the tool's MCP App resource, typically a `ui://` resource identifier. Use `session.mcp.resources.read` to fetch its HTML and resource metadata. + #[serde(skip_serializing_if = "Option::is_none")] + pub resource_uri: Option, + /// Tool visibility advertised by the server. When absent, MCP Apps defaults apply. + #[serde(skip_serializing_if = "Option::is_none")] + pub visibility: Option>, +} + +/// MCP tool metadata with tool name, optional description, and normalized MCP Apps discovery metadata. /// ///
/// @@ -5474,6 +5511,9 @@ pub struct McpTools { pub description: Option, /// Tool name. pub name: String, + /// Normalized MCP Apps discovery metadata. An empty object indicates that a valid `_meta.ui` block was present without recognized fields. + #[serde(skip_serializing_if = "Option::is_none")] + pub ui: Option, } /// Tools exposed by the connected MCP server. Throws when the server is not connected. @@ -11657,6 +11697,21 @@ pub struct SessionMetadataSnapshot { pub workspace_path: Option, } +/// Cost-category metadata for a CAPI model. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SessionModelPriceCategory { + pub id: String, + pub price_category: ModelPickerPriceCategory, +} + /// The list of models available to this session. /// ///
@@ -11670,6 +11725,9 @@ pub struct SessionMetadataSnapshot { pub struct SessionModelList { /// Available models, ordered with the most preferred default first. Includes both Copilot (CAPI) models and any registry BYOK models; a BYOK model appears under its provider-qualified selection id (`provider/id`). pub list: Vec, + /// Cost categories for the full CAPI catalog, including picker-disabled models that Auto may select. Metadata only; entries absent from `list` are not manually selectable. + #[serde(skip_serializing_if = "Option::is_none")] + pub model_price_categories: Option>, /// Per-quota snapshots returned alongside the model list, keyed by quota type. #[serde(skip_serializing_if = "Option::is_none")] pub quota_snapshots: Option>, @@ -13222,6 +13280,9 @@ pub struct SessionUpdateOptionsParams { #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SessionUpdateOptionsResult { + /// Number of hooks loaded from installed plugins, returned when installedPlugins is updated + #[serde(skip_serializing_if = "Option::is_none")] + pub plugin_hook_count: Option, /// Whether the operation succeeded pub success: bool, } @@ -16514,6 +16575,9 @@ pub struct SessionModelSetReasoningEffortResult { pub struct SessionModelListResult { /// Available models, ordered with the most preferred default first. Includes both Copilot (CAPI) models and any registry BYOK models; a BYOK model appears under its provider-qualified selection id (`provider/id`). pub list: Vec, + /// Cost categories for the full CAPI catalog, including picker-disabled models that Auto may select. Metadata only; entries absent from `list` are not manually selectable. + #[serde(skip_serializing_if = "Option::is_none")] + pub model_price_categories: Option>, /// Per-quota snapshots returned alongside the model list, keyed by quota type. #[serde(skip_serializing_if = "Option::is_none")] pub quota_snapshots: Option>, @@ -17914,6 +17978,9 @@ pub struct SessionProviderAddResult { #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SessionOptionsUpdateResult { + /// Number of hooks loaded from installed plugins, returned when installedPlugins is updated + #[serde(skip_serializing_if = "Option::is_none")] + pub plugin_hook_count: Option, /// Whether the operation succeeded pub success: bool, } @@ -20781,6 +20848,63 @@ pub enum HMACAuthInfoType { Hmac, } +/// Hook event name dispatched through the SDK callback transport. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum HookType { + /// Runs before a tool is invoked. + #[serde(rename = "preToolUse")] + PreToolUse, + /// Runs before an MCP tool is invoked. + #[serde(rename = "preMcpToolCall")] + PreMcpToolCall, + /// Runs after a tool completes successfully. + #[serde(rename = "postToolUse")] + PostToolUse, + /// Runs after a tool fails. + #[serde(rename = "postToolUseFailure")] + PostToolUseFailure, + /// Runs after the user submits a prompt. + #[serde(rename = "userPromptSubmitted")] + UserPromptSubmitted, + /// Runs when a session starts. + #[serde(rename = "sessionStart")] + SessionStart, + /// Runs when a session ends. + #[serde(rename = "sessionEnd")] + SessionEnd, + /// Runs after an agent result is produced. + #[serde(rename = "postResult")] + PostResult, + /// Runs before a pull request description is generated. + #[serde(rename = "prePRDescription")] + PrePRDescription, + /// Runs when the agent encounters an error. + #[serde(rename = "errorOccurred")] + ErrorOccurred, + /// Runs when the agent stops. + #[serde(rename = "agentStop")] + AgentStop, + /// Runs when a subagent starts. + #[serde(rename = "subagentStart")] + SubagentStart, + /// Runs when a subagent stops. + #[serde(rename = "subagentStop")] + SubagentStop, + /// Runs before conversation context is compacted. + #[serde(rename = "preCompact")] + PreCompact, + /// Runs when the agent requests permission. + #[serde(rename = "permissionRequest")] + PermissionRequest, + /// Runs when the agent emits a notification. + #[serde(rename = "notification")] + Notification, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// Constant value. Always "github". #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum InstalledPluginSourceGitHubSource { @@ -21205,6 +21329,28 @@ pub enum McpHeadersHandlePendingHeadersRefreshRequest { None(McpHeadersHandlePendingHeadersRefreshRequestNone), } +/// Consumer allowed to call an MCP tool. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum McpToolUiVisibility { + /// The model may call the tool. + #[serde(rename = "model")] + Model, + /// An MCP App view may call the tool. + #[serde(rename = "app")] + App, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum McpOauthPendingRequestResponseTokenKind { #[serde(rename = "token")] diff --git a/rust/src/generated/rpc.rs b/rust/src/generated/rpc.rs index 64b663e59e..403933a27c 100644 --- a/rust/src/generated/rpc.rs +++ b/rust/src/generated/rpc.rs @@ -4349,7 +4349,7 @@ impl<'a> SessionRpcMcp<'a> { Ok(serde_json::from_value(_value)?) } - /// Lists the tools exposed by a connected MCP server on this session's host. + /// Lists the tools exposed by a connected MCP server on this session's host. This performs a live `tools/list` request. Tool UI metadata is returned independently of whether MCP Apps rendering is enabled for the session. /// /// Wire method: `session.mcp.listTools`. /// diff --git a/rust/src/generated/session_events.rs b/rust/src/generated/session_events.rs index aa2f1e3a2a..cf670c4856 100644 --- a/rust/src/generated/session_events.rs +++ b/rust/src/generated/session_events.rs @@ -77,6 +77,8 @@ pub enum SessionEventType { AssistantTurnStart, #[serde(rename = "assistant.intent")] AssistantIntent, + #[serde(rename = "assistant.server_tool_progress")] + AssistantServerToolProgress, #[serde(rename = "assistant.reasoning")] AssistantReasoning, #[serde(rename = "assistant.reasoning_delta")] @@ -195,6 +197,15 @@ pub enum SessionEventType { ///
#[serde(rename = "session.auto_mode_resolved")] SessionAutoModeResolved, + /// + ///
+ /// + /// **Experimental.** This type is part of an experimental wire-protocol surface + /// and may change or be removed in future SDK or CLI releases. + /// + ///
+ #[serde(rename = "session.managed_settings_resolved")] + SessionManagedSettingsResolved, #[serde(rename = "commands.changed")] CommandsChanged, #[serde(rename = "capabilities.changed")] @@ -359,6 +370,8 @@ pub enum SessionEventData { AssistantTurnStart(AssistantTurnStartData), #[serde(rename = "assistant.intent")] AssistantIntent(AssistantIntentData), + #[serde(rename = "assistant.server_tool_progress")] + AssistantServerToolProgress(AssistantServerToolProgressData), #[serde(rename = "assistant.reasoning")] AssistantReasoning(AssistantReasoningData), #[serde(rename = "assistant.reasoning_delta")] @@ -470,6 +483,15 @@ pub enum SessionEventData { ///
#[serde(rename = "session.auto_mode_resolved")] SessionAutoModeResolved(SessionAutoModeResolvedData), + /// + ///
+ /// + /// **Experimental.** This type is part of an experimental wire-protocol surface + /// and may change or be removed in future SDK or CLI releases. + /// + ///
+ #[serde(rename = "session.managed_settings_resolved")] + SessionManagedSettingsResolved(SessionManagedSettingsResolvedData), #[serde(rename = "commands.changed")] CommandsChanged(CommandsChangedData), #[serde(rename = "capabilities.changed")] @@ -1461,6 +1483,18 @@ pub struct AssistantIntentData { pub intent: String, } +/// Session event "assistant.server_tool_progress". Live progress signal for a provider-hosted server tool (e.g. hosted web search) while it runs, before the finalized serverTools envelope lands on the terminal assistant.message +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AssistantServerToolProgressData { + /// Kind of hosted server tool that is running. Only `web_search` is emitted today. + pub kind: String, + /// Position of the hosted tool call in the response output. Stable across the call's lifecycle events (unlike the provider's per-event item id, which CAPI rotates), so the host keys the live in-progress row on it. + pub output_index: i64, + /// Lifecycle status of the hosted call: `in_progress`, `searching`, or `completed`. + pub status: String, +} + /// Session event "assistant.reasoning". Assistant reasoning content for timeline display with complete thinking text #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -3653,6 +3687,29 @@ pub struct SamplingCompletedData { pub request_id: RequestId, } +/// Single HTTP header entry as a name/value pair. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct HeaderEntry { + /// HTTP response header name as observed by the runtime. + pub name: String, + /// HTTP response header value as observed by the runtime. + pub value: String, +} + +/// Raw HTTP response details from the OAuth auth challenge, as observed by the runtime. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct McpOauthHttpResponse { + /// Complete UTF-8 response body for host-specific challenge handling, including an empty string for an empty body. Omitted when the complete body is not valid UTF-8; body read failures fail the HTTP operation rather than exposing a partial response. + #[serde(skip_serializing_if = "Option::is_none")] + pub body: Option, + /// HTTP response headers as observed by the runtime. Order and casing are transport-dependent, and duplicate header names may appear multiple times. + pub headers: Vec, + /// HTTP status code returned with the auth challenge. + pub status_code: i32, +} + /// Static OAuth client configuration, if the server specifies one #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -3689,6 +3746,9 @@ pub struct McpOauthWWWAuthenticateParams { #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct McpOauthRequiredData { + /// Raw HTTP response details from the OAuth auth challenge, as observed by the runtime. Header order and casing are transport-dependent, and duplicate header names may appear multiple times. + #[serde(skip_serializing_if = "Option::is_none")] + pub http_response: Option, /// Why the runtime is requesting host-provided OAuth credentials. pub reason: McpOauthRequestReason, /// Unique identifier for this OAuth request; used to respond via session.mcp.oauth.handlePendingRequest @@ -3916,6 +3976,34 @@ pub struct SessionAutoModeResolvedData { pub reasoning_bucket: Option, } +/// Session event "session.managed_settings_resolved". Enterprise managed-settings resolution: the effective managed settings the session applied and where they came from, so SDK clients can show users what is enterprise-managed and by which authority. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted; for a session-independent pull, use the SDK `getManagedSettings()` API, which returns the identical payload. Managed settings have a single authoritative source, so the highest-authority present layer (server > device) wins wholesale; `bypassPermissionsDisabled` is deny-wins across layers. Marked experimental while the managed-settings surface stabilizes. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SessionManagedSettingsResolvedData { + /// Whether enterprise policy disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers, and forced on when `failClosed` is true. + pub bypass_permissions_disabled: bool, + /// Whether the device (MDM/plist/registry/file) managed-settings layer was present + pub device_managed: bool, + /// Whether managed policy could not be determined (e.g. a failed server fetch) and the session fell back to the fail-closed restriction. When true, restrictions such as disabling bypass-permissions are enforced even though `settings` may be absent. + pub fail_closed: bool, + /// The setting keys under enterprise management in the effective managed settings (e.g. `model`, `enabledPlugins`, `permissions`). Empty when no managed settings are in force. + pub managed_keys: Vec, + /// Whether the server (account/org) managed-settings layer was present + pub server_managed: bool, + /// The effective (resolved) managed settings values, so clients can render exactly what is enforced. Absent when no managed policy is in force. + #[serde(skip_serializing_if = "Option::is_none")] + pub settings: Option, + /// Which channel supplied the effective managed settings (the winning layer), or `none` when no policy is in force + pub source: ManagedSettingsResolvedSource, +} + /// A single slash command available in the session, as listed by the `commands.changed` event. #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -4119,7 +4207,7 @@ pub struct SessionMcpServerStatusChangedData { pub status: McpServerStatus, } -/// Session event "mcp.tools.list_changed". Payload of MCP `list_changed` notification events, emitted when an MCP server announces at runtime that one of its advertised lists changed. +/// Session event "mcp.tools.list_changed". Payload identifying the MCP server associated with a list change. #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct McpToolsListChangedData { @@ -4127,7 +4215,7 @@ pub struct McpToolsListChangedData { pub server_name: String, } -/// Session event "mcp.resources.list_changed". Payload of MCP `list_changed` notification events, emitted when an MCP server announces at runtime that one of its advertised lists changed. +/// Session event "mcp.resources.list_changed". Payload identifying the MCP server associated with a list change. #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct McpResourcesListChangedData { @@ -4135,7 +4223,7 @@ pub struct McpResourcesListChangedData { pub server_name: String, } -/// Session event "mcp.prompts.list_changed". Payload of MCP `list_changed` notification events, emitted when an MCP server announces at runtime that one of its advertised lists changed. +/// Session event "mcp.prompts.list_changed". Payload identifying the MCP server associated with a list change. #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct McpPromptsListChangedData { @@ -5570,6 +5658,24 @@ pub enum AutoModeResolvedReasoningBucket { Unknown, } +/// Which channel supplied the effective enterprise managed settings (highest-authority present layer wins wholesale) +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ManagedSettingsResolvedSource { + /// Account/org policy self-fetched from the GitHub managed-settings endpoint (higher authority). + #[serde(rename = "server")] + Server, + /// Device-level MDM policy discovered from plist/registry/file (lower authority). + #[serde(rename = "device")] + Device, + /// No managed policy is in force (no layer contributed). + #[serde(rename = "none")] + None, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// Exit plan mode action #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum ExitPlanModeAction { diff --git a/rust/src/hooks.rs b/rust/src/hooks.rs index ec8cdfa3a3..0c3d64076f 100644 --- a/rust/src/hooks.rs +++ b/rust/src/hooks.rs @@ -27,8 +27,8 @@ pub struct HookContext { pub struct PreToolUseInput { /// The runtime session ID of the session that triggered the hook. pub session_id: String, - /// Unix timestamp (ms). - pub timestamp: i64, + /// Unix timestamp in ms (the runtime serializes this as a JSON float). + pub timestamp: f64, /// Working directory. #[serde(rename = "cwd")] pub working_directory: PathBuf, @@ -65,8 +65,8 @@ pub struct PreToolUseOutput { pub struct PreMcpToolCallInput { /// The runtime session ID of the session that triggered the hook. pub session_id: String, - /// Unix timestamp (ms). - pub timestamp: i64, + /// Unix timestamp in ms (the runtime serializes this as a JSON float). + pub timestamp: f64, /// Working directory. #[serde(rename = "cwd")] pub working_directory: PathBuf, @@ -104,8 +104,8 @@ pub struct PreMcpToolCallOutput { pub struct PostToolUseInput { /// The runtime session ID of the session that triggered the hook. pub session_id: String, - /// Unix timestamp (ms). - pub timestamp: i64, + /// Unix timestamp in ms (the runtime serializes this as a JSON float). + pub timestamp: f64, /// Working directory. #[serde(rename = "cwd")] pub working_directory: PathBuf, @@ -144,8 +144,8 @@ pub struct PostToolUseOutput { pub struct PostToolUseFailureInput { /// The runtime session ID of the session that triggered the hook. pub session_id: String, - /// Unix timestamp (ms). - pub timestamp: i64, + /// Unix timestamp in ms (the runtime serializes this as a JSON float). + pub timestamp: f64, /// Working directory. #[serde(rename = "cwd")] pub working_directory: PathBuf, @@ -175,8 +175,8 @@ pub struct PostToolUseFailureOutput { pub struct UserPromptSubmittedInput { /// The runtime session ID of the session that triggered the hook. pub session_id: String, - /// Unix timestamp (ms). - pub timestamp: i64, + /// Unix timestamp in ms (the runtime serializes this as a JSON float). + pub timestamp: f64, /// Working directory. #[serde(rename = "cwd")] pub working_directory: PathBuf, @@ -205,8 +205,8 @@ pub struct UserPromptSubmittedOutput { pub struct SessionStartInput { /// The runtime session ID of the session that triggered the hook. pub session_id: String, - /// Unix timestamp (ms). - pub timestamp: i64, + /// Unix timestamp in ms (the runtime serializes this as a JSON float). + pub timestamp: f64, /// Working directory. #[serde(rename = "cwd")] pub working_directory: PathBuf, @@ -235,8 +235,8 @@ pub struct SessionStartOutput { pub struct SessionEndInput { /// The runtime session ID of the session that triggered the hook. pub session_id: String, - /// Unix timestamp (ms). - pub timestamp: i64, + /// Unix timestamp in ms (the runtime serializes this as a JSON float). + pub timestamp: f64, /// Working directory. #[serde(rename = "cwd")] pub working_directory: PathBuf, @@ -271,8 +271,8 @@ pub struct SessionEndOutput { pub struct ErrorOccurredInput { /// The runtime session ID of the session that triggered the hook. pub session_id: String, - /// Unix timestamp (ms). - pub timestamp: i64, + /// Unix timestamp in ms (the runtime serializes this as a JSON float). + pub timestamp: f64, /// Working directory. #[serde(rename = "cwd")] pub working_directory: PathBuf, diff --git a/rust/tests/e2e/hooks_extended.rs b/rust/tests/e2e/hooks_extended.rs index 259d462d56..ab93a0c3cd 100644 --- a/rust/tests/e2e/hooks_extended.rs +++ b/rust/tests/e2e/hooks_extended.rs @@ -36,7 +36,7 @@ async fn should_invoke_onsessionstart_hook_on_new_session() { session.send_and_wait("Say hi").await.expect("send"); let input = recv_with_timeout(&mut rx, "sessionStart hook").await; assert_eq!(input.source, "new"); - assert!(input.timestamp > 0); + assert!(input.timestamp > 0.0); assert!(!input.working_directory.as_os_str().is_empty()); session.disconnect().await.expect("disconnect session"); @@ -68,7 +68,7 @@ async fn should_invoke_onuserpromptsubmitted_hook_when_sending_a_message() { session.send_and_wait("Say hello").await.expect("send"); let input = recv_with_timeout(&mut rx, "userPromptSubmitted hook").await; assert!(input.prompt.contains("Say hello")); - assert!(input.timestamp > 0); + assert!(input.timestamp > 0.0); assert!(!input.working_directory.as_os_str().is_empty()); session.disconnect().await.expect("disconnect session"); @@ -100,7 +100,7 @@ async fn should_invoke_onsessionend_hook_when_session_is_disconnected() { session.send_and_wait("Say hi").await.expect("send"); session.disconnect().await.expect("disconnect session"); let input = recv_with_timeout(&mut rx, "sessionEnd hook").await; - assert!(input.timestamp > 0); + assert!(input.timestamp > 0.0); assert!(!input.working_directory.as_os_str().is_empty()); client.stop().await.expect("stop client"); @@ -237,7 +237,7 @@ async fn should_invoke_sessionend_hook() { session.send_and_wait("Say bye").await.expect("send"); session.disconnect().await.expect("disconnect session"); let input = recv_with_timeout(&mut rx, "sessionEnd hook").await; - assert!(input.timestamp > 0); + assert!(input.timestamp > 0.0); client.stop().await.expect("stop client"); }) @@ -412,7 +412,7 @@ async fn should_invoke_posttoolusefailure_hook_for_failed_tool_result() { .as_str() .is_some_and(|path| path.contains("missing.txt")) ); - assert!(input.timestamp > 0); + assert!(input.timestamp > 0.0); assert!(!input.working_directory.as_os_str().is_empty()); assert!( assistant_message_content(&answer).contains("HOOK_FAILURE_GUIDANCE_APPLIED") diff --git a/rust/tests/e2e/pre_mcp_tool_call_hook.rs b/rust/tests/e2e/pre_mcp_tool_call_hook.rs index 5dc782c963..fd05796fcd 100644 --- a/rust/tests/e2e/pre_mcp_tool_call_hook.rs +++ b/rust/tests/e2e/pre_mcp_tool_call_hook.rs @@ -126,7 +126,7 @@ async fn should_set_meta_via_premcptoolcall_hook() { assert_eq!(input.server_name, "meta-echo"); assert_eq!(input.tool_name, "echo_meta"); assert!(!input.working_directory.as_os_str().is_empty()); - assert!(input.timestamp > 0); + assert!(input.timestamp > 0.0); session.disconnect().await.expect("disconnect session"); client.stop().await.expect("stop client"); diff --git a/scripts/codegen/csharp.ts b/scripts/codegen/csharp.ts index caa67e1682..24ec217f5b 100644 --- a/scripts/codegen/csharp.ts +++ b/scripts/codegen/csharp.ts @@ -27,6 +27,7 @@ import { findSharedSchemaDefinitions, postProcessSchema, propagateInternalVisibility, + filterNodeByVisibility, resolveRef, resolveObjectSchema, resolveSchema, @@ -2490,11 +2491,23 @@ function generateRpcCode( let sessionRpcParts: string[] = []; if (schema.session) sessionRpcParts = emitSessionRpcClasses(schema.session, classes); + // Client handler surfaces (interfaces, handler properties, RPC registration) + // are only generated for public methods. Internal client methods (e.g. + // `hooks.invoke`) are runtime transport plumbing and must not surface any + // generated code — including their request/result DTOs, which would + // otherwise leak as `internal` types referenced by a `public` handler + // interface (CS0050/CS0051 inconsistent accessibility). let clientSessionParts: string[] = []; - if (schema.clientSession) clientSessionParts = emitClientSessionApiRegistration(schema.clientSession, classes); + if (schema.clientSession) { + const publicClientSession = filterNodeByVisibility(schema.clientSession, "public"); + if (publicClientSession) clientSessionParts = emitClientSessionApiRegistration(publicClientSession, classes); + } let clientGlobalParts: string[] = []; - if (schema.clientGlobal) clientGlobalParts = emitClientGlobalApiRegistration(schema.clientGlobal, classes); + if (schema.clientGlobal) { + const publicClientGlobal = filterNodeByVisibility(schema.clientGlobal, "public"); + if (publicClientGlobal) clientGlobalParts = emitClientGlobalApiRegistration(publicClientGlobal, classes); + } const lines: string[] = []; lines.push(`${COPYRIGHT} diff --git a/test/harness/package-lock.json b/test/harness/package-lock.json index 6405fdcdd4..1a8462d661 100644 --- a/test/harness/package-lock.json +++ b/test/harness/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "devDependencies": { - "@github/copilot": "^1.0.71-2", + "@github/copilot": "^1.0.71", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "@types/node-forge": "^1.3.14", @@ -501,9 +501,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.71-2", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.71-2.tgz", - "integrity": "sha512-En1onRCWjPy64wnjB9B6T6nXgPI7/myHksMotpKHzh8+CnVbaYQr2n/rBKM1BVScWgpA0zn19HmKZZlg82LW7A==", + "version": "1.0.71", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.71.tgz", + "integrity": "sha512-F3axBi+sXSLYDJbxCBW36bM6MYKNC2rlyAf3Ivo/MjiHKKJW7j5AmaR1IRYS9Gt8r9mxOwWFM1cJFA+CuLaR8g==", "dev": true, "license": "SEE LICENSE IN LICENSE.md", "dependencies": { @@ -513,20 +513,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.71-2", - "@github/copilot-darwin-x64": "1.0.71-2", - "@github/copilot-linux-arm64": "1.0.71-2", - "@github/copilot-linux-x64": "1.0.71-2", - "@github/copilot-linuxmusl-arm64": "1.0.71-2", - "@github/copilot-linuxmusl-x64": "1.0.71-2", - "@github/copilot-win32-arm64": "1.0.71-2", - "@github/copilot-win32-x64": "1.0.71-2" + "@github/copilot-darwin-arm64": "1.0.71", + "@github/copilot-darwin-x64": "1.0.71", + "@github/copilot-linux-arm64": "1.0.71", + "@github/copilot-linux-x64": "1.0.71", + "@github/copilot-linuxmusl-arm64": "1.0.71", + "@github/copilot-linuxmusl-x64": "1.0.71", + "@github/copilot-win32-arm64": "1.0.71", + "@github/copilot-win32-x64": "1.0.71" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.71-2", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.71-2.tgz", - "integrity": "sha512-6DA1W3RFbyDPL/MXPeAzM9HxS7hvZOnToJHupGf4QGGs4dG2dzmTyEv0LkD4rFtyc1dx6QtyOjRt5vUsWw39Xw==", + "version": "1.0.71", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.71.tgz", + "integrity": "sha512-mEWzyqbqRAWgyU7i2uuSRoVPx/TwaFQX0nZmw0bc30aJ0BnO7cy2kYQyCHw8ykmf/tfxT0xauZ6k0BOFmWizzQ==", "cpu": [ "arm64" ], @@ -541,9 +541,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.71-2", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.71-2.tgz", - "integrity": "sha512-u/DWMhTCFaGf9TE70IgXKk0/9kWiijiHfEMVRqedbzdUNxGQ5u4lsaquEirHj3sSegVw8MZzgfKAzrT9CTr0aA==", + "version": "1.0.71", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.71.tgz", + "integrity": "sha512-Md9yEg406OBVBx3w4PeEj62TubulVLBcHleqmCoOoUmPgUxPZotUbrqz3rtbzADbXfrrD7JWvVsbd2UiNL194w==", "cpu": [ "x64" ], @@ -558,9 +558,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.71-2", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.71-2.tgz", - "integrity": "sha512-xKKk5o+zFJZ3EcoDOiCOdn1sbc8N/tHkn2j2sFQahE2SDp18ZpA2lzZp6XZ32VgxQJx0qlhPWh5BRn32Sc9csw==", + "version": "1.0.71", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.71.tgz", + "integrity": "sha512-ykLJYOqBj3jRB5IJCDugLClAqbr7DmtTbUjlNY7+Jdq/n6i+d7xUQGclf1IWL5gnxbGQVAf+zkToD+sRM389Kg==", "cpu": [ "arm64" ], @@ -575,9 +575,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.71-2", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.71-2.tgz", - "integrity": "sha512-We/kZNlEAXxhm9vMBae63Yy07RXUsTlOLsg5WrG7aNm0kh5ocUFpf5EodvtBbTmVIlGCvvm/RM1cYic6lPtD+w==", + "version": "1.0.71", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.71.tgz", + "integrity": "sha512-pC0FNHG+BBwZd6yZlM85kkAGN+uJhM6o+THi76N2GnnSxmw7+remb1mvYxdgRVbdCm+LBUIbCKRWJLuMwrfb6A==", "cpu": [ "x64" ], @@ -592,9 +592,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.71-2", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.71-2.tgz", - "integrity": "sha512-Hnyz/C4uNAXZVt6/RMtQBI89W0fxvUizm0mlp/ZohSthN03fv/PppbEsY7U5WqbTuDBuOKIU4kf3fCDC2elMCQ==", + "version": "1.0.71", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.71.tgz", + "integrity": "sha512-hBmDljFTjacxqZTasCEy43H8EIzuXB/hHEBBCMFjhB9J00nIxsO6Dh0woTifKpx7knTYZdpTjjca3D0pAoZlUA==", "cpu": [ "arm64" ], @@ -609,9 +609,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.71-2", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.71-2.tgz", - "integrity": "sha512-CQDeUqq2wbM+a/Tec68O+6Gp8f3cUZ5DLAqcNwxzN+86b5Gsu9xyGMV2eIF/sEYxakhY4mpHhjwdZBySue7IBA==", + "version": "1.0.71", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.71.tgz", + "integrity": "sha512-CfTXU8pa5dxRz22xQzoi3TiG1PJo9+WR8PRDiPSdkIBSyPJ1NvX87DJmfXjTgeAfR+wkjt/p0keDCaBBVhNmUA==", "cpu": [ "x64" ], @@ -626,9 +626,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.71-2", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.71-2.tgz", - "integrity": "sha512-lb81urkKJ+yWIy62yw9NmyjX5eFiw0y2TVIkVje26ot+gCiCJPisqyRWwhkfq0g/YKRbS8uoX4r+KHAhRH1wlg==", + "version": "1.0.71", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.71.tgz", + "integrity": "sha512-+HI1DokixXhHUahj06Fw67ZAigBuXKC58BFma4UJOGrQsDgwOSbqeTQHCw6vuymzjKlg3sactfsCUTaefkjscQ==", "cpu": [ "arm64" ], @@ -643,9 +643,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.71-2", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.71-2.tgz", - "integrity": "sha512-j3mQO2OUVoO+ZGE5KGF3iiekTikQZDQTnZO9RquWPXLwQs6ZdgRw9pPhbpXh0eoM1osZAW1/tmafcyYNpdBgWA==", + "version": "1.0.71", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.71.tgz", + "integrity": "sha512-02kXOBd9CwBbCaztuf71WYWn+uGapCuiaasomN4tcMH3HBVZ4gi3J0ZUoRcgcS80xh81uQyeBHbnUKzb/RE/9A==", "cpu": [ "x64" ], diff --git a/test/harness/package.json b/test/harness/package.json index bfc8879147..18b19e21ac 100644 --- a/test/harness/package.json +++ b/test/harness/package.json @@ -14,7 +14,7 @@ "node": "^20.19.0 || >=22.12.0" }, "devDependencies": { - "@github/copilot": "^1.0.71-2", + "@github/copilot": "^1.0.71", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "@types/node-forge": "^1.3.14",