From 849f07aa307d4d0178e2c9fee231cb2035406c39 Mon Sep 17 00:00:00 2001 From: Josh Spicer <23246594+joshspicer@users.noreply.github.com> Date: Wed, 29 Jul 2026 12:22:16 -0700 Subject: [PATCH 1/2] feat(sdk): add optional managedSettings.permissions to session create/resume Add an optional per-session `managedSettings` field (permissions-only contract) across all six language SDKs, alongside the existing `enableManagedSettings` boolean. Hosts can inject enterprise permission policy at session startup via: managedSettings.permissions = { disableBypassPermissionsMode?: "disable", deny?: string[], ask?: string[], allow?: string[], } Semantics: startup-only (not persisted), must be re-supplied on resume, composes restrictively with runtime-managed settings, and older runtimes fail closed. Wired through hand-written wire types at both create and resume in Node, Python, Go, .NET, Rust, and Java, plus tests, docs, and a CHANGELOG entry. Generated RPC mirror types regenerated from the runtime schema (TS/Python/Go/Rust; C# unaffected as it does not mirror SessionOpenOptions). No SDK protocol bump. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- CHANGELOG.md | 35 +++++++ dotnet/src/Client.cs | 4 + dotnet/src/Types.cs | 65 +++++++++++++ .../test/Unit/ClientSessionLifetimeTests.cs | 72 ++++++++++++++ go/client.go | 2 + go/client_test.go | 97 +++++++++++++++++++ go/rpc/zrpc.go | 34 +++++++ go/rpc/zrpc_encoding.go | 2 + go/types.go | 44 +++++++++ .../github/copilot/SessionRequestBuilder.java | 2 + .../copilot/rpc/CreateSessionRequest.java | 17 ++++ .../github/copilot/rpc/ManagedSettings.java | 33 +++++++ .../rpc/ManagedSettingsPermissions.java | 89 +++++++++++++++++ .../copilot/rpc/ResumeSessionConfig.java | 20 ++++ .../copilot/rpc/ResumeSessionRequest.java | 17 ++++ .../com/github/copilot/rpc/SessionConfig.java | 25 +++++ .../github/copilot/ManagedSettingsTest.java | 44 +++++++++ nodejs/src/client.ts | 2 + nodejs/src/generated/rpc.ts | 36 +++++++ nodejs/src/index.ts | 2 + nodejs/src/types.ts | 63 ++++++++++++ nodejs/test/client.test.ts | 79 +++++++++++++++ python/copilot/__init__.py | 4 + python/copilot/client.py | 81 ++++++++++++++++ python/copilot/generated/rpc.py | 91 ++++++++++++++++- python/test_client.py | 94 ++++++++++++++++-- rust/src/generated/api_types.rs | 60 ++++++++++++ rust/src/types.rs | 82 ++++++++++++++++ rust/src/wire.rs | 2 + rust/tests/session_test.rs | 91 ++++++++++++++++- 30 files changed, 1279 insertions(+), 10 deletions(-) create mode 100644 java/src/main/java/com/github/copilot/rpc/ManagedSettings.java create mode 100644 java/src/main/java/com/github/copilot/rpc/ManagedSettingsPermissions.java create mode 100644 java/src/test/java/com/github/copilot/ManagedSettingsTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 41bf05e171..8502e688b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,41 @@ All notable changes to the Copilot SDK are documented in this file. This changelog is automatically generated by an AI agent when stable releases are published. See [GitHub Releases](https://github.com/github/copilot-sdk/releases) for the full list. +## [Unreleased] + +### Feature: host-injected managed settings permissions + +Session create and resume accept a new optional `managedSettings` option that injects an enterprise permissions policy at session startup, alongside the existing `enableManagedSettings` self-fetch flag. The current contract is permissions-only: `disableBypassPermissionsMode` (the literal `"disable"`), plus `deny`, `ask`, and `allow` rule lists. The layer composes restrictively with any server- or device-level managed settings (deny/ask are unioned, every present allow list must admit a tool, and `disableBypassPermissionsMode` is deny-wins). + +This layer is startup-only and is not persisted with the session, so it must be re-supplied on resume to remain in effect; omitting it on resume clears the previously injected layer. It can be combined with `enableManagedSettings`. Older runtimes that do not recognize the field reject session creation (fail-closed) rather than silently ignoring it, so it requires a Copilot CLI runtime whose schema includes managed settings. + +```ts +const session = await client.createSession({ + managedSettings: { + permissions: { + disableBypassPermissionsMode: "disable", + deny: ["shell(rm*)"], + ask: ["write"], + }, + }, +}); +``` + +```cs +var session = await client.CreateSessionAsync(new SessionConfig +{ + ManagedSettings = new ManagedSettings + { + Permissions = new ManagedSettingsPermissions + { + DisableBypassPermissionsMode = "disable", + Deny = ["shell(rm*)"], + Ask = ["write"], + }, + }, +}); +``` + ## [v1.0.7](https://github.com/github/copilot-sdk/releases/tag/v1.0.7) (2026-07-16) ### Feature: in-process (FFI) transport diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs index 9b50d6942d..004ed6600d 100644 --- a/dotnet/src/Client.cs +++ b/dotnet/src/Client.cs @@ -1203,6 +1203,7 @@ public async Task CreateSessionAsync(SessionConfig config, Cance ExpAssignments: config.ExpAssignments, EnableManagedSettings: config.EnableManagedSettings, GitHubMcpToolConfig: config.GitHubMcpToolConfig, + ManagedSettings: config.ManagedSettings, EnableGitHubTelemetryForwarding: _options.OnGitHubTelemetry != null ? true : null); var rpcTimestamp = Stopwatch.GetTimestamp(); @@ -1420,6 +1421,7 @@ public async Task ResumeSessionAsync(string sessionId, ResumeSes ExpAssignments: config.ExpAssignments, EnableManagedSettings: config.EnableManagedSettings, GitHubMcpToolConfig: config.GitHubMcpToolConfig, + ManagedSettings: config.ManagedSettings, EnableGitHubTelemetryForwarding: _options.OnGitHubTelemetry != null ? true : null); var rpcTimestamp = Stopwatch.GetTimestamp(); @@ -2774,6 +2776,7 @@ internal record CreateSessionRequest( OptionsUpdateToolFilterPrecedence? ToolFilterPrecedence = null, [property: JsonPropertyName("expAssignments")] CopilotExpAssignmentResponse? ExpAssignments = null, [property: JsonPropertyName("enableManagedSettings")] bool? EnableManagedSettings = null, + [property: JsonPropertyName("managedSettings")] ManagedSettings? ManagedSettings = null, bool? EnableGitHubTelemetryForwarding = null, [property: JsonPropertyName("githubMcpToolConfig")] GitHubMcpToolConfig? GitHubMcpToolConfig = null); #pragma warning restore GHCP001 @@ -2883,6 +2886,7 @@ internal record ResumeSessionRequest( OptionsUpdateToolFilterPrecedence? ToolFilterPrecedence = null, [property: JsonPropertyName("expAssignments")] CopilotExpAssignmentResponse? ExpAssignments = null, [property: JsonPropertyName("enableManagedSettings")] bool? EnableManagedSettings = null, + [property: JsonPropertyName("managedSettings")] ManagedSettings? ManagedSettings = null, bool? EnableGitHubTelemetryForwarding = null, [property: JsonPropertyName("githubMcpToolConfig")] GitHubMcpToolConfig? GitHubMcpToolConfig = null); #pragma warning restore GHCP001 diff --git a/dotnet/src/Types.cs b/dotnet/src/Types.cs index e0b3d19145..b423088aa1 100644 --- a/dotnet/src/Types.cs +++ b/dotnet/src/Types.cs @@ -2990,6 +2990,59 @@ public sealed class GitHubMcpToolConfig public bool? DisableFormDeferral { get; set; } } +/// +/// Permission rules injected as a managed-settings layer at session bootstrap. +/// All fields are optional; omitted fields impose no constraint from this layer. +/// +/// +/// This layer composes restrictively with any server- or device-level managed +/// settings: and rules are unioned across +/// layers, every present list must admit a tool for it to be +/// allowed, and is honored if any +/// layer sets it (deny-wins). +/// +public sealed class ManagedSettingsPermissions +{ + /// + /// When set to "disable", bypass-permissions mode is turned off for the + /// session regardless of other layers. Serialized as + /// disableBypassPermissionsMode. + /// + [JsonPropertyName("disableBypassPermissionsMode")] + public string? DisableBypassPermissionsMode { get; set; } + + /// Tool-permission patterns that are always denied. + [JsonPropertyName("deny")] + public IList? Deny { get; set; } + + /// Tool-permission patterns that require an explicit ask. + [JsonPropertyName("ask")] + public IList? Ask { get; set; } + + /// Tool-permission patterns that are allowed without prompting. + [JsonPropertyName("allow")] + public IList? Allow { get; set; } +} + +/// +/// Managed-settings layer injected at session startup. Currently carries only a +/// object. +/// +/// +/// This layer is startup-only and is not persisted with the session. It must be +/// re-supplied on to remain in +/// effect; omitting it on resume clears the previously injected layer. It can be +/// combined with . Older +/// runtimes that do not recognize the managedSettings field reject session +/// creation (fail-closed). +/// +public sealed class ManagedSettings +{ + /// Permission rules for this managed-settings layer. + [JsonPropertyName("permissions")] + public ManagedSettingsPermissions? Permissions { get; set; } +} + /// /// Shared configuration properties for creating or resuming a Copilot session. /// Use when creating a new session, or @@ -3081,6 +3134,7 @@ protected SessionConfigBase(SessionConfigBase? other) RemoteSession = other.RemoteSession; ExpAssignments = other.ExpAssignments; EnableManagedSettings = other.EnableManagedSettings; + ManagedSettings = other.ManagedSettings; #pragma warning disable GHCP001 Canvases = other.Canvases is not null ? [.. other.Canvases] : null; RequestCanvasRenderer = other.RequestCanvasRenderer; @@ -3532,6 +3586,17 @@ protected SessionConfigBase(SessionConfigBase? other) /// public bool? EnableManagedSettings { get; set; } + /// + /// Optional managed-settings layer injected at session bootstrap. Currently + /// carries a permissions object that composes restrictively with any + /// server- or device-level managed settings. This layer is startup-only and + /// is not persisted: it must be re-supplied on resume to remain in effect, + /// and omitting it on resume clears the previously injected layer. Can be + /// combined with . Serialized on the wire + /// as managedSettings. + /// + public ManagedSettings? ManagedSettings { get; set; } + #pragma warning disable GHCP001 /// /// Canvas declarations advertised by this connection. The runtime forwards diff --git a/dotnet/test/Unit/ClientSessionLifetimeTests.cs b/dotnet/test/Unit/ClientSessionLifetimeTests.cs index e1143db17c..94dd1b68bd 100644 --- a/dotnet/test/Unit/ClientSessionLifetimeTests.cs +++ b/dotnet/test/Unit/ClientSessionLifetimeTests.cs @@ -450,6 +450,78 @@ private static int GetPrivateDictionaryCount(CopilotClient client, string fieldN return (int)count.GetValue(dictionary)!; } + [Fact] + public async Task CreateSessionAsync_Serializes_ManagedSettings_Permissions() + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + await client.StartAsync(); + + await using var session = await client.CreateSessionAsync(new SessionConfig + { + EnableManagedSettings = true, + ManagedSettings = new ManagedSettings + { + Permissions = new ManagedSettingsPermissions + { + DisableBypassPermissionsMode = "disable", + Deny = ["shell(rm*)"], + Ask = ["write"], + Allow = [] + } + }, + OnPermissionRequest = PermissionHandler.ApproveAll + }); + + var request = Assert.Single(server.Requests, request => request.Method == "session.create"); + Assert.True(request.Params.GetProperty("enableManagedSettings").GetBoolean()); + var permissions = request.Params.GetProperty("managedSettings").GetProperty("permissions"); + Assert.Equal("disable", permissions.GetProperty("disableBypassPermissionsMode").GetString()); + Assert.Equal("shell(rm*)", Assert.Single(permissions.GetProperty("deny").EnumerateArray()).GetString()); + Assert.Equal("write", Assert.Single(permissions.GetProperty("ask").EnumerateArray()).GetString()); + Assert.Empty(permissions.GetProperty("allow").EnumerateArray()); + } + + [Fact] + public async Task CreateSessionAsync_Omits_ManagedSettings_When_Unset() + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + await client.StartAsync(); + + await using var session = await client.CreateSessionAsync(new SessionConfig + { + OnPermissionRequest = PermissionHandler.ApproveAll + }); + + var request = Assert.Single(server.Requests, request => request.Method == "session.create"); + Assert.False(request.Params.TryGetProperty("managedSettings", out _)); + } + + [Fact] + public async Task ResumeSessionAsync_Serializes_ManagedSettings_Permissions() + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + + await using var session = await client.ResumeSessionAsync("session-managed", new ResumeSessionConfig + { + ManagedSettings = new ManagedSettings + { + Permissions = new ManagedSettingsPermissions + { + Deny = ["shell(rm*)"] + } + }, + OnPermissionRequest = PermissionHandler.ApproveAll, + OnEvent = _ => { } + }); + + var request = Assert.Single(server.Requests, request => request.Method == "session.resume"); + var permissions = request.Params.GetProperty("managedSettings").GetProperty("permissions"); + Assert.Equal("shell(rm*)", Assert.Single(permissions.GetProperty("deny").EnumerateArray()).GetString()); + } + private static void DispatchEvent(CopilotSession session, SessionEvent evt) { var method = typeof(CopilotSession).GetMethod("DispatchEvent", BindingFlags.Instance | BindingFlags.NonPublic) diff --git a/go/client.go b/go/client.go index 34f6d574a4..e484c62e75 100644 --- a/go/client.go +++ b/go/client.go @@ -829,6 +829,7 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses req.ExtensionInfo = config.ExtensionInfo req.ExpAssignments = config.ExpAssignments req.EnableManagedSettings = config.EnableManagedSettings + req.ManagedSettings = config.ManagedSettings if len(config.Commands) > 0 { cmds := make([]wireCommand, 0, len(config.Commands)) @@ -1205,6 +1206,7 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string, req.ExtensionInfo = config.ExtensionInfo req.ExpAssignments = config.ExpAssignments req.EnableManagedSettings = config.EnableManagedSettings + req.ManagedSettings = config.ManagedSettings if config.OnPermissionRequest != nil { req.RequestPermission = Bool(true) } diff --git a/go/client_test.go b/go/client_test.go index 2a6db6e861..a35923d1ab 100644 --- a/go/client_test.go +++ b/go/client_test.go @@ -3424,3 +3424,100 @@ func TestResumeSessionRequest_ExpAssignments(t *testing.T) { } }) } + +func TestSessionRequests_ManagedSettings(t *testing.T) { + settings := &ManagedSettings{ + Permissions: &ManagedSettingsPermissions{ + DisableBypassPermissionsMode: String("disable"), + Deny: []string{"Shell(git push)"}, + Ask: []string{"Domain(publish.example)"}, + Allow: []string{"Read(**)"}, + }, + } + + expectedPermissions := map[string]any{ + "disableBypassPermissionsMode": "disable", + "deny": []any{"Shell(git push)"}, + "ask": []any{"Domain(publish.example)"}, + "allow": []any{"Read(**)"}, + } + + t.Run("includes managedSettings on create when set", func(t *testing.T) { + req := createSessionRequest{EnableManagedSettings: Bool(true), ManagedSettings: settings} + data, err := json.Marshal(req) + 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 m["enableManagedSettings"] != true { + t.Errorf("Expected enableManagedSettings true, got %v", m["enableManagedSettings"]) + } + ms, ok := m["managedSettings"].(map[string]any) + if !ok { + t.Fatalf("Expected managedSettings object, got %v", m["managedSettings"]) + } + perms, ok := ms["permissions"].(map[string]any) + if !ok { + t.Fatalf("Expected permissions object, got %v", ms["permissions"]) + } + if !reflect.DeepEqual(perms, expectedPermissions) { + t.Errorf("permissions mismatch:\n got: %#v\nwant: %#v", perms, expectedPermissions) + } + }) + + t.Run("includes managedSettings on resume when set", func(t *testing.T) { + req := resumeSessionRequest{SessionID: "s1", ManagedSettings: settings} + data, err := json.Marshal(req) + 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["managedSettings"].(map[string]any); !ok { + t.Fatalf("Expected managedSettings object, got %v", m["managedSettings"]) + } + }) + + t.Run("omits managedSettings when nil", func(t *testing.T) { + req := createSessionRequest{} + data, _ := json.Marshal(req) + var m map[string]any + json.Unmarshal(data, &m) + if _, ok := m["managedSettings"]; ok { + t.Error("Expected managedSettings to be omitted when nil") + } + }) + + t.Run("omits empty permission arrays (omitempty idiom)", func(t *testing.T) { + // Go's `omitempty` drops both nil and empty slices; an empty rule list + // is semantically equivalent to no rules for that key. + req := createSessionRequest{ManagedSettings: &ManagedSettings{ + Permissions: &ManagedSettingsPermissions{ + DisableBypassPermissionsMode: String("disable"), + Deny: []string{}, + Ask: []string{}, + Allow: []string{}, + }, + }} + data, err := json.Marshal(req) + if err != nil { + t.Fatalf("Failed to marshal: %v", err) + } + var m map[string]any + json.Unmarshal(data, &m) + perms := m["managedSettings"].(map[string]any)["permissions"].(map[string]any) + if perms["disableBypassPermissionsMode"] != "disable" { + t.Errorf("Expected disableBypassPermissionsMode preserved, got %v", perms["disableBypassPermissionsMode"]) + } + for _, key := range []string{"deny", "ask", "allow"} { + if _, ok := perms[key]; ok { + t.Errorf("Expected %s to be omitted when empty, got %v", key, perms[key]) + } + } + }) +} diff --git a/go/rpc/zrpc.go b/go/rpc/zrpc.go index a34dccd2e9..6d1930f5d0 100644 --- a/go/rpc/zrpc.go +++ b/go/rpc/zrpc.go @@ -9391,6 +9391,29 @@ type SessionLoadDeferredRepoHooksResult struct { type SessionLspInitializeResult struct { } +// Enterprise permission policy expressed with the runtime's managed permission-rule syntax. +// Experimental: SessionManagedPermissions is part of an experimental API and may change or +// be removed. +type SessionManagedPermissions struct { + // Permission rules that allow matching operations unless another managed source, deny, or + // ask rule restricts them. + Allow []string `json:"allow,omitzero"` + // Permission rules that require explicit human approval. + Ask []string `json:"ask,omitzero"` + // Permission rules that block matching operations. Deny has highest precedence. + Deny []string `json:"deny,omitzero"` + // When set to `disable`, prevents bypass/allow-all permission modes. + DisableBypassPermissionsMode *DisableBypassPermissionsMode `json:"disableBypassPermissionsMode,omitempty"` +} + +// Managed settings an SDK host may inject at session startup. Only permissions are accepted +// in this initial contract. +// Experimental: SessionManagedSettings is part of an experimental API and may change or be +// removed. +type SessionManagedSettings struct { + Permissions *SessionManagedPermissions `json:"permissions,omitempty"` +} + // Standard MCP CallToolResult // Experimental: SessionMCPAppsCallToolResult is part of an experimental API and may change // or be removed. @@ -9653,6 +9676,9 @@ type SessionOpenOptions struct { LogInteractiveShells *bool `json:"logInteractiveShells,omitempty"` // Identifier sent to LSP-style integrations. LspClientName *string `json:"lspClientName,omitempty"` + // Permissions-only enterprise policy injected by the SDK host at session create or resume. + // Composes restrictively with self-fetched and device policy and is not persisted. + ManagedSettings *SessionManagedSettings `json:"managedSettings,omitempty"` // Maximum decoded byte size of a single inline model-facing binary tool result persisted in // session events (default 10 MB). MaxInlineBinaryBytes *int64 `json:"maxInlineBinaryBytes,omitempty"` @@ -13090,6 +13116,14 @@ const ( DebugCollectLogsSourceShellLog DebugCollectLogsSource = "shell-log" ) +// Experimental: DisableBypassPermissionsMode is part of an experimental API and may change +// or be removed. +type DisableBypassPermissionsMode string + +const ( + DisableBypassPermissionsModeDisable DisableBypassPermissionsMode = "disable" +) + // Effective extension loading and agent-management mode // Experimental: DiscoveredExtensionMode is part of an experimental API and may change or be // removed. diff --git a/go/rpc/zrpc_encoding.go b/go/rpc/zrpc_encoding.go index fdc76729b5..2075fc3a28 100644 --- a/go/rpc/zrpc_encoding.go +++ b/go/rpc/zrpc_encoding.go @@ -3731,6 +3731,7 @@ func (r *SessionOpenOptions) UnmarshalJSON(data []byte) error { IsExperimentalMode *bool `json:"isExperimentalMode,omitempty"` LogInteractiveShells *bool `json:"logInteractiveShells,omitempty"` LspClientName *string `json:"lspClientName,omitempty"` + ManagedSettings *SessionManagedSettings `json:"managedSettings,omitempty"` MaxInlineBinaryBytes *int64 `json:"maxInlineBinaryBytes,omitempty"` Memory *MemoryConfiguration `json:"memory,omitempty"` Model *string `json:"model,omitempty"` @@ -3807,6 +3808,7 @@ func (r *SessionOpenOptions) UnmarshalJSON(data []byte) error { r.IsExperimentalMode = raw.IsExperimentalMode r.LogInteractiveShells = raw.LogInteractiveShells r.LspClientName = raw.LspClientName + r.ManagedSettings = raw.ManagedSettings r.MaxInlineBinaryBytes = raw.MaxInlineBinaryBytes r.Memory = raw.Memory r.Model = raw.Model diff --git a/go/types.go b/go/types.go index 9b3b53455f..79e4030df0 100644 --- a/go/types.go +++ b/go/types.go @@ -1450,6 +1450,43 @@ type SessionConfig struct { // be set; if omitted, the runtime is expected to reject session creation // (fail-closed). Unset behaves exactly as before. EnableManagedSettings *bool + // ManagedSettings supplies host-injected enterprise managed settings for + // the session. Unlike EnableManagedSettings (which asks the runtime to + // self-fetch account/org and device policy), this provides the managed + // policy directly. The runtime validates it with the same + // managed-permission parser it uses for fetched policy and composes it + // restrictively with any self-fetched (server) and device-managed (MDM) + // layers. It is startup-only and not persisted: re-supply it on resume, + // where it replaces the prior injected layer (omitting it clears the + // layer). It may be combined with EnableManagedSettings. Requires a runtime + // whose RPC schema includes managedSettings. + ManagedSettings *ManagedSettings +} + +// ManagedSettings is host-injected enterprise managed settings for a session. +// The first supported contract is permissions-only; unknown sibling keys are +// rejected by the runtime. Serialized on the wire as managedSettings. +type ManagedSettings struct { + // Permissions is the managed permission policy for the session. + Permissions *ManagedSettingsPermissions `json:"permissions,omitempty"` +} + +// ManagedSettingsPermissions is the permissions-only managed policy injected +// via ManagedSettings. Rule strings use the same vocabulary the runtime +// accepts for fetched managed policy (e.g. "Read(**)", "Shell(git push *)"); +// malformed rules are rejected by the runtime at session creation. +type ManagedSettingsPermissions struct { + // DisableBypassPermissionsMode, when set to "disable", turns off + // bypass-permissions ("yolo") mode for the session. Deny-wins: no other + // layer can re-enable it. + DisableBypassPermissionsMode *string `json:"disableBypassPermissionsMode,omitempty"` + // Deny lists operations that must always be denied. Unioned across layers. + Deny []string `json:"deny,omitempty"` + // Ask lists operations that must prompt for approval. Unioned across layers. + Ask []string `json:"ask,omitempty"` + // Allow lists operations permitted without prompting. Every declared allow + // list across managed layers must admit an operation for it to be allowed. + Allow []string `json:"allow,omitempty"` } // ToolDefer controls whether a tool may be deferred (loaded lazily via tool @@ -1901,6 +1938,11 @@ type ResumeSessionConfig struct { // SessionConfig.EnableManagedSettings. Re-supply on resume so the runtime // re-applies the managed-settings self-fetch after a CLI process restart. EnableManagedSettings *bool + // ManagedSettings re-injects host-provided managed settings on resume. See + // SessionConfig.ManagedSettings. It must be re-supplied on resume: it + // replaces the prior injected layer, and omitting it clears that layer so + // warm and cold resume behave identically. + ManagedSettings *ManagedSettings } // ProviderTokenArgs carries the context passed to a [BearerTokenProvider] callback @@ -2361,6 +2403,7 @@ type createSessionRequest struct { CanvasProvider *CanvasProviderIdentity `json:"canvasProvider,omitempty"` ExpAssignments *CopilotExpAssignmentResponse `json:"expAssignments,omitempty"` EnableManagedSettings *bool `json:"enableManagedSettings,omitempty"` + ManagedSettings *ManagedSettings `json:"managedSettings,omitempty"` Traceparent string `json:"traceparent,omitempty"` Tracestate string `json:"tracestate,omitempty"` } @@ -2455,6 +2498,7 @@ type resumeSessionRequest struct { CanvasProvider *CanvasProviderIdentity `json:"canvasProvider,omitempty"` ExpAssignments *CopilotExpAssignmentResponse `json:"expAssignments,omitempty"` EnableManagedSettings *bool `json:"enableManagedSettings,omitempty"` + ManagedSettings *ManagedSettings `json:"managedSettings,omitempty"` Traceparent string `json:"traceparent,omitempty"` Tracestate string `json:"tracestate,omitempty"` } diff --git a/java/src/main/java/com/github/copilot/SessionRequestBuilder.java b/java/src/main/java/com/github/copilot/SessionRequestBuilder.java index 593e881c1d..c240184f22 100644 --- a/java/src/main/java/com/github/copilot/SessionRequestBuilder.java +++ b/java/src/main/java/com/github/copilot/SessionRequestBuilder.java @@ -199,6 +199,7 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess request.setCloud(config.getCloud()); request.setExpAssignments(config.getExpAssignments()); config.getEnableManagedSettings().ifPresent(request::setEnableManagedSettings); + request.setManagedSettings(config.getManagedSettings()); return request; } @@ -333,6 +334,7 @@ static ResumeSessionRequest buildResumeRequest(String sessionId, ResumeSessionCo request.setRemoteSession(config.getRemoteSession()); request.setExpAssignments(config.getExpAssignments()); config.getEnableManagedSettings().ifPresent(request::setEnableManagedSettings); + request.setManagedSettings(config.getManagedSettings()); return request; } diff --git a/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java b/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java index 682d6fb7cf..a5b3882e42 100644 --- a/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java +++ b/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java @@ -228,6 +228,10 @@ public final class CreateSessionRequest { @JsonInclude(JsonInclude.Include.NON_NULL) private Boolean enableManagedSettings; + @JsonProperty("managedSettings") + @JsonInclude(JsonInclude.Include.NON_NULL) + private ManagedSettings managedSettings; + /** Gets the model name. @return the model */ public String getModel() { return model; @@ -1062,4 +1066,17 @@ public void setEnableManagedSettings(boolean enableManagedSettings) { public void clearEnableManagedSettings() { this.enableManagedSettings = null; } + + /** @return host-injected managed settings, or {@code null} when unset */ + public ManagedSettings getManagedSettings() { + return managedSettings; + } + + /** + * @param managedSettings + * host-injected managed settings + */ + public void setManagedSettings(ManagedSettings managedSettings) { + this.managedSettings = managedSettings; + } } diff --git a/java/src/main/java/com/github/copilot/rpc/ManagedSettings.java b/java/src/main/java/com/github/copilot/rpc/ManagedSettings.java new file mode 100644 index 0000000000..f122fb2799 --- /dev/null +++ b/java/src/main/java/com/github/copilot/rpc/ManagedSettings.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ +package com.github.copilot.rpc; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Managed settings an SDK host may inject at session create or resume. + * + *

+ * The initial public contract is permissions-only. + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public final class ManagedSettings { + @JsonProperty("permissions") + private ManagedSettingsPermissions permissions; + + /** @return the managed permission policy, or {@code null} when unset */ + public ManagedSettingsPermissions getPermissions() { + return permissions; + } + + /** + * @param permissions + * managed permission policy; @return this settings object + */ + public ManagedSettings setPermissions(ManagedSettingsPermissions permissions) { + this.permissions = permissions; + return this; + } +} diff --git a/java/src/main/java/com/github/copilot/rpc/ManagedSettingsPermissions.java b/java/src/main/java/com/github/copilot/rpc/ManagedSettingsPermissions.java new file mode 100644 index 0000000000..57b9319d0a --- /dev/null +++ b/java/src/main/java/com/github/copilot/rpc/ManagedSettingsPermissions.java @@ -0,0 +1,89 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ +package com.github.copilot.rpc; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.ArrayList; +import java.util.List; + +/** + * Enterprise permission policy injected by an SDK host at session startup. + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public final class ManagedSettingsPermissions { + @JsonProperty("disableBypassPermissionsMode") + private String disableBypassPermissionsMode; + + @JsonProperty("deny") + private List deny; + + @JsonProperty("ask") + private List ask; + + @JsonProperty("allow") + private List allow; + + /** @return the bypass-permissions policy, or {@code null} when unset */ + public String getDisableBypassPermissionsMode() { + return disableBypassPermissionsMode; + } + + /** + * Disables bypass/allow-all permission modes. + * + * @param value + * must be {@code "disable"} + * @return this policy + */ + public ManagedSettingsPermissions setDisableBypassPermissionsMode(String value) { + if (!"disable".equals(value)) { + throw new IllegalArgumentException("disableBypassPermissionsMode must be \"disable\""); + } + this.disableBypassPermissionsMode = value; + return this; + } + + /** @return rules that deny matching operations, or {@code null} when unset */ + public List getDeny() { + return deny; + } + + /** + * @param rules + * deny rules; @return this policy + */ + public ManagedSettingsPermissions setDeny(List rules) { + this.deny = rules == null ? null : new ArrayList<>(rules); + return this; + } + + /** @return rules that require approval, or {@code null} when unset */ + public List getAsk() { + return ask; + } + + /** + * @param rules + * ask rules; @return this policy + */ + public ManagedSettingsPermissions setAsk(List rules) { + this.ask = rules == null ? null : new ArrayList<>(rules); + return this; + } + + /** @return rules that allow matching operations, or {@code null} when unset */ + public List getAllow() { + return allow; + } + + /** + * @param rules + * allow rules; @return this policy + */ + public ManagedSettingsPermissions setAllow(List rules) { + this.allow = rules == null ? null : new ArrayList<>(rules); + return this; + } +} diff --git a/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java b/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java index 373f5e8120..110c02db2e 100644 --- a/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java +++ b/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java @@ -105,6 +105,7 @@ public class ResumeSessionConfig { private String remoteSession; private CopilotExpAssignmentResponse expAssignments; private Boolean enableManagedSettings; + private ManagedSettings managedSettings; /** * Gets the AI model to use. @@ -1863,6 +1864,24 @@ public ResumeSessionConfig setEnableManagedSettings(boolean enableManagedSetting return this; } + /** @return host-injected managed settings, or {@code null} when unset */ + public ManagedSettings getManagedSettings() { + return managedSettings; + } + + /** + * Supplies permissions-only managed settings for this resume. The value + * replaces the prior injected layer and is not persisted. + * + * @param managedSettings + * the host-injected managed settings + * @return this config for method chaining + */ + public ResumeSessionConfig setManagedSettings(ManagedSettings managedSettings) { + this.managedSettings = managedSettings; + return this; + } + /** * Creates a shallow clone of this {@code ResumeSessionConfig} instance. *

@@ -1941,6 +1960,7 @@ public ResumeSessionConfig clone() { copy.remoteSession = this.remoteSession; copy.expAssignments = this.expAssignments; copy.enableManagedSettings = this.enableManagedSettings; + copy.managedSettings = this.managedSettings; return copy; } } diff --git a/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java b/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java index c3559d7b2d..3824d75484 100644 --- a/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java +++ b/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java @@ -230,6 +230,10 @@ public final class ResumeSessionRequest { @JsonInclude(JsonInclude.Include.NON_NULL) private Boolean enableManagedSettings; + @JsonProperty("managedSettings") + @JsonInclude(JsonInclude.Include.NON_NULL) + private ManagedSettings managedSettings; + /** Gets the session ID. @return the session ID */ public String getSessionId() { return sessionId; @@ -1077,4 +1081,17 @@ public void setEnableManagedSettings(boolean enableManagedSettings) { public void clearEnableManagedSettings() { this.enableManagedSettings = null; } + + /** @return host-injected managed settings, or {@code null} when unset */ + public ManagedSettings getManagedSettings() { + return managedSettings; + } + + /** + * @param managedSettings + * host-injected managed settings + */ + public void setManagedSettings(ManagedSettings managedSettings) { + this.managedSettings = managedSettings; + } } diff --git a/java/src/main/java/com/github/copilot/rpc/SessionConfig.java b/java/src/main/java/com/github/copilot/rpc/SessionConfig.java index 827a00df69..10ab9f8741 100644 --- a/java/src/main/java/com/github/copilot/rpc/SessionConfig.java +++ b/java/src/main/java/com/github/copilot/rpc/SessionConfig.java @@ -106,6 +106,7 @@ public class SessionConfig { private CloudSessionOptions cloud; private CopilotExpAssignmentResponse expAssignments; private Boolean enableManagedSettings; + private ManagedSettings managedSettings; /** * Gets the custom session ID. @@ -1994,6 +1995,29 @@ public SessionConfig setEnableManagedSettings(boolean enableManagedSettings) { return this; } + /** + * Gets host-injected managed settings for this session. + * + * @return the managed settings, or {@code null} when unset + */ + public ManagedSettings getManagedSettings() { + return managedSettings; + } + + /** + * Supplies permissions-only managed settings at session startup. The runtime + * validates and composes this policy restrictively with self-fetched and device + * policy. Re-supply it on resume because it is not persisted. + * + * @param managedSettings + * the host-injected managed settings + * @return this config instance for method chaining + */ + public SessionConfig setManagedSettings(ManagedSettings managedSettings) { + this.managedSettings = managedSettings; + return this; + } + /** * Creates a shallow clone of this {@code SessionConfig} instance. *

@@ -2077,6 +2101,7 @@ public SessionConfig clone() { copy.cloud = this.cloud; copy.expAssignments = this.expAssignments; copy.enableManagedSettings = this.enableManagedSettings; + copy.managedSettings = this.managedSettings; return copy; } } diff --git a/java/src/test/java/com/github/copilot/ManagedSettingsTest.java b/java/src/test/java/com/github/copilot/ManagedSettingsTest.java new file mode 100644 index 0000000000..1e991e0008 --- /dev/null +++ b/java/src/test/java/com/github/copilot/ManagedSettingsTest.java @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ +package com.github.copilot; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.copilot.rpc.ManagedSettings; +import com.github.copilot.rpc.ManagedSettingsPermissions; +import com.github.copilot.rpc.ResumeSessionConfig; +import com.github.copilot.rpc.SessionConfig; +import java.util.List; +import org.junit.jupiter.api.Test; + +class ManagedSettingsTest { + @Test + void forwardsManagedSettingsOnCreateAndResume() throws Exception { + var permissions = new ManagedSettingsPermissions().setDisableBypassPermissionsMode("disable") + .setDeny(List.of("Shell(rm *)")).setAsk(List.of("Domain(publish.example)")) + .setAllow(List.of("Read(**)")); + var managedSettings = new ManagedSettings().setPermissions(permissions); + + var create = SessionRequestBuilder.buildCreateRequest( + new SessionConfig().setEnableManagedSettings(true).setManagedSettings(managedSettings), + "managed-create"); + var resume = SessionRequestBuilder.buildResumeRequest("managed-resume", + new ResumeSessionConfig().setEnableManagedSettings(true).setManagedSettings(managedSettings)); + + assertEquals(managedSettings, create.getManagedSettings()); + assertEquals(managedSettings, resume.getManagedSettings()); + var json = new ObjectMapper().writeValueAsString(create); + assertEquals(true, json.contains("\"enableManagedSettings\":true")); + assertEquals(true, json.contains("\"managedSettings\":{\"permissions\"")); + assertEquals(true, json.contains("\"disableBypassPermissionsMode\":\"disable\"")); + } + + @Test + void rejectsUnsupportedBypassValue() { + var permissions = new ManagedSettingsPermissions(); + assertThrows(IllegalArgumentException.class, () -> permissions.setDisableBypassPermissionsMode("enable")); + } +} diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index c0cb6ed17e..e4c222de99 100644 --- a/nodejs/src/client.ts +++ b/nodejs/src/client.ts @@ -1605,6 +1605,7 @@ export class CopilotClient { cloud: config.cloud, expAssignments: config.expAssignments, enableManagedSettings: config.enableManagedSettings, + managedSettings: config.managedSettings, }); const { @@ -1849,6 +1850,7 @@ export class CopilotClient { openCanvases: config.openCanvases, expAssignments: config.expAssignments, enableManagedSettings: config.enableManagedSettings, + managedSettings: config.managedSettings, }); const { workspacePath, capabilities, openCanvases } = response as { diff --git a/nodejs/src/generated/rpc.ts b/nodejs/src/generated/rpc.ts index b45294a5d7..feed96faf8 100644 --- a/nodejs/src/generated/rpc.ts +++ b/nodejs/src/generated/rpc.ts @@ -418,6 +418,9 @@ export type DebugCollectLogsResultKind = | "archive" /** A directory containing redacted files was written. */ | "directory"; + +/** @experimental */ +export type DisableBypassPermissionsMode = "disable"; /** * Persisted extension discovery source * @@ -14443,6 +14446,38 @@ export interface SessionLoadDeferredRepoHooksResult { */ hookCount: number; } +/** + * Enterprise permission policy expressed with the runtime's managed permission-rule syntax. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SessionManagedPermissions". + */ +/** @experimental */ +export interface SessionManagedPermissions { + disableBypassPermissionsMode?: DisableBypassPermissionsMode; + /** + * Permission rules that block matching operations. Deny has highest precedence. + */ + deny?: string[]; + /** + * Permission rules that require explicit human approval. + */ + ask?: string[]; + /** + * Permission rules that allow matching operations unless another managed source, deny, or ask rule restricts them. + */ + allow?: string[]; +} +/** + * Managed settings an SDK host may inject at session startup. Only permissions are accepted in this initial contract. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SessionManagedSettings". + */ +/** @experimental */ +export interface SessionManagedSettings { + permissions?: SessionManagedPermissions; +} /** * Point-in-time snapshot of slow-changing session identifier and state fields * @@ -14594,6 +14629,7 @@ export interface SessionOpenOptions { * Opt-in: self-fetch and enforce enterprise managed settings at session bootstrap. */ enableManagedSettings?: boolean; + managedSettings?: SessionManagedSettings; /** * Opt in to capturing file changes for session rewind and session diff. Capture cannot reconstruct changes made before it was enabled. On create it starts capture from the first turn. It is also honored on resume: for a session that already has tracked prior turns, tracking continues automatically even if this is omitted; passing it on resume additionally enables tracking for an eligible session that has no prior root turn yet. Resuming a session whose prior root turns were never tracked has no restorable baseline, so tracking stays disabled for it and rewind reports file change tracking as unavailable; the resume itself still succeeds, so sessions that predate tracking remain loadable. The opt-in is only rejected when the session can never track (a subagent session, or one without local session storage). It is intentionally absent from the mutable options update because enabling it after edits have occurred would create an incomplete, misleading baseline. Subagents share the parent session's capture store and are not tracked as separate rewind points: a file a subagent writes is attributed to whichever root user turn was open when the capture was staged, just before the tool body ran. A turn cannot open while a staged capture is still in flight, so a subagent tool that staged under the spawning turn stays attributed to it however late the write lands, while a capture it stages after the user's next message belongs to that later turn. Attribution decides which turn's rewind point counts and file preview include that write; it does not narrow which rewinds revert it, because a rewind restores every capture from the selected turn onward, so the earlier spawning turn reverts it as well. */ diff --git a/nodejs/src/index.ts b/nodejs/src/index.ts index 8d79a71a11..8b8fdd5054 100644 --- a/nodejs/src/index.ts +++ b/nodejs/src/index.ts @@ -104,6 +104,8 @@ export type { DefaultAgentConfig, BearerTokenProvider, MessageOptions, + ManagedSettings, + ManagedSettingsPermissions, ModelBilling, ModelBillingTokenPrices, ModelBillingTokenPricesLongContext, diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index b677808eca..3b5ce6d0e6 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -2044,6 +2044,45 @@ export interface GitHubMcpToolConfig { disableFormDeferral?: boolean; } +/** + * Permissions-only managed policy injected by the host via + * {@link SessionConfigBase.managedSettings}. + * + * Rule strings use the same vocabulary the runtime accepts for fetched managed + * policy (e.g. `"Read(**)"`, `"Shell(git push *)"`); malformed rules are + * rejected at session creation. + */ +export interface ManagedSettingsPermissions { + /** + * When set to `"disable"`, bypass-permissions ("yolo") mode is turned off + * for the session. This is deny-wins: it cannot be re-enabled by any other + * layer. + */ + disableBypassPermissionsMode?: "disable"; + /** Operations that must always be denied. Unioned across managed layers. */ + deny?: string[]; + /** + * Operations that must prompt for approval. Unioned across managed layers. + */ + ask?: string[]; + /** + * Operations permitted without prompting. Every declared `allow` list + * (across managed layers) must admit an operation for it to be allowed. + */ + allow?: string[]; +} + +/** + * Host-injected enterprise managed settings. The first supported contract is + * permissions-only; unknown sibling keys are rejected by the runtime. + * + * @see {@link SessionConfigBase.managedSettings} + */ +export interface ManagedSettings { + /** Managed permission policy for the session. */ + permissions?: ManagedSettingsPermissions; +} + /** * Shared configuration fields used by both {@link SessionConfig} (for * creating a new session) and {@link ResumeSessionConfig} (for resuming @@ -2528,6 +2567,30 @@ export interface SessionConfigBase { */ enableManagedSettings?: boolean; + /** + * Host-injected enterprise managed settings for this session. + * + * Unlike {@link SessionConfigBase.enableManagedSettings} — which asks the + * runtime to *self-fetch* account/org and device policy — this field lets + * the host supply the managed policy directly. The runtime validates it + * with the same managed-permission parser it uses for fetched policy and + * composes it restrictively with any self-fetched (server) and + * device-managed (MDM) layers: `deny`/`ask` rules are unioned, every + * declared `allow` list must admit an operation, and + * `disableBypassPermissionsMode: "disable"` is deny-wins. + * + * This is startup-only. It is **not** persisted: it must be re-supplied on + * {@link CopilotClient.resumeSession | resume}, where it replaces the prior + * injected layer (omitting it clears the layer, so warm and cold resume + * behave identically). It may be combined with `enableManagedSettings`; + * when both are supplied the injected, server, and device restrictions all + * apply. + * + * Requires a Copilot runtime whose RPC schema includes `managedSettings`; + * older runtimes reject the unknown field rather than silently ignoring it. + */ + managedSettings?: ManagedSettings; + /** * When true, skips embedding-based retrieval for this session. * Use in multitenant deployments to prevent cross-session information leakage diff --git a/nodejs/test/client.test.ts b/nodejs/test/client.test.ts index 78fbe0426c..c9d0fbb374 100644 --- a/nodejs/test/client.test.ts +++ b/nodejs/test/client.test.ts @@ -3556,3 +3556,82 @@ describe("CopilotClient", () => { }); }); }); + +describe("managedSettings serialization", () => { + async function captureCreateParams(config: Record): Promise { + const client = new CopilotClient(); + await client.start(); + onTestFinished(() => stopClient(client)); + 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, ...config }); + const call = spy.mock.calls.find(([method]) => method === "session.create"); + return call![1]; + } + + it("forwards the full permissions object on session.create", async () => { + const params = await captureCreateParams({ + managedSettings: { + permissions: { + disableBypassPermissionsMode: "disable", + deny: ["Shell(git push)"], + ask: ["Domain(publish.example)"], + allow: ["Read(**)"], + }, + }, + }); + expect(params.managedSettings).toEqual({ + permissions: { + disableBypassPermissionsMode: "disable", + deny: ["Shell(git push)"], + ask: ["Domain(publish.example)"], + allow: ["Read(**)"], + }, + }); + }); + + it("omits managedSettings when not supplied", async () => { + const params = await captureCreateParams({}); + expect(params.managedSettings).toBeUndefined(); + }); + + it("coexists with enableManagedSettings", async () => { + const params = await captureCreateParams({ + enableManagedSettings: true, + managedSettings: { permissions: { deny: ["Edit(/secrets/**)"] } }, + }); + expect(params.enableManagedSettings).toBe(true); + expect(params.managedSettings).toEqual({ permissions: { deny: ["Edit(/secrets/**)"] } }); + }); + + it("preserves empty arrays in the permissions object", async () => { + const params = await captureCreateParams({ + managedSettings: { permissions: { deny: [], ask: [], allow: [] } }, + }); + expect(params.managedSettings).toEqual({ permissions: { deny: [], ask: [], allow: [] } }); + }); + + it("forwards managedSettings on session.resume", async () => { + const client = new CopilotClient(); + await client.start(); + onTestFinished(() => stopClient(client)); + const spy = vi + .spyOn((client as any).connection!, "sendRequest") + .mockImplementation(async (method: string, params: any) => { + if (method === "session.resume") return { sessionId: params.sessionId }; + throw new Error(`Unexpected method: ${method}`); + }); + await client.resumeSession("session-1", { + onPermissionRequest: approveAll, + managedSettings: { permissions: { ask: ["Domain(publish.example)"] } }, + }); + const call = spy.mock.calls.find(([method]) => method === "session.resume"); + expect(call![1].managedSettings).toEqual({ + permissions: { ask: ["Domain(publish.example)"] }, + }); + }); +}); diff --git a/python/copilot/__init__.py b/python/copilot/__init__.py index 8b0100df24..eac97b69df 100644 --- a/python/copilot/__init__.py +++ b/python/copilot/__init__.py @@ -41,6 +41,8 @@ GetStatusResponse, InProcessRuntimeConnection, LogLevel, + ManagedSettings, + ManagedSettingsPermissions, ModelBilling, ModelCapabilities, ModelInfo, @@ -269,6 +271,8 @@ "McpAuthStaticClientConfig", "McpAuthToken", "McpAuthWwwAuthenticateParams", + "ManagedSettings", + "ManagedSettingsPermissions", "ModelBilling", "ModelBillingTokenPrices", "ModelBillingTokenPricesLongContext", diff --git a/python/copilot/client.py b/python/copilot/client.py index 8f7c2daf0a..1fbd5045eb 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -247,6 +247,63 @@ def _capi_session_options_to_wire(options: CapiSessionOptions) -> dict[str, Any] return wire +@dataclass +class ManagedSettingsPermissions: + """Permissions-only managed policy injected via :class:`ManagedSettings`. + + Rule strings use the same vocabulary the runtime accepts for fetched + managed policy (e.g. ``"Read(**)"``, ``"Shell(git push *)"``); malformed + rules are rejected by the runtime at session creation. + """ + + disable_bypass_permissions_mode: Literal["disable"] | None = None + """When ``"disable"``, turns off bypass-permissions ("yolo") mode for the + session. Deny-wins: no other layer can re-enable it. Sent on the wire as + ``disableBypassPermissionsMode``.""" + deny: list[str] | None = None + """Operations that must always be denied. Unioned across managed layers.""" + ask: list[str] | None = None + """Operations that must prompt for approval. Unioned across managed layers.""" + allow: list[str] | None = None + """Operations permitted without prompting. Every declared ``allow`` list + across managed layers must admit an operation for it to be allowed.""" + + +@dataclass +class ManagedSettings: + """Host-injected enterprise managed settings for a session. + + Unlike ``enable_managed_settings`` — which asks the runtime to *self-fetch* + account/org and device policy — this supplies the managed policy directly. + The runtime validates it with the same managed-permission parser it uses + for fetched policy and composes it restrictively with any self-fetched + (server) and device-managed (MDM) layers. + + The first supported contract is permissions-only; unknown sibling keys are + rejected by the runtime. Serialized on the wire as ``managedSettings``. + """ + + permissions: ManagedSettingsPermissions | None = None + """Managed permission policy for the session.""" + + +def _managed_settings_to_dict(settings: ManagedSettings) -> dict[str, Any]: + wire: dict[str, Any] = {} + permissions = settings.permissions + if permissions is not None: + perms: dict[str, Any] = {} + if permissions.disable_bypass_permissions_mode is not None: + perms["disableBypassPermissionsMode"] = permissions.disable_bypass_permissions_mode + if permissions.deny is not None: + perms["deny"] = list(permissions.deny) + if permissions.ask is not None: + perms["ask"] = list(permissions.ask) + if permissions.allow is not None: + perms["allow"] = list(permissions.allow) + wire["permissions"] = perms + return wire + + # Implicit provider name for the singular, whole-session ``provider`` config. # Named providers are keyed by their own ``name``. _DEFAULT_BEARER_TOKEN_PROVIDER_NAME = "default" @@ -2088,6 +2145,7 @@ async def create_session( exp_assignments: CopilotExpAssignmentResponse | None = None, enable_managed_settings: bool | None = None, github_mcp_tool_config: GitHubMcpToolConfig | None = None, + managed_settings: ManagedSettings | None = None, ) -> CopilotSession: """ Create a new conversation session with the Copilot CLI. @@ -2235,6 +2293,15 @@ async def create_session( expected to reject session creation (fail-closed). When unset, behaves exactly as before. Sent on the wire as ``enableManagedSettings``. + managed_settings: Host-injected enterprise managed settings for the + session. Supplies managed policy directly instead of + self-fetching; the runtime validates it and composes it + restrictively with any self-fetched (server) and device-managed + layers. Startup-only and not persisted: re-supply on + :meth:`resume_session` (omitting it clears the injected layer). + May be combined with ``enable_managed_settings``. Requires a + runtime whose RPC schema includes ``managedSettings``. Sent on + the wire as ``managedSettings``. Returns: A :class:`CopilotSession` instance for the new session. @@ -2381,6 +2448,10 @@ async def create_session( if enable_managed_settings is not None: payload["enableManagedSettings"] = enable_managed_settings + # Host-injected managed settings (permissions-only contract) + if managed_settings is not None: + payload["managedSettings"] = _managed_settings_to_dict(managed_settings) + # Add working directory if provided if working_directory: payload["workingDirectory"] = working_directory @@ -2785,6 +2856,7 @@ async def resume_session( exp_assignments: CopilotExpAssignmentResponse | None = None, enable_managed_settings: bool | None = None, github_mcp_tool_config: GitHubMcpToolConfig | None = None, + managed_settings: ManagedSettings | None = None, ) -> CopilotSession: """ Resume an existing conversation session by its ID. @@ -2933,6 +3005,11 @@ async def resume_session( expected to reject session creation (fail-closed). When unset, behaves exactly as before. Sent on the wire as ``enableManagedSettings``. + managed_settings: Host-injected enterprise managed settings for the + session. Must be re-supplied on resume; it replaces the prior + injected layer, and omitting it clears that layer so warm and + cold resume behave identically. See :meth:`create_session`. Sent + on the wire as ``managedSettings``. Returns: A :class:`CopilotSession` instance for the resumed session. @@ -3102,6 +3179,10 @@ async def resume_session( if enable_managed_settings is not None: payload["enableManagedSettings"] = enable_managed_settings + # Host-injected managed settings (permissions-only contract) + if managed_settings is not None: + payload["managedSettings"] = _managed_settings_to_dict(managed_settings) + if working_directory: payload["workingDirectory"] = working_directory if config_directory: diff --git a/python/copilot/generated/rpc.py b/python/copilot/generated/rpc.py index fb9d586758..08e7828eb7 100644 --- a/python/copilot/generated/rpc.py +++ b/python/copilot/generated/rpc.py @@ -1809,6 +1809,12 @@ def to_dict(self) -> dict: result["path"] = from_union([from_str, from_none], self.path) return result +# Experimental: this type is part of an experimental API and may change or be removed. +class DisableBypassPermissionsMode(Enum): + """When set to `disable`, prevents bypass/allow-all permission modes.""" + + DISABLE = "disable" + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class DiscoveredExtensionPlugin: @@ -13147,6 +13153,45 @@ def to_dict(self) -> dict: result["outputDirectory"] = from_union([from_str, from_none], self.output_directory) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class SessionManagedPermissions: + """Enterprise permission policy expressed with the runtime's managed permission-rule syntax.""" + + allow: list[str] | None = None + """Permission rules that allow matching operations unless another managed source, deny, or + ask rule restricts them. + """ + ask: list[str] | None = None + """Permission rules that require explicit human approval.""" + + deny: list[str] | None = None + """Permission rules that block matching operations. Deny has highest precedence.""" + + disable_bypass_permissions_mode: DisableBypassPermissionsMode | None = None + """When set to `disable`, prevents bypass/allow-all permission modes.""" + + @staticmethod + def from_dict(obj: Any) -> 'SessionManagedPermissions': + assert isinstance(obj, dict) + allow = from_union([lambda x: from_list(from_str, x), from_none], obj.get("allow")) + ask = from_union([lambda x: from_list(from_str, x), from_none], obj.get("ask")) + deny = from_union([lambda x: from_list(from_str, x), from_none], obj.get("deny")) + disable_bypass_permissions_mode = from_union([DisableBypassPermissionsMode, from_none], obj.get("disableBypassPermissionsMode")) + return SessionManagedPermissions(allow, ask, deny, disable_bypass_permissions_mode) + + def to_dict(self) -> dict: + result: dict = {} + if self.allow is not None: + result["allow"] = from_union([lambda x: from_list(from_str, x), from_none], self.allow) + if self.ask is not None: + result["ask"] = from_union([lambda x: from_list(from_str, x), from_none], self.ask) + if self.deny is not None: + result["deny"] = from_union([lambda x: from_list(from_str, x), from_none], self.deny) + if self.disable_bypass_permissions_mode is not None: + result["disableBypassPermissionsMode"] = from_union([lambda x: to_enum(DisableBypassPermissionsMode, x), from_none], self.disable_bypass_permissions_mode) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class DiscoveredExtension: @@ -21385,6 +21430,29 @@ def to_dict(self) -> dict: result["skippedEntries"] = from_union([lambda x: from_list(lambda x: to_class(DebugCollectLogsSkippedEntry, x), x), from_none], self.skipped_entries) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class SessionManagedSettings: + """Managed settings an SDK host may inject at session startup. Only permissions are accepted + in this initial contract. + + Permissions-only enterprise policy injected by the SDK host at session create or resume. + Composes restrictively with self-fetched and device policy and is not persisted. + """ + permissions: SessionManagedPermissions | None = None + + @staticmethod + def from_dict(obj: Any) -> 'SessionManagedSettings': + assert isinstance(obj, dict) + permissions = from_union([SessionManagedPermissions.from_dict, from_none], obj.get("permissions")) + return SessionManagedSettings(permissions) + + def to_dict(self) -> dict: + result: dict = {} + if self.permissions is not None: + result["permissions"] = from_union([lambda x: to_class(SessionManagedPermissions, x), from_none], self.permissions) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class DiscoveredExtensions: @@ -25553,6 +25621,10 @@ class SessionOpenOptions: lsp_client_name: str | None = None """Identifier sent to LSP-style integrations.""" + managed_settings: SessionManagedSettings | None = None + """Permissions-only enterprise policy injected by the SDK host at session create or resume. + Composes restrictively with self-fetched and device policy and is not persisted. + """ max_inline_binary_bytes: int | None = None """Maximum decoded byte size of a single inline model-facing binary tool result persisted in session events (default 10 MB). @@ -25680,6 +25752,7 @@ def from_dict(obj: Any) -> 'SessionOpenOptions': is_experimental_mode = from_union([from_bool, from_none], obj.get("isExperimentalMode")) log_interactive_shells = from_union([from_bool, from_none], obj.get("logInteractiveShells")) lsp_client_name = from_union([from_str, from_none], obj.get("lspClientName")) + managed_settings = from_union([SessionManagedSettings.from_dict, from_none], obj.get("managedSettings")) max_inline_binary_bytes = from_union([from_int, from_none], obj.get("maxInlineBinaryBytes")) memory = from_union([MemoryConfiguration.from_dict, from_none], obj.get("memory")) model = from_union([from_str, from_none], obj.get("model")) @@ -25707,7 +25780,7 @@ def from_dict(obj: Any) -> 'SessionOpenOptions': verbosity = from_union([Verbosity, from_none], obj.get("verbosity")) working_directory = from_union([from_str, from_none], obj.get("workingDirectory")) working_directory_context = from_union([SessionContext.from_dict, from_none], obj.get("workingDirectoryContext")) - return SessionOpenOptions(additional_content_exclusion_policies, additional_directories, agent_context, allow_all_mcp_server_instructions, ask_user_disabled, auth_info, available_tools, capi, client_kind, client_name, coauthor_enabled, config_dir, continue_on_auto_mode, copilot_url, custom_agents_local_only, detached_from_spawning_parent_engagement_id, detached_from_spawning_parent_session_id, disabled_instruction_sources, disabled_skills, enable_citations, enable_file_change_tracking, enable_managed_settings, enable_on_demand_instruction_discovery, enable_script_safety, enable_streaming, env_value_mode, events_log_directory, events_log_includes_subagents, excluded_builtin_agents, excluded_tools, exp_assignments, feature_flags, included_builtin_agents, installed_plugins, integration_id, is_experimental_mode, log_interactive_shells, lsp_client_name, max_inline_binary_bytes, memory, model, model_capabilities_overrides, models, name, provider, providers, reasoning_effort, reasoning_summary, remote_defaulted_on, remote_exporting, remote_steerable, running_in_interactive_mode, sandbox_config, session_capabilities, session_id, session_limits, shell, shell_init_profile, shell_process_flags, skill_directories, skip_custom_instructions, trajectory_file, verbosity, working_directory, working_directory_context) + return SessionOpenOptions(additional_content_exclusion_policies, additional_directories, agent_context, allow_all_mcp_server_instructions, ask_user_disabled, auth_info, available_tools, capi, client_kind, client_name, coauthor_enabled, config_dir, continue_on_auto_mode, copilot_url, custom_agents_local_only, detached_from_spawning_parent_engagement_id, detached_from_spawning_parent_session_id, disabled_instruction_sources, disabled_skills, enable_citations, enable_file_change_tracking, enable_managed_settings, enable_on_demand_instruction_discovery, enable_script_safety, enable_streaming, env_value_mode, events_log_directory, events_log_includes_subagents, excluded_builtin_agents, excluded_tools, exp_assignments, feature_flags, included_builtin_agents, installed_plugins, integration_id, is_experimental_mode, log_interactive_shells, lsp_client_name, managed_settings, max_inline_binary_bytes, memory, model, model_capabilities_overrides, models, name, provider, providers, reasoning_effort, reasoning_summary, remote_defaulted_on, remote_exporting, remote_steerable, running_in_interactive_mode, sandbox_config, session_capabilities, session_id, session_limits, shell, shell_init_profile, shell_process_flags, skill_directories, skip_custom_instructions, trajectory_file, verbosity, working_directory, working_directory_context) def to_dict(self) -> dict: result: dict = {} @@ -25787,6 +25860,8 @@ def to_dict(self) -> dict: result["logInteractiveShells"] = from_union([from_bool, from_none], self.log_interactive_shells) if self.lsp_client_name is not None: result["lspClientName"] = from_union([from_str, from_none], self.lsp_client_name) + if self.managed_settings is not None: + result["managedSettings"] = from_union([lambda x: to_class(SessionManagedSettings, x), from_none], self.managed_settings) if self.max_inline_binary_bytes is not None: result["maxInlineBinaryBytes"] = from_union([from_int, from_none], self.max_inline_binary_bytes) if self.memory is not None: @@ -28383,6 +28458,7 @@ class RPC: debug_collect_logs_result_kind: DebugCollectLogsResultKind debug_collect_logs_skipped_entry: DebugCollectLogsSkippedEntry debug_collect_logs_source: DebugCollectLogsSource + disable_bypass_permissions_mode: DisableBypassPermissionsMode discovered_canvas: DiscoveredCanvas discovered_extension: DiscoveredExtension discovered_extension_mode: DiscoveredExtensionMode @@ -29016,6 +29092,8 @@ class RPC: session_list_filter: SessionListFilter session_load_deferred_repo_hooks_result: SessionLoadDeferredRepoHooksResult session_log_level: SessionLogLevel + session_managed_permissions: SessionManagedPermissions + session_managed_settings: SessionManagedSettings session_mcp_apps_call_tool_result: dict[str, Any] session_metadata_snapshot: SessionMetadataSnapshot session_mode: SessionMode @@ -29382,6 +29460,7 @@ def from_dict(obj: Any) -> 'RPC': debug_collect_logs_result_kind = DebugCollectLogsResultKind(obj.get("DebugCollectLogsResultKind")) debug_collect_logs_skipped_entry = DebugCollectLogsSkippedEntry.from_dict(obj.get("DebugCollectLogsSkippedEntry")) debug_collect_logs_source = DebugCollectLogsSource(obj.get("DebugCollectLogsSource")) + disable_bypass_permissions_mode = DisableBypassPermissionsMode(obj.get("DisableBypassPermissionsMode")) discovered_canvas = DiscoveredCanvas.from_dict(obj.get("DiscoveredCanvas")) discovered_extension = DiscoveredExtension.from_dict(obj.get("DiscoveredExtension")) discovered_extension_mode = DiscoveredExtensionMode(obj.get("DiscoveredExtensionMode")) @@ -30015,6 +30094,8 @@ def from_dict(obj: Any) -> 'RPC': session_list_filter = SessionListFilter.from_dict(obj.get("SessionListFilter")) session_load_deferred_repo_hooks_result = SessionLoadDeferredRepoHooksResult.from_dict(obj.get("SessionLoadDeferredRepoHooksResult")) session_log_level = SessionLogLevel(obj.get("SessionLogLevel")) + session_managed_permissions = SessionManagedPermissions.from_dict(obj.get("SessionManagedPermissions")) + session_managed_settings = SessionManagedSettings.from_dict(obj.get("SessionManagedSettings")) session_mcp_apps_call_tool_result = from_dict(lambda x: x, obj.get("SessionMcpAppsCallToolResult")) session_metadata_snapshot = SessionMetadataSnapshot.from_dict(obj.get("SessionMetadataSnapshot")) session_mode = SessionMode(obj.get("SessionMode")) @@ -30268,7 +30349,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_list_request, 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, agent_set_prompt_request, agents_get_discovery_paths_request, allow_all_permission_set_result, allow_all_permission_state, api_key_auth_info, auth_info, auth_info_type, built_in_model_catalog, built_in_model_catalog_entry, 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_exclusion_check_paths_request, content_exclusion_check_paths_result, content_exclusion_path_check, 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_extension, discovered_extension_mode, discovered_extension_plugin, discovered_extensions, discovered_extensions_disable_request, discovered_extensions_enable_request, discovered_extension_source, 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_direction, 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, factory_abort_request, factory_ack_result, factory_agent_options, factory_agent_request, factory_agent_result, factory_agent_summary, factory_cancel_request, factory_current_phase, factory_declared_limits, factory_durable_operation, factory_execute_request, factory_execute_result, factory_get_run_progress_request, factory_get_run_request, factory_journal_get_request, factory_journal_get_result, factory_journal_put_request, factory_list_runs_request, factory_list_runs_result, factory_log_line, factory_log_line_kind, factory_log_request, factory_phase_observation, factory_phase_status, factory_progress_line, factory_progress_page, factory_resume_request, factory_resume_result, factory_run_consumed, factory_run_detail, factory_run_failure, factory_run_failure_kind, factory_run_limits, factory_run_request, factory_run_result, factory_run_status, factory_run_summary, factory_run_terminal, 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_file_restore_skip_reason, history_list_rewind_points_result, history_preview_rewind_request, history_preview_rewind_result, history_rewind_change_type, history_rewind_file_preview, history_rewind_mode, history_rewind_outcome, history_rewind_point, history_rewind_request, history_rewind_result, history_rewind_unavailable_reason, history_skipped_file_restore, 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, interrupt_main_turn_request, interrupt_main_turn_result, 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_authentication_state_changed_request, 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_oauth_respond_request, mcp_oauth_respond_result, 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_factory, 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_factory, 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_factory, 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, queue_begin_deferred_idle_drain_request, queue_begin_deferred_idle_drain_result, queue_consume_system_notifications_request, queued_command_handled, queued_command_not_handled, queued_command_result, queue_defer_session_idle_request, queue_duplicate_at_request, queue_duplicate_at_result, queue_enqueue_resume_pending_result, queue_finish_deferred_idle_drain_request, queue_finish_deferred_idle_drain_result, queue_has_pending_result, queue_insert_at_request, queue_insert_at_result, queue_insert_message, queue_move_item_request, queue_move_item_result, queue_pending_items, queue_pending_items_kind, queue_pending_items_result, queue_remove_at_request, queue_remove_at_result, queue_remove_most_recent_result, queue_send_now_request, queue_send_now_result, queue_set_drain_paused_request, queue_snapshot_result, queue_update_text_request, queue_update_text_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, run_options, 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_network_proxy, sandbox_config_user_policy_seatbelt, schedule_add_at_request, schedule_add_cron_request, schedule_add_request, schedule_add_result, schedule_add_self_paced_request, schedule_entry, schedule_has_self_paced_result, schedule_list, schedule_rearm_self_paced_request, 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, send_system_notification_request, server_agent_list, server_instruction_source_list, server_skill, server_skill_list, session_activity, session_agent_list_request, session_auth_status, session_bulk_delete_result, session_cancel_all_background_agents_result, session_capability, session_commands_list_request, 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_sqlite_transaction_error, session_fs_sqlite_transaction_error_class, session_fs_sqlite_transaction_request, session_fs_sqlite_transaction_result, session_fs_sqlite_transaction_statement, session_fs_stat_request, session_fs_stat_result, session_fs_write_file_request, session_history_compact_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_limit_prediction_baseline_data, session_limit_prediction_client_type, session_limit_prediction_details, session_limit_prediction_predict_request, session_limit_prediction_request, session_limit_prediction_result, session_limit_prediction_source, session_limit_prediction_tier, session_limit_prediction_tier_option, session_limit_prediction_unavailable_reason, 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_list_request, 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_plugins_reload_request, session_provider_get_endpoint_request, session_prune_result, sessions_bulk_delete_request, sessions_check_in_use_request, sessions_check_in_use_result, sessions_close_request, sessions_close_result, sessions_delete_request, 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_metadata_request, sessions_get_metadata_result, sessions_get_persisted_remote_steerable_request, sessions_get_persisted_remote_steerable_result, session_sizes, sessions_list_non_empty_session_ids_request, sessions_list_non_empty_session_ids_result, 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_init_profile, shell_init_script, shell_init_script_shell, shell_kill_request, shell_kill_result, shell_kill_signal, shell_options, 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_add_summary_request, workspaces_add_summary_result, workspaces_autopilot_objective_exists_result, workspaces_checkpoints, workspaces_create_file_request, workspaces_delete_autopilot_objective_result, workspaces_diff_request, workspaces_ensure_request, workspaces_get_workspace_result, workspaces_list_checkpoints_result, workspaces_list_files_result, workspaces_read_autopilot_objective_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, workspaces_truncate_summaries_request, workspace_summary_host_type, workspaces_update_metadata_request, workspaces_workspace_details_host_type, workspaces_write_autopilot_objective_request, workspaces_write_autopilot_objective_result, 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_list_request, 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, agent_set_prompt_request, agents_get_discovery_paths_request, allow_all_permission_set_result, allow_all_permission_state, api_key_auth_info, auth_info, auth_info_type, built_in_model_catalog, built_in_model_catalog_entry, 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_exclusion_check_paths_request, content_exclusion_check_paths_result, content_exclusion_path_check, 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, disable_bypass_permissions_mode, discovered_canvas, discovered_extension, discovered_extension_mode, discovered_extension_plugin, discovered_extensions, discovered_extensions_disable_request, discovered_extensions_enable_request, discovered_extension_source, 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_direction, 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, factory_abort_request, factory_ack_result, factory_agent_options, factory_agent_request, factory_agent_result, factory_agent_summary, factory_cancel_request, factory_current_phase, factory_declared_limits, factory_durable_operation, factory_execute_request, factory_execute_result, factory_get_run_progress_request, factory_get_run_request, factory_journal_get_request, factory_journal_get_result, factory_journal_put_request, factory_list_runs_request, factory_list_runs_result, factory_log_line, factory_log_line_kind, factory_log_request, factory_phase_observation, factory_phase_status, factory_progress_line, factory_progress_page, factory_resume_request, factory_resume_result, factory_run_consumed, factory_run_detail, factory_run_failure, factory_run_failure_kind, factory_run_limits, factory_run_request, factory_run_result, factory_run_status, factory_run_summary, factory_run_terminal, 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_file_restore_skip_reason, history_list_rewind_points_result, history_preview_rewind_request, history_preview_rewind_result, history_rewind_change_type, history_rewind_file_preview, history_rewind_mode, history_rewind_outcome, history_rewind_point, history_rewind_request, history_rewind_result, history_rewind_unavailable_reason, history_skipped_file_restore, 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, interrupt_main_turn_request, interrupt_main_turn_result, 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_authentication_state_changed_request, 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_oauth_respond_request, mcp_oauth_respond_result, 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_factory, 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_factory, 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_factory, 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, queue_begin_deferred_idle_drain_request, queue_begin_deferred_idle_drain_result, queue_consume_system_notifications_request, queued_command_handled, queued_command_not_handled, queued_command_result, queue_defer_session_idle_request, queue_duplicate_at_request, queue_duplicate_at_result, queue_enqueue_resume_pending_result, queue_finish_deferred_idle_drain_request, queue_finish_deferred_idle_drain_result, queue_has_pending_result, queue_insert_at_request, queue_insert_at_result, queue_insert_message, queue_move_item_request, queue_move_item_result, queue_pending_items, queue_pending_items_kind, queue_pending_items_result, queue_remove_at_request, queue_remove_at_result, queue_remove_most_recent_result, queue_send_now_request, queue_send_now_result, queue_set_drain_paused_request, queue_snapshot_result, queue_update_text_request, queue_update_text_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, run_options, 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_network_proxy, sandbox_config_user_policy_seatbelt, schedule_add_at_request, schedule_add_cron_request, schedule_add_request, schedule_add_result, schedule_add_self_paced_request, schedule_entry, schedule_has_self_paced_result, schedule_list, schedule_rearm_self_paced_request, 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, send_system_notification_request, server_agent_list, server_instruction_source_list, server_skill, server_skill_list, session_activity, session_agent_list_request, session_auth_status, session_bulk_delete_result, session_cancel_all_background_agents_result, session_capability, session_commands_list_request, 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_sqlite_transaction_error, session_fs_sqlite_transaction_error_class, session_fs_sqlite_transaction_request, session_fs_sqlite_transaction_result, session_fs_sqlite_transaction_statement, session_fs_stat_request, session_fs_stat_result, session_fs_write_file_request, session_history_compact_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_limit_prediction_baseline_data, session_limit_prediction_client_type, session_limit_prediction_details, session_limit_prediction_predict_request, session_limit_prediction_request, session_limit_prediction_result, session_limit_prediction_source, session_limit_prediction_tier, session_limit_prediction_tier_option, session_limit_prediction_unavailable_reason, session_list, session_list_entry, session_list_filter, session_load_deferred_repo_hooks_result, session_log_level, session_managed_permissions, session_managed_settings, session_mcp_apps_call_tool_result, session_metadata_snapshot, session_mode, session_model_list, session_model_list_request, 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_plugins_reload_request, session_provider_get_endpoint_request, session_prune_result, sessions_bulk_delete_request, sessions_check_in_use_request, sessions_check_in_use_result, sessions_close_request, sessions_close_result, sessions_delete_request, 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_metadata_request, sessions_get_metadata_result, sessions_get_persisted_remote_steerable_request, sessions_get_persisted_remote_steerable_result, session_sizes, sessions_list_non_empty_session_ids_request, sessions_list_non_empty_session_ids_result, 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_init_profile, shell_init_script, shell_init_script_shell, shell_kill_request, shell_kill_result, shell_kill_signal, shell_options, 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_add_summary_request, workspaces_add_summary_result, workspaces_autopilot_objective_exists_result, workspaces_checkpoints, workspaces_create_file_request, workspaces_delete_autopilot_objective_result, workspaces_diff_request, workspaces_ensure_request, workspaces_get_workspace_result, workspaces_list_checkpoints_result, workspaces_list_files_result, workspaces_read_autopilot_objective_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, workspaces_truncate_summaries_request, workspace_summary_host_type, workspaces_update_metadata_request, workspaces_workspace_details_host_type, workspaces_write_autopilot_objective_request, workspaces_write_autopilot_objective_result, session_context_attribution, session_context_info, subagent_settings, task_progress, workspace_summary) def to_dict(self) -> dict: result: dict = {} @@ -30381,6 +30462,7 @@ def to_dict(self) -> dict: result["DebugCollectLogsResultKind"] = to_enum(DebugCollectLogsResultKind, self.debug_collect_logs_result_kind) result["DebugCollectLogsSkippedEntry"] = to_class(DebugCollectLogsSkippedEntry, self.debug_collect_logs_skipped_entry) result["DebugCollectLogsSource"] = to_enum(DebugCollectLogsSource, self.debug_collect_logs_source) + result["DisableBypassPermissionsMode"] = to_enum(DisableBypassPermissionsMode, self.disable_bypass_permissions_mode) result["DiscoveredCanvas"] = to_class(DiscoveredCanvas, self.discovered_canvas) result["DiscoveredExtension"] = to_class(DiscoveredExtension, self.discovered_extension) result["DiscoveredExtensionMode"] = to_enum(DiscoveredExtensionMode, self.discovered_extension_mode) @@ -31014,6 +31096,8 @@ def to_dict(self) -> dict: result["SessionListFilter"] = to_class(SessionListFilter, self.session_list_filter) result["SessionLoadDeferredRepoHooksResult"] = to_class(SessionLoadDeferredRepoHooksResult, self.session_load_deferred_repo_hooks_result) result["SessionLogLevel"] = to_enum(SessionLogLevel, self.session_log_level) + result["SessionManagedPermissions"] = to_class(SessionManagedPermissions, self.session_managed_permissions) + result["SessionManagedSettings"] = to_class(SessionManagedSettings, self.session_managed_settings) result["SessionMcpAppsCallToolResult"] = from_dict(lambda x: x, self.session_mcp_apps_call_tool_result) result["SessionMetadataSnapshot"] = to_class(SessionMetadataSnapshot, self.session_metadata_snapshot) result["SessionMode"] = to_enum(SessionMode, self.session_mode) @@ -34228,6 +34312,7 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "DebugCollectLogsResultKind", "DebugCollectLogsSkippedEntry", "DebugCollectLogsSource", + "DisableBypassPermissionsMode", "DiscoveredCanvas", "DiscoveredExtension", "DiscoveredExtensionMode", @@ -34992,6 +35077,8 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "SessionListFilter", "SessionLoadDeferredRepoHooksResult", "SessionLogLevel", + "SessionManagedPermissions", + "SessionManagedSettings", "SessionMcpAppsCallToolResult", "SessionMetadataSnapshot", "SessionModelList", diff --git a/python/test_client.py b/python/test_client.py index 0f3ba8136e..37f8316530 100644 --- a/python/test_client.py +++ b/python/test_client.py @@ -28,6 +28,8 @@ CloudSessionRepository, CopilotExpAssignmentResponse, ExpConfigEntry, + ManagedSettings, + ManagedSettingsPermissions, ModelBilling, ModelCapabilities, ModelInfo, @@ -611,6 +613,59 @@ async def mock_request(method, params, **kwargs): finally: await client.force_stop() + @pytest.mark.asyncio + async def test_create_and_resume_session_forward_managed_settings(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 + session = await client.create_session( + on_permission_request=PermissionHandler.approve_all, + enable_managed_settings=True, + managed_settings=ManagedSettings( + permissions=ManagedSettingsPermissions( + disable_bypass_permissions_mode="disable", + deny=["Shell(git push)"], + ask=["Domain(publish.example)"], + allow=["Read(**)"], + ) + ), + ) + await client.resume_session( + session.session_id, + on_permission_request=PermissionHandler.approve_all, + managed_settings=ManagedSettings( + permissions=ManagedSettingsPermissions(ask=["Domain(publish.example)"]) + ), + ) + + assert captured["session.create"]["enableManagedSettings"] is True + assert captured["session.create"]["managedSettings"] == { + "permissions": { + "disableBypassPermissionsMode": "disable", + "deny": ["Shell(git push)"], + "ask": ["Domain(publish.example)"], + "allow": ["Read(**)"], + } + } + assert captured["session.resume"]["managedSettings"] == { + "permissions": {"ask": ["Domain(publish.example)"]} + } + finally: + await client.force_stop() + @pytest.mark.asyncio async def test_create_and_resume_session_default_enable_experimental_mode_by_mode(self): with TemporaryDirectory() as base_directory: @@ -651,6 +706,7 @@ async def mock_request(method, params, **kwargs): finally: await client.force_stop() + async def test_managed_settings_omitted_when_not_supplied(self): client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -658,7 +714,7 @@ async def mock_request(method, params, **kwargs): async def mock_request(method, params, **kwargs): captured[method] = params - if method in ("session.create", "session.resume"): + if method == "session.create": result = {"sessionId": params.get("sessionId") or "session-1"} callback = kwargs.get("on_response_inline") if callback is not None: @@ -667,16 +723,42 @@ async def mock_request(method, params, **kwargs): return {} client._client.request = mock_request - session = await client.create_session( + await client.create_session( on_permission_request=PermissionHandler.approve_all, ) - await client.resume_session( - session.session_id, + + assert "managedSettings" not in captured["session.create"] + finally: + await client.force_stop() + + @pytest.mark.asyncio + async def test_managed_settings_preserves_empty_arrays(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 == "session.create": + 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 + await client.create_session( on_permission_request=PermissionHandler.approve_all, + managed_settings=ManagedSettings( + permissions=ManagedSettingsPermissions(deny=[], ask=[], allow=[]) + ), ) - assert "isExperimentalMode" not in captured["session.create"] - assert "isExperimentalMode" not in captured["session.resume"] + assert captured["session.create"]["managedSettings"] == { + "permissions": {"deny": [], "ask": [], "allow": []} + } finally: await client.force_stop() diff --git a/rust/src/generated/api_types.rs b/rust/src/generated/api_types.rs index cb0c4da541..f6af76ea70 100644 --- a/rust/src/generated/api_types.rs +++ b/rust/src/generated/api_types.rs @@ -13755,6 +13755,46 @@ pub struct SessionLoadDeferredRepoHooksResult { pub startup_prompts: Vec, } +/// Enterprise permission policy expressed with the runtime's managed permission-rule syntax. +/// +///

+/// +/// **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 SessionManagedPermissions { + /// Permission rules that allow matching operations unless another managed source, deny, or ask rule restricts them. + #[serde(skip_serializing_if = "Option::is_none")] + pub allow: Option>, + /// Permission rules that require explicit human approval. + #[serde(skip_serializing_if = "Option::is_none")] + pub ask: Option>, + /// Permission rules that block matching operations. Deny has highest precedence. + #[serde(skip_serializing_if = "Option::is_none")] + pub deny: Option>, + /// When set to `disable`, prevents bypass/allow-all permission modes. + #[serde(skip_serializing_if = "Option::is_none")] + pub disable_bypass_permissions_mode: Option, +} + +/// Managed settings an SDK host may inject at session startup. Only permissions are accepted in this initial contract. +/// +///
+/// +/// **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 SessionManagedSettings { + #[serde(skip_serializing_if = "Option::is_none")] + pub permissions: Option, +} + /// Public-facing projection of workspace metadata for SDK / TUI consumers #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -14117,6 +14157,9 @@ pub struct SessionOpenOptions { /// Identifier sent to LSP-style integrations. #[serde(skip_serializing_if = "Option::is_none")] pub lsp_client_name: Option, + /// Permissions-only enterprise policy injected by the SDK host at session create or resume. Composes restrictively with self-fetched and device policy and is not persisted. + #[serde(skip_serializing_if = "Option::is_none")] + pub managed_settings: Option, /// Maximum decoded byte size of a single inline model-facing binary tool result persisted in session events (default 10 MB). #[serde(skip_serializing_if = "Option::is_none")] pub max_inline_binary_bytes: Option, @@ -24170,6 +24213,23 @@ pub enum DebugCollectLogsResultKind { Unknown, } +/// +///
+/// +/// **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 DisableBypassPermissionsMode { + #[serde(rename = "disable")] + Disable, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// Persisted extension discovery source /// ///
diff --git a/rust/src/types.rs b/rust/src/types.rs index 1c5e457a36..29b779ec84 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -1744,6 +1744,50 @@ pub struct CopilotExpAssignmentResponse { pub assignment_context: String, } +/// Permission rules injected as a managed-settings layer at session bootstrap. +/// +/// All fields are optional; an omitted field imposes no constraint from this +/// layer. This layer composes restrictively with any server- or device-level +/// managed settings: [`deny`](Self::deny) and [`ask`](Self::ask) rules are +/// unioned across layers, every present [`allow`](Self::allow) list must admit a +/// tool for it to be allowed, and +/// [`disable_bypass_permissions_mode`](Self::disable_bypass_permissions_mode) is +/// honored if any layer sets it (deny-wins). +#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ManagedSettingsPermissions { + /// When set to `"disable"`, bypass-permissions mode is turned off for the + /// session regardless of other layers. Serialized as + /// `disableBypassPermissionsMode`. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub disable_bypass_permissions_mode: Option, + /// Tool-permission patterns that are always denied. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deny: Option>, + /// Tool-permission patterns that require an explicit ask. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ask: Option>, + /// Tool-permission patterns that are allowed without prompting. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub allow: Option>, +} + +/// Managed-settings layer injected at session startup. Currently carries only a +/// [`permissions`](Self::permissions) object. +/// +/// This layer is startup-only and is not persisted with the session. It must be +/// re-supplied on resume to remain in effect; omitting it on resume clears the +/// previously injected layer. It can be combined with +/// [`SessionConfig::enable_managed_settings`]. Older runtimes that do not +/// recognize the `managedSettings` field reject session creation (fail-closed). +#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ManagedSettings { + /// Permission rules for this managed-settings layer. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub permissions: Option, +} + /// Configuration for creating a new session via the `session.create` RPC. /// /// All fields are optional — the CLI applies sensible defaults. @@ -2028,6 +2072,15 @@ pub struct SessionConfig { /// (fail-closed). When `None`, behaves exactly as before. Set via /// [`with_enable_managed_settings`](Self::with_enable_managed_settings). pub enable_managed_settings: Option, + /// Optional managed-settings layer injected at session bootstrap. Currently + /// carries a [`permissions`](ManagedSettingsPermissions) object that composes + /// restrictively with any server- or device-level managed settings. This + /// layer is startup-only and is not persisted: it must be re-supplied on + /// resume to remain in effect. Can be combined with + /// [`enable_managed_settings`](Self::enable_managed_settings). Serialized on + /// the wire as `managedSettings`. Set via + /// [`with_managed_settings`](Self::with_managed_settings). + pub managed_settings: Option, /// Custom session filesystem provider for this session. Required when /// the [`Client`](crate::Client) was started with /// [`ClientOptions::session_fs`](crate::ClientOptions::session_fs) set. @@ -2172,6 +2225,7 @@ impl std::fmt::Debug for SessionConfig { .field("exp_assignments", &self.exp_assignments) .field("enable_managed_settings", &self.enable_managed_settings) .field("enable_experimental_mode", &self.enable_experimental_mode) + .field("managed_settings", &self.managed_settings) .field( "session_fs_provider", &self.session_fs_provider.as_ref().map(|_| ""), @@ -2281,6 +2335,7 @@ impl Default for SessionConfig { commands: None, exp_assignments: None, enable_managed_settings: None, + managed_settings: None, session_fs_provider: None, permission_handler: None, elicitation_handler: None, @@ -2444,6 +2499,7 @@ impl SessionConfig { exp_assignments: self.exp_assignments, enable_managed_settings: self.enable_managed_settings, is_experimental_mode: self.enable_experimental_mode, + managed_settings: self.managed_settings, }; let runtime = SessionConfigRuntime { @@ -3047,6 +3103,15 @@ impl SessionConfig { self.enable_managed_settings = Some(enabled); self } + + /// Inject a managed-settings layer (currently permission rules) at session + /// bootstrap. This layer is startup-only and is not persisted, so it must be + /// re-supplied on resume to remain in effect. Can be combined with + /// [`with_enable_managed_settings`](Self::with_enable_managed_settings). + pub fn with_managed_settings(mut self, managed_settings: ManagedSettings) -> Self { + self.managed_settings = Some(managed_settings); + self + } } /// /// See [`SessionConfig`] for the construction patterns (chained `with_*` @@ -3233,6 +3298,12 @@ pub struct ResumeSessionConfig { /// process restart. Set via /// [`with_enable_managed_settings`](Self::with_enable_managed_settings). pub enable_managed_settings: Option, + /// Optional managed-settings layer injected on resume. See + /// [`SessionConfig::managed_settings`]. This layer is not persisted, so it + /// must be re-supplied on resume to remain in effect; omitting it clears the + /// previously injected layer. Serialized on the wire as `managedSettings`. + /// Set via [`with_managed_settings`](Self::with_managed_settings). + pub managed_settings: Option, /// Custom session filesystem provider. Required on resume when the /// [`Client`](crate::Client) was started with /// [`ClientOptions::session_fs`](crate::ClientOptions::session_fs). @@ -3370,6 +3441,7 @@ impl std::fmt::Debug for ResumeSessionConfig { .field("exp_assignments", &self.exp_assignments) .field("enable_managed_settings", &self.enable_managed_settings) .field("enable_experimental_mode", &self.enable_experimental_mode) + .field("managed_settings", &self.managed_settings) .field( "session_fs_provider", &self.session_fs_provider.as_ref().map(|_| ""), @@ -3525,6 +3597,7 @@ impl ResumeSessionConfig { exp_assignments: self.exp_assignments, enable_managed_settings: self.enable_managed_settings, is_experimental_mode: self.enable_experimental_mode, + managed_settings: self.managed_settings, suppress_resume_event: self.suppress_resume_event, continue_pending_work: self.continue_pending_work, }; @@ -3616,6 +3689,7 @@ impl ResumeSessionConfig { commands: None, exp_assignments: None, enable_managed_settings: None, + managed_settings: None, session_fs_provider: None, suppress_resume_event: None, continue_pending_work: None, @@ -4200,6 +4274,14 @@ impl ResumeSessionConfig { self.enable_managed_settings = Some(enabled); self } + + /// Inject a managed-settings layer (currently permission rules) on resume. + /// See [`SessionConfig::with_managed_settings`]. Must be re-supplied on + /// resume; omitting it clears the previously injected layer. + pub fn with_managed_settings(mut self, managed_settings: ManagedSettings) -> Self { + self.managed_settings = Some(managed_settings); + self + } } /// Controls how the system message is constructed. diff --git a/rust/src/wire.rs b/rust/src/wire.rs index 1620e5ee1a..643887c44b 100644 --- a/rust/src/wire.rs +++ b/rust/src/wire.rs @@ -182,6 +182,7 @@ pub(crate) struct SessionCreateWire { pub enable_managed_settings: Option, #[serde(skip_serializing_if = "Option::is_none")] pub is_experimental_mode: Option, + pub managed_settings: Option, } /// The exact JSON shape sent on the `session.resume` JSON-RPC request. @@ -327,4 +328,5 @@ pub(crate) struct SessionResumeWire { pub enable_managed_settings: Option, #[serde(skip_serializing_if = "Option::is_none")] pub is_experimental_mode: Option, + pub managed_settings: Option, } diff --git a/rust/tests/session_test.rs b/rust/tests/session_test.rs index 41cae3e950..d1a08fd853 100644 --- a/rust/tests/session_test.rs +++ b/rust/tests/session_test.rs @@ -22,8 +22,8 @@ use github_copilot_sdk::session_events::{ use github_copilot_sdk::types::{ CanvasProviderIdentity, CloudSessionOptions, CloudSessionRepository, CommandContext, CommandDefinition, CommandHandler, DeliveryMode, ElicitationRequest, ElicitationResult, - ExitPlanModeData, ExtensionInfo, MessageOptions, RequestId, SessionConfig, SessionId, - SetModelOptions, Tool, ToolInvocation, ToolResult, + ExitPlanModeData, ExtensionInfo, ManagedSettings, ManagedSettingsPermissions, MessageOptions, + RequestId, SessionConfig, SessionId, SetModelOptions, Tool, ToolInvocation, ToolResult, }; use github_copilot_sdk::{Client, ContextTier, ErrorKind, ProtocolErrorKind, tool}; use serde_json::Value; @@ -763,6 +763,93 @@ async fn create_session_sends_canvas_wire_fields() { timeout(TIMEOUT, create_handle).await.unwrap().unwrap(); } +#[tokio::test] +async fn create_and_resume_send_managed_settings_permissions() { + use github_copilot_sdk::types::ResumeSessionConfig; + + let (client, mut server_read, mut server_write) = make_client(); + + let managed = ManagedSettings { + permissions: Some(ManagedSettingsPermissions { + disable_bypass_permissions_mode: Some("disable".to_string()), + deny: Some(vec!["shell(rm*)".to_string()]), + ask: Some(vec!["write".to_string()]), + allow: Some(vec![]), + }), + }; + + let create_handle = tokio::spawn({ + let client = client.clone(); + let managed = managed.clone(); + async move { + client + .create_session( + SessionConfig::default() + .with_enable_managed_settings(true) + .with_managed_settings(managed), + ) + .await + .unwrap() + } + }); + + let request = read_framed(&mut server_read).await; + assert_eq!(request["method"], "session.create"); + assert_eq!(request["params"]["enableManagedSettings"], true); + let perms = &request["params"]["managedSettings"]["permissions"]; + assert_eq!(perms["disableBypassPermissionsMode"], "disable"); + assert_eq!(perms["deny"][0], "shell(rm*)"); + assert_eq!(perms["ask"][0], "write"); + assert_eq!(perms["allow"], serde_json::json!([])); + + let id = request["id"].as_u64().unwrap(); + let session_id = requested_session_id(&request).to_string(); + let response = serde_json::json!({ + "jsonrpc": "2.0", + "id": id, + "result": { "sessionId": session_id.clone() }, + }); + write_framed(&mut server_write, &serde_json::to_vec(&response).unwrap()).await; + timeout(TIMEOUT, create_handle).await.unwrap().unwrap(); + + let resume_handle = tokio::spawn({ + let client = client.clone(); + let session_id = session_id.clone(); + async move { + client + .resume_session( + ResumeSessionConfig::new(SessionId::from(session_id)) + .with_managed_settings(managed), + ) + .await + .unwrap() + } + }); + + let request = read_framed(&mut server_read).await; + assert_eq!(request["method"], "session.resume"); + assert_eq!( + request["params"]["managedSettings"]["permissions"]["deny"][0], + "shell(rm*)" + ); + + let id = request["id"].as_u64().unwrap(); + let response = serde_json::json!({ + "jsonrpc": "2.0", + "id": id, + "result": { "sessionId": session_id }, + }); + write_framed(&mut server_write, &serde_json::to_vec(&response).unwrap()).await; + + let reload = read_framed(&mut server_read).await; + assert_eq!(reload["method"], "session.skills.reload"); + let id = reload["id"].as_u64().unwrap(); + let response = serde_json::json!({ "jsonrpc": "2.0", "id": id, "result": {} }); + write_framed(&mut server_write, &serde_json::to_vec(&response).unwrap()).await; + + timeout(TIMEOUT, resume_handle).await.unwrap().unwrap(); +} + fn make_client_with_telemetry( callback: github_copilot_sdk::github_telemetry::GitHubTelemetryCallback, ) -> (Client, tokio::io::DuplexStream, tokio::io::DuplexStream) { From 5411c8857628353b5111d7c02df62fc704e3bec5 Mon Sep 17 00:00:00 2001 From: Josh Spicer <23246594+joshspicer@users.noreply.github.com> Date: Mon, 3 Aug 2026 08:04:58 -0700 Subject: [PATCH 2/2] fix(sdk): enforce safeguards for injected policy Treat direct managedSettings injection as a managed session in every language SDK and document the compatible-runtime requirement. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: d5d4d699-33e2-4a55-9d48-57d2e483dd3d --- CHANGELOG.md | 2 +- dotnet/src/Client.cs | 2 +- dotnet/src/Types.cs | 4 +-- .../test/Unit/ClientSessionLifetimeTests.cs | 6 +++-- go/client.go | 8 ++++-- go/client_test.go | 9 +++++++ .../github/copilot/SessionRequestBuilder.java | 6 +++-- .../github/copilot/ManagedSettingsTest.java | 12 +++++++++ nodejs/src/client.ts | 7 +++-- nodejs/src/types.ts | 5 ++-- nodejs/test/client.test.ts | 19 ++++++++++++++ python/copilot/client.py | 6 +++-- python/test_client.py | 4 ++- rust/src/session.rs | 26 ++++++++++++++++--- rust/src/types.rs | 5 ++-- 15 files changed, 99 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8502e688b6..c9d3e3eb29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ See [GitHub Releases](https://github.com/github/copilot-sdk/releases) for the fu Session create and resume accept a new optional `managedSettings` option that injects an enterprise permissions policy at session startup, alongside the existing `enableManagedSettings` self-fetch flag. The current contract is permissions-only: `disableBypassPermissionsMode` (the literal `"disable"`), plus `deny`, `ask`, and `allow` rule lists. The layer composes restrictively with any server- or device-level managed settings (deny/ask are unioned, every present allow list must admit a tool, and `disableBypassPermissionsMode` is deny-wins). -This layer is startup-only and is not persisted with the session, so it must be re-supplied on resume to remain in effect; omitting it on resume clears the previously injected layer. It can be combined with `enableManagedSettings`. Older runtimes that do not recognize the field reject session creation (fail-closed) rather than silently ignoring it, so it requires a Copilot CLI runtime whose schema includes managed settings. +This layer is startup-only and is not persisted with the session, so it must be re-supplied on resume to remain in effect; omitting it on resume clears the previously injected layer. It can be combined with `enableManagedSettings`. Older runtimes may ignore the additive field, so hosts must not rely on injected policy until they ship a Copilot CLI runtime whose schema includes managed settings. ```ts const session = await client.createSession({ diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs index 004ed6600d..364612f93d 100644 --- a/dotnet/src/Client.cs +++ b/dotnet/src/Client.cs @@ -785,7 +785,7 @@ private CopilotSession InitializeSession( session.RegisterTools(config.Tools ?? []); session.RegisterPermissionHandler( config.OnPermissionRequest, - config.EnableManagedSettings is true); + config.EnableManagedSettings is true || config.ManagedSettings is not null); session.RegisterMcpAuthHandler(config.OnMcpAuthRequest); session.RegisterCommands(config.Commands); session.RegisterElicitationHandler(config.OnElicitationRequest); diff --git a/dotnet/src/Types.cs b/dotnet/src/Types.cs index b423088aa1..69741d8b40 100644 --- a/dotnet/src/Types.cs +++ b/dotnet/src/Types.cs @@ -3033,8 +3033,8 @@ public sealed class ManagedSettingsPermissions /// re-supplied on to remain in /// effect; omitting it on resume clears the previously injected layer. It can be /// combined with . Older -/// runtimes that do not recognize the managedSettings field reject session -/// creation (fail-closed). +/// runtimes may ignore this additive field, so hosts must not rely on injected +/// policy until they ship a compatible runtime. /// public sealed class ManagedSettings { diff --git a/dotnet/test/Unit/ClientSessionLifetimeTests.cs b/dotnet/test/Unit/ClientSessionLifetimeTests.cs index 94dd1b68bd..fc862ec7ad 100644 --- a/dotnet/test/Unit/ClientSessionLifetimeTests.cs +++ b/dotnet/test/Unit/ClientSessionLifetimeTests.cs @@ -459,7 +459,6 @@ public async Task CreateSessionAsync_Serializes_ManagedSettings_Permissions() await using var session = await client.CreateSessionAsync(new SessionConfig { - EnableManagedSettings = true, ManagedSettings = new ManagedSettings { Permissions = new ManagedSettingsPermissions @@ -474,7 +473,10 @@ public async Task CreateSessionAsync_Serializes_ManagedSettings_Permissions() }); var request = Assert.Single(server.Requests, request => request.Method == "session.create"); - Assert.True(request.Params.GetProperty("enableManagedSettings").GetBoolean()); + Assert.False(request.Params.TryGetProperty("enableManagedSettings", out _)); + var managedField = typeof(CopilotSession).GetField("_managedSettingsEnabled", BindingFlags.Instance | BindingFlags.NonPublic) + ?? throw new InvalidOperationException("Managed settings field was not found."); + Assert.True((bool)managedField.GetValue(session)!); var permissions = request.Params.GetProperty("managedSettings").GetProperty("permissions"); Assert.Equal("disable", permissions.GetProperty("disableBypassPermissionsMode").GetString()); Assert.Equal("shell(rm*)", Assert.Single(permissions.GetProperty("deny").EnumerateArray()).GetString()); diff --git a/go/client.go b/go/client.go index e484c62e75..da7b2c79b6 100644 --- a/go/client.go +++ b/go/client.go @@ -750,6 +750,10 @@ func extractTransformCallbacks(config *SystemMessageConfig) (*SystemMessageConfi return wireConfig, callbacks } +func hasManagedSettings(enableManagedSettings *bool, managedSettings *ManagedSettings) bool { + return (enableManagedSettings != nil && *enableManagedSettings) || managedSettings != nil +} + func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Session, error) { if config == nil { config = &SessionConfig{} @@ -913,7 +917,7 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses sessionID, c.client, "", - config.EnableManagedSettings != nil && *config.EnableManagedSettings, + hasManagedSettings(config.EnableManagedSettings, config.ManagedSettings), ) s.registerTools(config.Tools) @@ -1242,7 +1246,7 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string, sessionID, c.client, "", - config.EnableManagedSettings != nil && *config.EnableManagedSettings, + hasManagedSettings(config.EnableManagedSettings, config.ManagedSettings), ) session.registerTools(config.Tools) diff --git a/go/client_test.go b/go/client_test.go index a35923d1ab..5d97058f38 100644 --- a/go/client_test.go +++ b/go/client_test.go @@ -3442,6 +3442,15 @@ func TestSessionRequests_ManagedSettings(t *testing.T) { "allow": []any{"Read(**)"}, } + t.Run("direct injection enables managed safeguards", func(t *testing.T) { + if !hasManagedSettings(nil, settings) { + t.Fatal("expected injected managed settings to enable managed safeguards") + } + if hasManagedSettings(nil, nil) { + t.Fatal("expected an ordinary session to remain unmanaged") + } + }) + t.Run("includes managedSettings on create when set", func(t *testing.T) { req := createSessionRequest{EnableManagedSettings: Bool(true), ManagedSettings: settings} data, err := json.Marshal(req) diff --git a/java/src/main/java/com/github/copilot/SessionRequestBuilder.java b/java/src/main/java/com/github/copilot/SessionRequestBuilder.java index c240184f22..f2dd72cb73 100644 --- a/java/src/main/java/com/github/copilot/SessionRequestBuilder.java +++ b/java/src/main/java/com/github/copilot/SessionRequestBuilder.java @@ -372,7 +372,8 @@ static void configureSession(CopilotSession session, SessionConfig config) { if (config.getOnPermissionRequest() != null) { session.registerPermissionHandler(config.getOnPermissionRequest()); } - session.setManagedSettingsEnabled(config.getEnableManagedSettings().orElse(false)); + session.setManagedSettingsEnabled( + config.getEnableManagedSettings().orElse(false) || config.getManagedSettings() != null); if (config.getOnMcpAuthRequest() != null) { session.registerMcpAuthHandler(config.getOnMcpAuthRequest()); } @@ -423,7 +424,8 @@ static void configureSession(CopilotSession session, ResumeSessionConfig config) if (config.getOnPermissionRequest() != null) { session.registerPermissionHandler(config.getOnPermissionRequest()); } - session.setManagedSettingsEnabled(config.getEnableManagedSettings().orElse(false)); + session.setManagedSettingsEnabled( + config.getEnableManagedSettings().orElse(false) || config.getManagedSettings() != null); if (config.getOnMcpAuthRequest() != null) { session.registerMcpAuthHandler(config.getOnMcpAuthRequest()); } diff --git a/java/src/test/java/com/github/copilot/ManagedSettingsTest.java b/java/src/test/java/com/github/copilot/ManagedSettingsTest.java index 1e991e0008..20d226751b 100644 --- a/java/src/test/java/com/github/copilot/ManagedSettingsTest.java +++ b/java/src/test/java/com/github/copilot/ManagedSettingsTest.java @@ -41,4 +41,16 @@ void rejectsUnsupportedBypassValue() { var permissions = new ManagedSettingsPermissions(); assertThrows(IllegalArgumentException.class, () -> permissions.setDisableBypassPermissionsMode("enable")); } + + @Test + void directInjectionEnablesManagedSafeguards() throws Exception { + var session = new CopilotSession("session-1", null); + var settings = new ManagedSettings().setPermissions(new ManagedSettingsPermissions()); + + SessionRequestBuilder.configureSession(session, new SessionConfig().setManagedSettings(settings)); + + var field = CopilotSession.class.getDeclaredField("managedSettingsEnabled"); + field.setAccessible(true); + assertEquals(true, field.getBoolean(session)); + } } diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index e4c222de99..f395a0083d 100644 --- a/nodejs/src/client.ts +++ b/nodejs/src/client.ts @@ -1467,7 +1467,9 @@ export class CopilotClient { this.onGetTraceContext, { mcpAuthHandler: config.onMcpAuthRequest, - managedSettingsEnabled: config.enableManagedSettings, + managedSettingsEnabled: + config.enableManagedSettings === true || + config.managedSettings !== undefined, } ); s.registerTools(config.tools); @@ -1706,7 +1708,8 @@ export class CopilotClient { this.onGetTraceContext, { mcpAuthHandler: config.onMcpAuthRequest, - managedSettingsEnabled: config.enableManagedSettings, + managedSettingsEnabled: + config.enableManagedSettings === true || config.managedSettings !== undefined, } ); session.registerTools(config.tools); diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index 3b5ce6d0e6..2dac1ce500 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -2586,8 +2586,9 @@ export interface SessionConfigBase { * when both are supplied the injected, server, and device restrictions all * apply. * - * Requires a Copilot runtime whose RPC schema includes `managedSettings`; - * older runtimes reject the unknown field rather than silently ignoring it. + * Requires a Copilot runtime whose RPC schema includes `managedSettings`. + * Older runtimes may ignore this additive field, so hosts must not rely on + * injected policy until they ship a compatible runtime. */ managedSettings?: ManagedSettings; diff --git a/nodejs/test/client.test.ts b/nodejs/test/client.test.ts index c9d0fbb374..c123d12df5 100644 --- a/nodejs/test/client.test.ts +++ b/nodejs/test/client.test.ts @@ -3594,6 +3594,25 @@ describe("managedSettings serialization", () => { }); }); + it("marks directly injected sessions as managed", async () => { + const client = new CopilotClient(); + await client.start(); + onTestFinished(() => stopClient(client)); + 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}`); + } + ); + + const session = await client.createSession({ + onPermissionRequest: approveAll, + managedSettings: { permissions: { deny: ["Edit(/secrets/**)"] } }, + }); + + expect((session as any).managedSettingsEnabled).toBe(true); + }); + it("omits managedSettings when not supplied", async () => { const params = await captureCreateParams({}); expect(params.managedSettings).toBeUndefined(); diff --git a/python/copilot/client.py b/python/copilot/client.py index 1fbd5045eb..f70185dbad 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -2634,7 +2634,8 @@ def _initialize_session(sid: str) -> CopilotSession: sid, self._client, workspace_path=None, - managed_settings_enabled=enable_managed_settings is True, + managed_settings_enabled=enable_managed_settings is True + or managed_settings is not None, ) if self._session_fs_config: if create_session_fs_handler is None: @@ -3291,7 +3292,8 @@ async def resume_session( session_id, self._client, workspace_path=None, - managed_settings_enabled=enable_managed_settings is True, + managed_settings_enabled=enable_managed_settings is True + or managed_settings is not None, ) if self._session_fs_config: if create_session_fs_handler is None: diff --git a/python/test_client.py b/python/test_client.py index 37f8316530..d3447cd784 100644 --- a/python/test_client.py +++ b/python/test_client.py @@ -643,7 +643,7 @@ async def mock_request(method, params, **kwargs): ) ), ) - await client.resume_session( + resumed_session = await client.resume_session( session.session_id, on_permission_request=PermissionHandler.approve_all, managed_settings=ManagedSettings( @@ -651,6 +651,8 @@ async def mock_request(method, params, **kwargs): ), ) + assert session._managed_settings_enabled is True + assert resumed_session._managed_settings_enabled is True assert captured["session.create"]["enableManagedSettings"] is True assert captured["session.create"]["managedSettings"] == { "permissions": { diff --git a/rust/src/session.rs b/rust/src/session.rs index d505541a50..c6c806b1c1 100644 --- a/rust/src/session.rs +++ b/rust/src/session.rs @@ -66,6 +66,13 @@ pub(crate) struct SessionHandlers { pub tools: Arc>>, } +fn has_managed_settings( + enable_managed_settings: Option, + managed_settings: Option<&crate::types::ManagedSettings>, +) -> bool { + enable_managed_settings == Some(true) || managed_settings.is_some() +} + /// Shared state between a [`Session`] and its event loop, used by [`Session::send_and_wait`]. struct IdleWaiter { tx: oneshot::Sender, Error>>, @@ -899,7 +906,10 @@ impl Client { ); let handlers = SessionHandlers { permission: permission_handler, - managed_settings_enabled: wire.enable_managed_settings == Some(true), + managed_settings_enabled: has_managed_settings( + wire.enable_managed_settings, + wire.managed_settings.as_ref(), + ), elicitation: runtime.elicitation_handler.take(), mcp_auth: runtime.mcp_auth_handler.take(), user_input: runtime.user_input_handler.take(), @@ -1169,7 +1179,10 @@ impl Client { ); let handlers = SessionHandlers { permission: permission_handler, - managed_settings_enabled: wire.enable_managed_settings == Some(true), + managed_settings_enabled: has_managed_settings( + wire.enable_managed_settings, + wire.managed_settings.as_ref(), + ), elicitation: runtime.elicitation_handler.take(), mcp_auth: runtime.mcp_auth_handler.take(), user_input: runtime.user_input_handler.take(), @@ -2550,9 +2563,16 @@ fn inject_transform_sections_resume( mod tests { use serde_json::json; - use super::{notification_permission_payload, permission_request_data}; + use super::{has_managed_settings, notification_permission_payload, permission_request_data}; use crate::handler::PermissionResult; + #[test] + fn direct_injection_enables_managed_safeguards() { + let settings = crate::types::ManagedSettings::default(); + assert!(has_managed_settings(None, Some(&settings))); + assert!(!has_managed_settings(None, None)); + } + #[test] fn notification_payload_suppresses_no_result() { assert!(notification_permission_payload(&PermissionResult::NoResult).is_none()); diff --git a/rust/src/types.rs b/rust/src/types.rs index 29b779ec84..e61f57b6f5 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -1778,8 +1778,9 @@ pub struct ManagedSettingsPermissions { /// This layer is startup-only and is not persisted with the session. It must be /// re-supplied on resume to remain in effect; omitting it on resume clears the /// previously injected layer. It can be combined with -/// [`SessionConfig::enable_managed_settings`]. Older runtimes that do not -/// recognize the `managedSettings` field reject session creation (fail-closed). +/// [`SessionConfig::enable_managed_settings`]. Older runtimes may ignore this +/// additive field, so hosts must not rely on injected policy until they ship a +/// compatible runtime. #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ManagedSettings {