diff --git a/CHANGELOG.md b/CHANGELOG.md index 41bf05e171..e9f22a3df7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,43 @@ 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`. Host injection requires Copilot CLI `1.0.79-5` or later and does not require an SDK protocol version bump. + +The generated session-event types also expose truthful injected-policy provenance: `session.managed_settings_resolved` can report `source` as `client` or `mixed`, with optional `clientManaged` metadata. + +```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 = 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 228b115601..2df1f05d10 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); @@ -1205,6 +1205,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, AdditionalDirectories: config.AdditionalDirectories); @@ -1425,6 +1426,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, AdditionalDirectories: config.AdditionalDirectories); @@ -2781,6 +2783,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, IList? AdditionalDirectories = null); @@ -2895,6 +2898,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, IList? AdditionalDirectories = null); diff --git a/dotnet/src/Generated/Rpc.cs b/dotnet/src/Generated/Rpc.cs index 32778850b8..604ed09600 100644 --- a/dotnet/src/Generated/Rpc.cs +++ b/dotnet/src/Generated/Rpc.cs @@ -1564,6 +1564,10 @@ public sealed class ServerSkill [JsonPropertyName("argumentHint")] public string? ArgumentHint { get; set; } + /// Canonical slash command name used to invoke the skill, without the leading '/'. + [JsonPropertyName("commandName")] + public string? CommandName { get; set; } + /// Description of what the skill does. [JsonPropertyName("description")] public string Description { get; set; } = string.Empty; @@ -2036,6 +2040,19 @@ internal sealed class UserSettingsSetRequest public JsonElement Settings { get; set; } } +/// Validated device-managed settings discovered before a session exists. +[Experimental(Diagnostics.Experimental)] +public sealed class ManagedSettingsReadResult +{ + /// Discovery or validation error text when managed settings could not be read safely. + [JsonPropertyName("errorMessage")] + public string? ErrorMessage { get; set; } + + /// Validated, canonical managed-settings JSON. Omitted when no managed settings were discovered or when discovered settings failed validation. + [JsonPropertyName("settingsJson")] + public JsonElement? SettingsJson { get; set; } +} + /// Indicates whether the calling client was registered as the session filesystem provider. [Experimental(Diagnostics.Experimental)] public sealed class SessionFsSetProviderResult @@ -6734,6 +6751,10 @@ public sealed class Skill [JsonPropertyName("argumentHint")] public string? ArgumentHint { get; set; } + /// Canonical slash command name used to invoke the skill, without the leading '/'. + [JsonPropertyName("commandName")] + public string? CommandName { get; set; } + /// Description of what the skill does. [JsonPropertyName("description")] public string Description { get; set; } = string.Empty; @@ -8739,9 +8760,9 @@ public sealed class SandboxConfig [JsonPropertyName("addCurrentWorkingDirectory")] public bool? AddCurrentWorkingDirectory { get; set; } - /// Whether to auto-grant read access to common developer-tool caches, registries, and toolchains in their default home locations (cargo, go, npm, Maven, and more), plus read-write access to (and, on Unix, up-front creation of) the scratch caches builds write on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so builds work without exporting CARGO_HOME/GOPATH/etc. Default: true (enabled by default; set to false to opt out). - [JsonPropertyName("allowDevToolCaches")] - public bool? AllowDevToolCaches { get; set; } + /// Whether to auto-grant read access to common developer-tool caches, registries, and toolchains in their default home locations (cargo, go, npm, Maven, and more), plus read-write access to (and, on Unix, up-front creation of) the scratch caches builds write on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so builds work without extra configuration; a relocated CARGO_HOME additionally gets its Cargo lock files granted read-write. Default: true (enabled by default; set to false to opt out). + [JsonPropertyName("allowDevToolAccess")] + public bool? AllowDevToolAccess { get; set; } /// Whether sandboxing is enabled for the session. [JsonPropertyName("enabled")] @@ -10608,6 +10629,23 @@ public sealed class PermissionRequestResult public bool Success { get; set; } } +/// Optional informational context describing how and where the permission decision was made. This does not affect permission behavior. +[Experimental(Diagnostics.Experimental)] +public sealed class PermissionDecisionContext +{ + /// Disposition of the permission request as observed by the responding client. + [JsonPropertyName("outcome")] + public PermissionDecisionOutcome Outcome { get; set; } + + /// Controlled reason or actor responsible for the response. + [JsonPropertyName("source")] + public PermissionDecisionSource Source { get; set; } + + /// Client surface that submitted the response. + [JsonPropertyName("surface")] + public PermissionDecisionSurface Surface { get; set; } +} + /// The client's response to the pending permission prompt. /// Polymorphic base type discriminated by kind. [Experimental(Diagnostics.Experimental)] @@ -11188,6 +11226,10 @@ public partial class PermissionDecisionDeniedByPermissionRequestHook : Permissio [Experimental(Diagnostics.Experimental)] internal sealed class PermissionDecisionRequest { + /// Optional informational context describing how and where this response was made. Omit it to preserve legacy behavior without attributing an origin. + [JsonPropertyName("decisionContext")] + public PermissionDecisionContext? DecisionContext { get; set; } + /// Request ID of the pending permission request. [JsonPropertyName("requestId")] public string RequestId { get; set; } = string.Empty; @@ -12122,6 +12164,38 @@ internal sealed class MetadataContextInfoRequest public string SessionId { get; set; } = string.Empty; } +/// The six normalized `/context` header buckets, computed from the same tokenization as `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` describe window capacity rather than occupied context, so the values do not sum to `totalTokens`. +public sealed class MetadataContextAttributionResultContextAttributionCategories +{ + /// Output reserve plus post-blocking-threshold buffer. + [JsonPropertyName("buffer")] + public long Buffer { get; set; } + + /// Custom-instructions tokens (0 when none are configured). + [JsonPropertyName("customInstructions")] + public long CustomInstructions { get; set; } + + /// Remaining unused window capacity (clamped at 0). + [JsonPropertyName("freeSpace")] + public long FreeSpace { get; set; } + + /// MCP tool-definition tokens. + [JsonPropertyName("mcpTools")] + public long McpTools { get; set; } + + /// Conversation (user/assistant/tool) message tokens. + [JsonPropertyName("messages")] + public long Messages { get; set; } + + /// System prompt tokens, excluding custom instructions. + [JsonPropertyName("systemPrompt")] + public long SystemPrompt { get; set; } + + /// Non-MCP tool-definition tokens. + [JsonPropertyName("systemTools")] + public long SystemTools { get; set; } +} + /// Successful compaction history for the session. public sealed class MetadataContextAttributionResultContextAttributionCompactions { @@ -12161,14 +12235,42 @@ public sealed class MetadataContextAttributionResultContextAttributionEntry /// Per-source token attribution snapshot for the current context window. The heaviest individual messages are available separately via `metadata.getContextHeaviestMessages`. public sealed class MetadataContextAttributionResultContextAttribution { + /// Output reserve plus the tokens past the buffer-exhaustion blocking threshold. Mirrors `SessionContextInfo.bufferTokens`. + [JsonPropertyName("bufferTokens")] + public long BufferTokens { get; set; } + + /// The six normalized `/context` header buckets, computed from the same tokenization as `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` describe window capacity rather than occupied context, so the values do not sum to `totalTokens`. + [JsonPropertyName("categories")] + public MetadataContextAttributionResultContextAttributionCategories Categories { get => field ??= new(); set; } + /// Successful compaction history for the session. [JsonPropertyName("compactions")] public MetadataContextAttributionResultContextAttributionCompactions Compactions { get => field ??= new(); set; } + /// Token count at which background compaction starts. Mirrors `SessionContextInfo.compactionThreshold`. + [JsonPropertyName("compactionThreshold")] + public long CompactionThreshold { get; set; } + /// Flat list of per-source attribution entries. Group by `kind` and render unrecognized kinds generically. Nesting and rollups are expressed via `parentId`. [JsonPropertyName("entries")] public IList Entries { get => field ??= []; set; } + /// Prompt limit plus the model's output reserve: the full context window `categories.freeSpace` and `categories.buffer` are measured against. Mirrors `SessionContextInfo.limit`. + [JsonPropertyName("limit")] + public long Limit { get; set; } + + /// The concrete model id the entire breakdown was tokenized against (feeds the per-model token multiplier). Under `Auto` (Free/Student) this is the resolved model, not the literal `auto` sentinel, so totals are not undercounted. A single-model approximation of a potentially multi-model Auto session. + [JsonPropertyName("modelId")] + public string ModelId { get; set; } = string.Empty; + + /// How `modelId` was chosen. Not a closed set — tolerate unknown values. Known values today: `autoResolved` (the model Auto resolved to), `selected` (the user's explicitly selected model), `default` (a fallback before any model is known). + [JsonPropertyName("modelSource")] + public string ModelSource { get; set; } = string.Empty; + + /// Maximum prompt tokens the resolved model accepts — the denominator for a `##k/###k` context-usage display. Mirrors `SessionContextInfo.promptTokenLimit`. + [JsonPropertyName("promptTokenLimit")] + public long PromptTokenLimit { get; set; } + /// Total token count of the current context window the entries are measured against (system message + conversation messages + tool definitions — the same total reported by /context). Divide an entry's `tokens` by this to derive its share. [JsonPropertyName("totalTokens")] public long TotalTokens { get; set; } @@ -14937,6 +15039,55 @@ public sealed class CanvasProviderInvokeActionRequest public string SessionId { get; set; } = string.Empty; } +/// Opaque integrator-owned process launch profile for one extension entrypoint. +[Experimental(Diagnostics.Experimental)] +public sealed class ExtensionLaunchProfile +{ + /// Opaque integrator-defined arguments passed to the executable. The runtime does not append the extension entrypoint. + [JsonPropertyName("args")] + public IList Args { get => field ??= []; set; } + + /// Opaque integrator-defined environment variables. Runtime-owned COPILOT_SDK_PATH, SESSION_ID, and COPILOT_EXTENSION_PARENT_PID values take precedence. + [JsonPropertyName("env")] + public IDictionary Env { get => field ??= new Dictionary(); set; } + + /// Executable used to launch the extension entrypoint. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("executable")] + public string Executable { get; set; } = string.Empty; +} + +/// The launch profile for a supported entrypoint. Omit launch when the provider does not support the entrypoint. +[Experimental(Diagnostics.Experimental)] +public sealed class ExtensionLaunchProviderResolveResult +{ + /// Opaque launch profile, omitted when this provider does not support the entrypoint. + [JsonPropertyName("launch")] + public ExtensionLaunchProfile? Launch { get; set; } +} + +/// A discovered extension entrypoint that the registered integrator may classify and resolve to an opaque launch profile. +[Experimental(Diagnostics.Experimental)] +public sealed class ExtensionLaunchProviderResolveRequest +{ + /// Source-qualified extension identifier. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// Absolute path to the discovered extension entrypoint. + [JsonPropertyName("modulePath")] + public string ModulePath { get; set; } = string.Empty; + + /// Human-readable extension name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Discovery source for the extension entrypoint. + [JsonPropertyName("source")] + public ExtensionSource Source { get; set; } +} + /// Acknowledgement. Returning successfully simply means the SDK accepted the start frame; it does not imply the request will succeed. [Experimental(Diagnostics.Experimental)] public sealed class LlmInferenceHttpRequestStartResult @@ -20936,6 +21087,210 @@ public override void Write(Utf8JsonWriter writer, PermissionsConfigureAdditional } +/// Disposition of a permission request as observed by the responding client. +[Experimental(Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct PermissionDecisionOutcome : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public PermissionDecisionOutcome(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// The request was approved automatically without a new human decision. + public static PermissionDecisionOutcome AutoApproved { get; } = new("auto_approved"); + + /// The request was denied without an interactive user decision; source records why. + public static PermissionDecisionOutcome AutopilotDenied { get; } = new("autopilot_denied"); + + /// The response came from an interactive user prompt. + public static PermissionDecisionOutcome PromptedUser { get; } = new("prompted_user"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(PermissionDecisionOutcome left, PermissionDecisionOutcome right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(PermissionDecisionOutcome left, PermissionDecisionOutcome right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is PermissionDecisionOutcome other && Equals(other); + + /// + public bool Equals(PermissionDecisionOutcome other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override PermissionDecisionOutcome Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, PermissionDecisionOutcome value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(PermissionDecisionOutcome)); + } + } +} + + +/// Controlled reason or actor responsible for a permission response. +[Experimental(Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct PermissionDecisionSource : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public PermissionDecisionSource(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// The response followed the auto-approval judge recommendation. + public static PermissionDecisionSource JudgeRecommendation { get; } = new("judge_recommendation"); + + /// A human supplied the response through an interactive prompt. + public static PermissionDecisionSource HumanResponse { get; } = new("human_response"); + + /// The host applied a standing policy or override rather than a judge recommendation or human decision. + public static PermissionDecisionSource HostPolicy { get; } = new("host_policy"); + + /// The host denied the request because no interactive user response was available. + public static PermissionDecisionSource UnattendedFallback { get; } = new("unattended_fallback"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(PermissionDecisionSource left, PermissionDecisionSource right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(PermissionDecisionSource left, PermissionDecisionSource right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is PermissionDecisionSource other && Equals(other); + + /// + public bool Equals(PermissionDecisionSource other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override PermissionDecisionSource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, PermissionDecisionSource value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(PermissionDecisionSource)); + } + } +} + + +/// Client surface that submitted a permission response. +[Experimental(Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct PermissionDecisionSurface : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public PermissionDecisionSurface(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// The interactive Copilot CLI terminal UI. + public static PermissionDecisionSurface Tui { get; } = new("tui"); + + /// The non-interactive Copilot CLI prompt mode. + public static PermissionDecisionSurface PromptMode { get; } = new("prompt_mode"); + + /// The Copilot App client. + public static PermissionDecisionSurface CopilotApp { get; } = new("copilot_app"); + + /// A generic Copilot SDK client. + public static PermissionDecisionSurface Sdk { get; } = new("sdk"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(PermissionDecisionSurface left, PermissionDecisionSurface right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(PermissionDecisionSurface left, PermissionDecisionSurface right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is PermissionDecisionSurface other && Equals(other); + + /// + public bool Equals(PermissionDecisionSurface other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override PermissionDecisionSurface Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, PermissionDecisionSurface value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(PermissionDecisionSurface)); + } + } +} + + /// Optional source for allow-all telemetry. Defaults to `rpc` when omitted for SDK callers. [Experimental(Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] @@ -23035,6 +23390,14 @@ internal async Task ConnectAsync(string? token = null, bool? enab return await CopilotClient.InvokeRpcAsync(_rpc, "connect", [request], cancellationToken); } + /// Registers the calling SDK client as the per-entrypoint extension launch provider. Call before creating any sessions. When omitted, the runtime temporarily falls back to its built-in Node launcher for backward compatibility. + /// The to monitor for cancellation requests. The default is . + [Experimental(Diagnostics.Experimental)] + public async Task RegisterExtensionLaunchProviderAsync(CancellationToken cancellationToken = default) + { + await CopilotClient.InvokeRpcAsync(_rpc, "registerExtensionLaunchProvider", [], cancellationToken); + } + /// Models APIs. public ServerModelsApi Models => field ?? @@ -23107,6 +23470,12 @@ internal async Task ConnectAsync(string? token = null, bool? enab Interlocked.CompareExchange(ref field, new(_rpc), null) ?? field; + /// ManagedSettings APIs. + public ServerManagedSettingsApi ManagedSettings => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + /// Runtime APIs. public ServerRuntimeApi Runtime => field ?? @@ -23806,6 +24175,26 @@ public async Task SetAsync(object settings, CancellationT } } +/// Provides server-scoped ManagedSettings APIs. +[Experimental(Diagnostics.Experimental)] +public sealed class ServerManagedSettingsApi +{ + private readonly JsonRpc _rpc; + + internal ServerManagedSettingsApi(JsonRpc rpc) + { + _rpc = rpc; + } + + /// Discovers device-managed settings from production MDM and managed-file sources, validates them against the runtime-owned managed-settings schema, and returns the canonical JSON without requiring a session. + /// The to monitor for cancellation requests. The default is . + /// Validated device-managed settings discovered before a session exists. + public async Task ReadAsync(CancellationToken cancellationToken = default) + { + return await CopilotClient.InvokeRpcAsync(_rpc, "managedSettings.read", [], cancellationToken); + } +} + /// Provides server-scoped Runtime APIs. [Experimental(Diagnostics.Experimental)] public sealed class ServerRuntimeApi @@ -27006,15 +27395,16 @@ public async Task ConfigureAsync(bool? approveAllToo /// Provides a decision for a pending tool permission request. /// Request ID of the pending permission request. /// The client's response to the pending permission prompt. + /// Optional informational context describing how and where this response was made. Omit it to preserve legacy behavior without attributing an origin. /// The to monitor for cancellation requests. The default is . /// Indicates whether the permission decision was applied; false when the request was already resolved. - public async Task HandlePendingPermissionRequestAsync(string requestId, PermissionDecision result, CancellationToken cancellationToken = default) + public async Task HandlePendingPermissionRequestAsync(string requestId, PermissionDecision result, PermissionDecisionContext? decisionContext = null, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(requestId); ArgumentNullException.ThrowIfNull(result); _session.ThrowIfDisposed(); - var request = new PermissionDecisionRequest { SessionId = _session.SessionId, RequestId = requestId, Result = result }; + var request = new PermissionDecisionRequest { SessionId = _session.SessionId, RequestId = requestId, Result = result, DecisionContext = decisionContext }; return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.permissions.handlePendingPermissionRequest", [request], cancellationToken); } @@ -28528,6 +28918,17 @@ public static void RegisterClientSessionApiHandlers(JsonRpc rpc, FuncHandles `extensionLaunchProvider` client global API methods. +[Experimental(Diagnostics.Experimental)] +public interface IExtensionLaunchProviderHandler +{ + /// Asks the registered SDK client to resolve an opaque process launch profile for one discovered extension entrypoint immediately before launch or reload. The provider must respond within 15 seconds. + /// A discovered extension entrypoint that the registered integrator may classify and resolve to an opaque launch profile. + /// The to monitor for cancellation requests. The default is . + /// The launch profile for a supported entrypoint. Omit launch when the provider does not support the entrypoint. + Task ResolveAsync(ExtensionLaunchProviderResolveRequest request, CancellationToken cancellationToken = default); +} + /// Handles `llmInference` client global API methods. [Experimental(Diagnostics.Experimental)] public interface ILlmInferenceHandler @@ -28557,6 +28958,9 @@ public interface IGitHubTelemetryHandler /// Provides all client global API handler groups for a connection. public sealed class ClientGlobalApiHandlers { + /// Optional handler for ExtensionLaunchProvider client global API methods. + public IExtensionLaunchProviderHandler? ExtensionLaunchProvider { get; set; } + /// Optional handler for LlmInference client global API methods. public ILlmInferenceHandler? LlmInference { get; set; } @@ -28575,6 +28979,11 @@ internal static class ClientGlobalApiRegistration /// public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiHandlers handlers) { + rpc.SetLocalRpcMethod("extensionLaunchProvider.resolve", (Func>)(async (request, cancellationToken) => + { + var handler = handlers.ExtensionLaunchProvider ?? throw new InvalidOperationException("No extensionLaunchProvider client-global handler registered"); + return await handler.ResolveAsync(request, cancellationToken); + }), singleObjectParam: true); rpc.SetLocalRpcMethod("llmInference.httpRequestStart", (Func>)(async (request, cancellationToken) => { var handler = handlers.LlmInference ?? throw new InvalidOperationException("No llmInference client-global handler registered"); @@ -29045,6 +29454,9 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(ExecuteCommandParams))] [JsonSerializable(typeof(ExecuteCommandResult))] [JsonSerializable(typeof(Extension))] +[JsonSerializable(typeof(ExtensionLaunchProfile))] +[JsonSerializable(typeof(ExtensionLaunchProviderResolveRequest))] +[JsonSerializable(typeof(ExtensionLaunchProviderResolveResult))] [JsonSerializable(typeof(ExtensionList))] [JsonSerializable(typeof(ExtensionsDisableRequest))] [JsonSerializable(typeof(ExtensionsEnableRequest))] @@ -29134,6 +29546,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(LogRequest))] [JsonSerializable(typeof(LogResult))] [JsonSerializable(typeof(LspInitializeRequest))] +[JsonSerializable(typeof(ManagedSettingsReadResult))] [JsonSerializable(typeof(MarketplaceAddResult))] [JsonSerializable(typeof(MarketplaceBrowseResult))] [JsonSerializable(typeof(MarketplaceInfo))] @@ -29221,6 +29634,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(McpUnregisterExternalClientRequest))] [JsonSerializable(typeof(MetadataContextAttributionResult))] [JsonSerializable(typeof(MetadataContextAttributionResultContextAttribution))] +[JsonSerializable(typeof(MetadataContextAttributionResultContextAttributionCategories))] [JsonSerializable(typeof(MetadataContextAttributionResultContextAttributionCompactions))] [JsonSerializable(typeof(MetadataContextAttributionResultContextAttributionEntry))] [JsonSerializable(typeof(MetadataContextHeaviestMessagesRequest))] @@ -29272,6 +29686,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(PermissionDecision))] [JsonSerializable(typeof(PermissionDecisionApproveForLocationApproval))] [JsonSerializable(typeof(PermissionDecisionApproveForSessionApproval))] +[JsonSerializable(typeof(PermissionDecisionContext))] [JsonSerializable(typeof(PermissionDecisionRequest))] [JsonSerializable(typeof(PermissionLocationAddToolApprovalParams))] [JsonSerializable(typeof(PermissionLocationApplyParams))] diff --git a/dotnet/src/Generated/SessionEvents.cs b/dotnet/src/Generated/SessionEvents.cs index 6b81a4770d..47c13846fb 100644 --- a/dotnet/src/Generated/SessionEvents.cs +++ b/dotnet/src/Generated/SessionEvents.cs @@ -1353,7 +1353,7 @@ public sealed partial class SessionAutoModeResolvedEvent : SessionEvent public required SessionAutoModeResolvedData Data { get; set; } } -/// Enterprise managed-settings resolution: the effective managed settings the session applied and where they came from, so SDK clients can show users what is enterprise-managed and by which authority. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted; for a session-independent pull, use the SDK `getManagedSettings()` API, which returns the identical payload. Managed settings have a single authoritative source, so the highest-authority present layer (server > device) wins wholesale; `bypassPermissionsDisabled` is deny-wins across layers. Marked experimental while the managed-settings surface stabilizes. +/// Enterprise managed-settings resolution: the effective managed settings the session applied and which channels contributed, so SDK clients can show users what is enterprise-managed. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted. Device values take precedence over server values per ordinary key, while permissions compose restrictively across device, server, and SDK-client layers. The account-scoped `getManagedSettings()` API does not include session-local client injection. Marked experimental while the managed-settings surface stabilizes. /// Represents the session.managed_settings_resolved event. [Experimental(Diagnostics.Experimental)] public sealed partial class SessionManagedSettingsResolvedEvent : SessionEvent @@ -4278,7 +4278,7 @@ public sealed partial class SessionAutoModeResolvedData public bool? StickyOverride { get; set; } } -/// Enterprise managed-settings resolution: the effective managed settings the session applied and where they came from, so SDK clients can show users what is enterprise-managed and by which authority. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted; for a session-independent pull, use the SDK `getManagedSettings()` API, which returns the identical payload. Managed settings have a single authoritative source, so the highest-authority present layer (server > device) wins wholesale; `bypassPermissionsDisabled` is deny-wins across layers. Marked experimental while the managed-settings surface stabilizes. +/// Enterprise managed-settings resolution: the effective managed settings the session applied and which channels contributed, so SDK clients can show users what is enterprise-managed. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted. Device values take precedence over server values per ordinary key, while permissions compose restrictively across device, server, and SDK-client layers. The account-scoped `getManagedSettings()` API does not include session-local client injection. Marked experimental while the managed-settings surface stabilizes. [Experimental(Diagnostics.Experimental)] public sealed partial class SessionManagedSettingsResolvedData { @@ -4286,7 +4286,12 @@ public sealed partial class SessionManagedSettingsResolvedData [JsonPropertyName("bypassPermissionsDisabled")] public required bool BypassPermissionsDisabled { get; set; } - /// Whether the device (MDM/plist/registry/file) managed-settings layer was present. + /// Whether a session-local permissions layer injected by the SDK host was present. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("clientManaged")] + public bool? ClientManaged { get; set; } + + /// Whether an actual device MDM/plist/registry/file managed-settings layer was present. [JsonPropertyName("deviceManaged")] public required bool DeviceManaged { get; set; } @@ -4298,7 +4303,7 @@ public sealed partial class SessionManagedSettingsResolvedData [JsonPropertyName("managedKeys")] public required string[] ManagedKeys { get; set; } - /// Whether server and device each supplied a permission allowlist, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. + /// Whether at least two managed sources supplied permission allowlists, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("permissionsAllowIntersected")] public bool? PermissionsAllowIntersected { get; set; } @@ -4312,7 +4317,7 @@ public sealed partial class SessionManagedSettingsResolvedData [JsonPropertyName("settings")] public JsonElement? Settings { get; set; } - /// Which channel supplied the effective managed settings (the winning layer), or `none` when no policy is in force. + /// Channel summary: `server`, `device`, or `client` when exactly one channel contributed; `mixed` when multiple channels contributed; otherwise `none`. Consult the per-channel booleans for exact provenance. [JsonPropertyName("source")] public required ManagedSettingsResolvedSource Source { get; set; } } @@ -8564,6 +8569,11 @@ public sealed partial class SkillsLoadedSkill [JsonPropertyName("argumentHint")] public string? ArgumentHint { get; set; } + /// Canonical slash command name used to invoke the skill, without the leading '/'. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("commandName")] + public string? CommandName { get; set; } + /// Description of what the skill does. [JsonPropertyName("description")] public required string Description { get; set; } @@ -12142,7 +12152,7 @@ public override void Write(Utf8JsonWriter writer, AutoModeResolvedReasoningBucke } } -/// Which channel supplied the effective enterprise managed settings (highest-authority present layer wins wholesale). +/// Summary of which managed-settings channels contributed to the effective session policy. Use the per-channel booleans for exact provenance. [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] public readonly struct ManagedSettingsResolvedSource : IEquatable @@ -12161,13 +12171,19 @@ public ManagedSettingsResolvedSource(string value) /// Gets the value associated with this . public string Value => _value ?? string.Empty; - /// Account/org policy self-fetched from the GitHub managed-settings endpoint (higher authority). + /// Only the server/account channel contributed. public static ManagedSettingsResolvedSource Server { get; } = new("server"); - /// Device-level MDM policy discovered from plist/registry/file (lower authority). + /// Only the device MDM/plist/registry/file channel contributed. public static ManagedSettingsResolvedSource Device { get; } = new("device"); - /// No managed policy is in force (no layer contributed). + /// Only session-local SDK-host injection contributed. + public static ManagedSettingsResolvedSource Client { get; } = new("client"); + + /// More than one channel contributed. Ordinary keys resolve device over server per key, while permissions compose restrictively across all present layers. + public static ManagedSettingsResolvedSource Mixed { get; } = new("mixed"); + + /// No managed policy is in force (no channel contributed). public static ManagedSettingsResolvedSource None { get; } = new("none"); /// Returns a value indicating whether two instances are equivalent. diff --git a/dotnet/src/Types.cs b/dotnet/src/Types.cs index 7489fbb4ec..680955a010 100644 --- a/dotnet/src/Types.cs +++ b/dotnet/src/Types.cs @@ -3044,6 +3044,70 @@ public sealed class GitHubMcpToolConfig public bool? DisableFormDeferral { get; set; } } +/// +/// Controls whether bypass-permissions mode is available in a managed session. +/// +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum DisableBypassPermissionsMode +{ + /// Turn off bypass-permissions mode. + [JsonStringEnumMemberName("disable")] + Disable +} + +/// +/// 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 DisableBypassPermissionsMode? 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 may ignore this additive field, so hosts must not rely on injected +/// policy until they ship a compatible runtime. +/// +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 @@ -3136,6 +3200,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; @@ -3601,6 +3666,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 7b7d8c109a..d4b4100b4c 100644 --- a/dotnet/test/Unit/ClientSessionLifetimeTests.cs +++ b/dotnet/test/Unit/ClientSessionLifetimeTests.cs @@ -10,6 +10,7 @@ using System.Runtime.CompilerServices; using System.Text; using System.Text.Json; +using GitHub.Copilot.Rpc; using Xunit; namespace GitHub.Copilot.Test.Unit; @@ -515,6 +516,94 @@ 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(); + var permissionInvocation = new TaskCompletionSource( + TaskCreationOptions.RunContinuationsAsynchronously); + + await using var session = await client.CreateSessionAsync(new SessionConfig + { + ManagedSettings = new ManagedSettings + { + Permissions = new ManagedSettingsPermissions + { + DisableBypassPermissionsMode = DisableBypassPermissionsMode.Disable, + Deny = ["shell(rm*)"], + Ask = ["write"], + Allow = [] + } + }, + OnPermissionRequest = (_, invocation) => + { + permissionInvocation.TrySetResult(invocation); + return Task.FromResult(PermissionDecision.NoResult()); + } + }); + + var request = Assert.Single(server.Requests, request => request.Method == "session.create"); + Assert.False(request.Params.TryGetProperty("enableManagedSettings", out _)); + 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()); + + DispatchEvent(session, new PermissionRequestedEvent + { + Data = new PermissionRequestedData + { + PermissionRequest = new PermissionRequest { Kind = "read" }, + RequestId = "managed-permission" + } + }); + var invocation = await permissionInvocation.Task.WaitAsync(TimeSpan.FromSeconds(5)); + Assert.True(invocation.ManagedSettingsEnabled); + } + + [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/dotnet/test/Unit/SessionEventSerializationTests.cs b/dotnet/test/Unit/SessionEventSerializationTests.cs index 64e28a5aee..326ac3f3c7 100644 --- a/dotnet/test/Unit/SessionEventSerializationTests.cs +++ b/dotnet/test/Unit/SessionEventSerializationTests.cs @@ -368,4 +368,65 @@ public void McpOauthRequiredData_Preserves_Static_Client_Secret() Assert.NotNull(authEvent.Data.StaticClientConfig); Assert.Equal("static-secret", authEvent.Data.StaticClientConfig.ClientSecret); } + + [Fact] + public void ManagedSettingsResolvedData_Preserves_Client_Provenance() + { + Assert.Equal("server", ManagedSettingsResolvedSource.Server.Value); + Assert.Equal("device", ManagedSettingsResolvedSource.Device.Value); + Assert.Equal("client", ManagedSettingsResolvedSource.Client.Value); + Assert.Equal("mixed", ManagedSettingsResolvedSource.Mixed.Value); + Assert.Equal("none", ManagedSettingsResolvedSource.None.Value); + + const string clientJson = """ + { + "id": "11111111-1111-1111-1111-111111111111", + "timestamp": "2026-03-15T21:26:54.987Z", + "parentId": null, + "type": "session.managed_settings_resolved", + "data": { + "source": "client", + "serverManaged": false, + "deviceManaged": false, + "clientManaged": true, + "failClosed": false, + "bypassPermissionsDisabled": true, + "managedKeys": ["permissions"] + } + } + """; + + var clientEvent = Assert.IsType( + SessionEvent.FromJson(clientJson)); + Assert.Equal(ManagedSettingsResolvedSource.Client, clientEvent.Data.Source); + Assert.True(clientEvent.Data.ClientManaged); + using (var document = JsonDocument.Parse(clientEvent.ToJson())) + { + Assert.True(document.RootElement.GetProperty("data").GetProperty("clientManaged").GetBoolean()); + } + + const string mixedJson = """ + { + "id": "22222222-2222-2222-2222-222222222222", + "timestamp": "2026-03-15T21:26:54.987Z", + "parentId": null, + "type": "session.managed_settings_resolved", + "data": { + "source": "mixed", + "serverManaged": true, + "deviceManaged": true, + "failClosed": false, + "bypassPermissionsDisabled": true, + "managedKeys": ["permissions"] + } + } + """; + + var mixedEvent = Assert.IsType( + SessionEvent.FromJson(mixedJson)); + Assert.Equal(ManagedSettingsResolvedSource.Mixed, mixedEvent.Data.Source); + Assert.Null(mixedEvent.Data.ClientManaged); + using var mixedDocument = JsonDocument.Parse(mixedEvent.ToJson()); + Assert.False(mixedDocument.RootElement.GetProperty("data").TryGetProperty("clientManaged", out _)); + } } diff --git a/go/client.go b/go/client.go index d2c43c26bd..856e933ea2 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{} @@ -833,6 +837,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)) @@ -917,7 +922,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) @@ -1215,6 +1220,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) } @@ -1250,7 +1256,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 b5274cfda0..3322d77412 100644 --- a/go/client_test.go +++ b/go/client_test.go @@ -3563,3 +3563,162 @@ func TestIsTerminal(t *testing.T) { } }) } + +func TestSessionRequests_ManagedSettings(t *testing.T) { + settings := &ManagedSettings{ + Permissions: &ManagedSettingsPermissions{ + DisableBypassPermissionsMode: DisableBypassPermissionsModeDisable, + 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("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) + 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("preserves explicit empty permission arrays", func(t *testing.T) { + // A non-nil empty allow list is restrictive: it admits no operations. + // Preserve field presence while still omitting nil slices. + req := createSessionRequest{ManagedSettings: &ManagedSettings{ + Permissions: &ManagedSettingsPermissions{ + DisableBypassPermissionsMode: DisableBypassPermissionsModeDisable, + 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 value, ok := perms[key].([]any); !ok || len(value) != 0 { + t.Errorf("Expected %s to be an explicit empty array, got %v", key, perms[key]) + } + } + }) + + t.Run("distinguishes explicit empty allow from an absent allow", func(t *testing.T) { + // Security-critical: a present empty allow list admits nothing, while an + // absent allow list imposes no allow restriction. The wire output must + // tell these apart per-field, so an explicit empty slice serializes as + // `[]` while a nil slice is omitted entirely. + req := createSessionRequest{ManagedSettings: &ManagedSettings{ + Permissions: &ManagedSettingsPermissions{ + Allow: []string{}, // present but empty: admit nothing + // Deny and Ask left nil: no such restriction supplied. + }, + }} + 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) + + allow, ok := perms["allow"].([]any) + if !ok || len(allow) != 0 { + t.Errorf("Expected allow to be an explicit empty array, got %v", perms["allow"]) + } + if _, present := perms["deny"]; present { + t.Errorf("Expected deny to be omitted when nil, got %v", perms["deny"]) + } + if _, present := perms["ask"]; present { + t.Errorf("Expected ask to be omitted when nil, got %v", perms["ask"]) + } + }) + + t.Run("distinguishes explicit empty arrays on resume", func(t *testing.T) { + req := resumeSessionRequest{SessionID: "s1", ManagedSettings: &ManagedSettings{ + Permissions: &ManagedSettingsPermissions{ + 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) + for _, key := range []string{"deny", "ask", "allow"} { + if value, ok := perms[key].([]any); !ok || len(value) != 0 { + t.Errorf("Expected %s to be an explicit empty array on resume, got %v", key, perms[key]) + } + } + }) +} diff --git a/go/rpc/zrpc.go b/go/rpc/zrpc.go index 5ecee9f6cb..6de48d06c1 100644 --- a/go/rpc/zrpc.go +++ b/go/rpc/zrpc.go @@ -2109,6 +2109,44 @@ type Extension struct { Status ExtensionStatus `json:"status"` } +// Opaque integrator-owned process launch profile for one extension entrypoint. +// Experimental: ExtensionLaunchProfile is part of an experimental API and may change or be +// removed. +type ExtensionLaunchProfile struct { + // Opaque integrator-defined arguments passed to the executable. The runtime does not append + // the extension entrypoint. + Args []string `json:"args"` + // Opaque integrator-defined environment variables. Runtime-owned COPILOT_SDK_PATH, + // SESSION_ID, and COPILOT_EXTENSION_PARENT_PID values take precedence. + Env map[string]string `json:"env"` + // Executable used to launch the extension entrypoint. + Executable string `json:"executable"` +} + +// A discovered extension entrypoint that the registered integrator may classify and resolve +// to an opaque launch profile. +// Experimental: ExtensionLaunchProviderResolveRequest is part of an experimental API and +// may change or be removed. +type ExtensionLaunchProviderResolveRequest struct { + // Source-qualified extension identifier. + ID string `json:"id"` + // Absolute path to the discovered extension entrypoint. + ModulePath string `json:"modulePath"` + // Human-readable extension name. + Name string `json:"name"` + // Discovery source for the extension entrypoint. + Source ExtensionSource `json:"source"` +} + +// The launch profile for a supported entrypoint. Omit launch when the provider does not +// support the entrypoint. +// Experimental: ExtensionLaunchProviderResolveResult is part of an experimental API and may +// change or be removed. +type ExtensionLaunchProviderResolveResult struct { + // Opaque launch profile, omitted when this provider does not support the entrypoint. + Launch *ExtensionLaunchProfile `json:"launch,omitempty"` +} + // Extensions discovered for the session, with their current status. // Experimental: ExtensionList is part of an experimental API and may change or be removed. type ExtensionList struct { @@ -3737,6 +3775,17 @@ type LspInitializeRequest struct { WorkingDirectory *string `json:"workingDirectory,omitempty"` } +// Validated device-managed settings discovered before a session exists. +// Experimental: ManagedSettingsReadResult is part of an experimental API and may change or +// be removed. +type ManagedSettingsReadResult struct { + // Discovery or validation error text when managed settings could not be read safely. + ErrorMessage *string `json:"errorMessage,omitempty"` + // Validated, canonical managed-settings JSON. Omitted when no managed settings were + // discovered or when discovered settings failed validation. + SettingsJSON any `json:"settingsJson,omitempty"` +} + // Result of registering a new marketplace. // Experimental: MarketplaceAddResult is part of an experimental API and may change or be // removed. @@ -6091,10 +6140,26 @@ func (PermissionDecisionApproveForSessionApprovalWrite) Kind() PermissionDecisio return PermissionDecisionApproveForSessionApprovalKindWrite } +// Optional informational context describing how and where the permission decision was made. +// This does not affect permission behavior. +// Experimental: PermissionDecisionContext is part of an experimental API and may change or +// be removed. +type PermissionDecisionContext struct { + // Disposition of the permission request as observed by the responding client. + Outcome PermissionDecisionOutcome `json:"outcome"` + // Controlled reason or actor responsible for the response. + Source PermissionDecisionSource `json:"source"` + // Client surface that submitted the response. + Surface PermissionDecisionSurface `json:"surface"` +} + // Pending permission request ID and the decision to apply (approve/reject and scope). // Experimental: PermissionDecisionRequest is part of an experimental API and may change or // be removed. type PermissionDecisionRequest struct { + // Optional informational context describing how and where this response was made. Omit it + // to preserve legacy behavior without attributing an origin. + DecisionContext *PermissionDecisionContext `json:"decisionContext,omitempty"` // Request ID of the pending permission request RequestID string `json:"requestId"` // The client's response to the pending permission prompt @@ -7852,6 +7917,11 @@ type RegisterEventInterestResult struct { Handle string `json:"handle"` } +// Experimental: RegisterExtensionLaunchProviderResult is part of an experimental API and +// may change or be removed. +type RegisterExtensionLaunchProviderResult struct { +} + // Params to attach an extension loader's tools to a session. // Experimental: RegisterExtensionToolsParams is part of an experimental API and may change // or be removed. @@ -8134,9 +8204,10 @@ type SandboxConfig struct { // toolchains in their default home locations (cargo, go, npm, Maven, and more), plus // read-write access to (and, on Unix, up-front creation of) the scratch caches builds write // on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so - // builds work without exporting CARGO_HOME/GOPATH/etc. Default: true (enabled by default; - // set to false to opt out). - AllowDevToolCaches *bool `json:"allowDevToolCaches,omitempty"` + // builds work without extra configuration; a relocated CARGO_HOME additionally gets its + // Cargo lock files granted read-write. Default: true (enabled by default; set to false to + // opt out). + AllowDevToolAccess *bool `json:"allowDevToolAccess,omitempty"` // Whether sandboxing is enabled for the session. Enabled bool `json:"enabled"` // Whether to export `GH_TOKEN` so the `gh` CLI authenticates inside the sandbox without the @@ -8570,6 +8641,8 @@ type ServerSkill struct { // Optional freeform hint describing the skill's expected arguments, from the // `argument-hint` frontmatter field ArgumentHint *string `json:"argumentHint,omitempty"` + // Canonical slash command name used to invoke the skill, without the leading '/' + CommandName *string `json:"commandName,omitempty"` // Description of what the skill does Description string `json:"description"` // Whether the skill is currently enabled (based on global config) @@ -8717,17 +8790,65 @@ type SessionContext struct { // Experimental: SessionContextAttribution is part of an experimental API and may change or // be removed. type SessionContextAttribution struct { + // Output reserve plus the tokens past the buffer-exhaustion blocking threshold. Mirrors + // `SessionContextInfo.bufferTokens`. + BufferTokens int64 `json:"bufferTokens"` + // The six normalized `/context` header buckets, computed from the same tokenization as + // `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` + // describe window capacity rather than occupied context, so the values do not sum to + // `totalTokens`. + Categories SessionContextAttributionCategories `json:"categories"` // Successful compaction history for the session. Compactions SessionContextAttributionCompactions `json:"compactions"` + // Token count at which background compaction starts. Mirrors + // `SessionContextInfo.compactionThreshold`. + CompactionThreshold int64 `json:"compactionThreshold"` // Flat list of per-source attribution entries. Group by `kind` and render unrecognized // kinds generically. Nesting and rollups are expressed via `parentId`. Entries []SessionContextAttributionEntriesItem `json:"entries"` + // Prompt limit plus the model's output reserve: the full context window + // `categories.freeSpace` and `categories.buffer` are measured against. Mirrors + // `SessionContextInfo.limit`. + Limit int64 `json:"limit"` + // The concrete model id the entire breakdown was tokenized against (feeds the per-model + // token multiplier). Under `Auto` (Free/Student) this is the resolved model, not the + // literal `auto` sentinel, so totals are not undercounted. A single-model approximation of + // a potentially multi-model Auto session. + ModelID string `json:"modelId"` + // How `modelId` was chosen. Not a closed set — tolerate unknown values. Known values today: + // `autoResolved` (the model Auto resolved to), `selected` (the user's explicitly selected + // model), `default` (a fallback before any model is known). + ModelSource string `json:"modelSource"` + // Maximum prompt tokens the resolved model accepts — the denominator for a `##k/###k` + // context-usage display. Mirrors `SessionContextInfo.promptTokenLimit`. + PromptTokenLimit int64 `json:"promptTokenLimit"` // Total token count of the current context window the entries are measured against (system // message + conversation messages + tool definitions — the same total reported by // /context). Divide an entry's `tokens` by this to derive its share. TotalTokens int64 `json:"totalTokens"` } +// The six normalized `/context` header buckets, computed from the same tokenization as +// `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` +// describe window capacity rather than occupied context, so the values do not sum to +// `totalTokens`. +type SessionContextAttributionCategories struct { + // Output reserve plus post-blocking-threshold buffer. + Buffer int64 `json:"buffer"` + // Custom-instructions tokens (0 when none are configured). + CustomInstructions int64 `json:"customInstructions"` + // Remaining unused window capacity (clamped at 0). + FreeSpace int64 `json:"freeSpace"` + // MCP tool-definition tokens. + MCPTools int64 `json:"mcpTools"` + // Conversation (user/assistant/tool) message tokens. + Messages int64 `json:"messages"` + // System prompt tokens, excluding custom instructions. + SystemPrompt int64 `json:"systemPrompt"` + // Non-MCP tool-definition tokens. + SystemTools int64 `json:"systemTools"` +} + // Successful compaction history for the session. type SessionContextAttributionCompactions struct { // Number of successful compactions in this session. @@ -9424,6 +9545,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. @@ -9615,6 +9759,9 @@ type SessionOpenOptions struct { DetachedFromSpawningParentSessionID *string `json:"detachedFromSpawningParentSessionId,omitempty"` // Instruction source IDs disabled for this session. DisabledInstructionSources []string `json:"disabledInstructionSources,omitzero"` + // MCP server names disabled for this session. Disabled servers are not started or + // authenticated on create or cold resume. + DisabledMCPServers []string `json:"disabledMcpServers,omitzero"` // Skill IDs disabled for this session. DisabledSkills []string `json:"disabledSkills,omitzero"` // Experimental: enable native model citations (Anthropic models today), normalized onto the @@ -9686,6 +9833,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"` @@ -10943,6 +11093,8 @@ type Skill struct { // Optional freeform hint describing the skill's expected arguments, from the // `argument-hint` frontmatter field ArgumentHint *string `json:"argumentHint,omitempty"` + // Canonical slash command name used to invoke the skill, without the leading '/' + CommandName *string `json:"commandName,omitempty"` // Description of what the skill does Description string `json:"description"` // Whether the skill is currently enabled @@ -13123,6 +13275,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. @@ -14111,6 +14271,53 @@ const ( PermissionDecisionKindUserNotAvailable PermissionDecisionKind = "user-not-available" ) +// Disposition of a permission request as observed by the responding client. +// Experimental: PermissionDecisionOutcome is part of an experimental API and may change or +// be removed. +type PermissionDecisionOutcome string + +const ( + // The request was approved automatically without a new human decision. + PermissionDecisionOutcomeAutoApproved PermissionDecisionOutcome = "auto_approved" + // The request was denied without an interactive user decision; source records why. + PermissionDecisionOutcomeAutopilotDenied PermissionDecisionOutcome = "autopilot_denied" + // The response came from an interactive user prompt. + PermissionDecisionOutcomePromptedUser PermissionDecisionOutcome = "prompted_user" +) + +// Controlled reason or actor responsible for a permission response. +// Experimental: PermissionDecisionSource is part of an experimental API and may change or +// be removed. +type PermissionDecisionSource string + +const ( + // The host applied a standing policy or override rather than a judge recommendation or + // human decision. + PermissionDecisionSourceHostPolicy PermissionDecisionSource = "host_policy" + // A human supplied the response through an interactive prompt. + PermissionDecisionSourceHumanResponse PermissionDecisionSource = "human_response" + // The response followed the auto-approval judge recommendation. + PermissionDecisionSourceJudgeRecommendation PermissionDecisionSource = "judge_recommendation" + // The host denied the request because no interactive user response was available. + PermissionDecisionSourceUnattendedFallback PermissionDecisionSource = "unattended_fallback" +) + +// Client surface that submitted a permission response. +// Experimental: PermissionDecisionSurface is part of an experimental API and may change or +// be removed. +type PermissionDecisionSurface string + +const ( + // The Copilot App client. + PermissionDecisionSurfaceCopilotApp PermissionDecisionSurface = "copilot_app" + // The non-interactive Copilot CLI prompt mode. + PermissionDecisionSurfacePromptMode PermissionDecisionSurface = "prompt_mode" + // A generic Copilot SDK client. + PermissionDecisionSurfaceSDK PermissionDecisionSurface = "sdk" + // The interactive Copilot CLI terminal UI. + PermissionDecisionSurfaceTui PermissionDecisionSurface = "tui" +) + // Whether the location is a git repo or directory // Experimental: PermissionLocationType is part of an experimental API and may change or be // removed. @@ -15605,6 +15812,29 @@ func (a *ServerLlmInferenceAPI) SetProvider(ctx context.Context) (*LlmInferenceS return &result, nil } +// Experimental: ServerManagedSettingsAPI contains experimental APIs that may change or be +// removed. +type ServerManagedSettingsAPI serverAPI + +// Read discovers device-managed settings from production MDM and managed-file sources, +// validates them against the runtime-owned managed-settings schema, and returns the +// canonical JSON without requiring a session. +// +// RPC method: managedSettings.read. +// +// Returns: Validated device-managed settings discovered before a session exists. +func (a *ServerManagedSettingsAPI) Read(ctx context.Context) (*ManagedSettingsReadResult, error) { + raw, err := a.client.Request(ctx, "managedSettings.read", nil) + if err != nil { + return nil, err + } + var result ManagedSettingsReadResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // Experimental: ServerMCPAPI contains experimental APIs that may change or be removed. type ServerMCPAPI serverAPI @@ -16747,23 +16977,24 @@ type ServerRPC struct { // Reuse a single struct instead of allocating one for each service on the heap. common serverAPI - Account *ServerAccountAPI - AgentRegistry *ServerAgentRegistryAPI - Agents *ServerAgentsAPI - Commands *ServerCommandsAPI - Extensions *ServerExtensionsAPI - Instructions *ServerInstructionsAPI - LlmInference *ServerLlmInferenceAPI - MCP *ServerMCPAPI - Models *ServerModelsAPI - Plugins *ServerPluginsAPI - Runtime *ServerRuntimeAPI - Secrets *ServerSecretsAPI - SessionFS *ServerSessionFSAPI - Sessions *ServerSessionsAPI - Skills *ServerSkillsAPI - Tools *ServerToolsAPI - User *ServerUserAPI + Account *ServerAccountAPI + AgentRegistry *ServerAgentRegistryAPI + Agents *ServerAgentsAPI + Commands *ServerCommandsAPI + Extensions *ServerExtensionsAPI + Instructions *ServerInstructionsAPI + LlmInference *ServerLlmInferenceAPI + ManagedSettings *ServerManagedSettingsAPI + MCP *ServerMCPAPI + Models *ServerModelsAPI + Plugins *ServerPluginsAPI + Runtime *ServerRuntimeAPI + Secrets *ServerSecretsAPI + SessionFS *ServerSessionFSAPI + Sessions *ServerSessionsAPI + Skills *ServerSkillsAPI + Tools *ServerToolsAPI + User *ServerUserAPI } // Ping checks server responsiveness and returns protocol information. @@ -16787,6 +17018,25 @@ func (a *ServerRPC) Ping(ctx context.Context, params *PingRequest) (*PingResult, return &result, nil } +// RegisterExtensionLaunchProvider registers the calling SDK client as the per-entrypoint +// extension launch provider. Call before creating any sessions. When omitted, the runtime +// temporarily falls back to its built-in Node launcher for backward compatibility. +// +// RPC method: registerExtensionLaunchProvider. +// Experimental: RegisterExtensionLaunchProvider is an experimental API and may change or be +// removed in future versions. +func (a *ServerRPC) RegisterExtensionLaunchProvider(ctx context.Context) (*RegisterExtensionLaunchProviderResult, error) { + raw, err := a.common.client.Request(ctx, "registerExtensionLaunchProvider", nil) + if err != nil { + return nil, err + } + var result RegisterExtensionLaunchProviderResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + func NewServerRPC(client *jsonrpc2.Client) *ServerRPC { r := &ServerRPC{} r.common = serverAPI{client: client} @@ -16797,6 +17047,7 @@ func NewServerRPC(client *jsonrpc2.Client) *ServerRPC { r.Extensions = (*ServerExtensionsAPI)(&r.common) r.Instructions = (*ServerInstructionsAPI)(&r.common) r.LlmInference = (*ServerLlmInferenceAPI)(&r.common) + r.ManagedSettings = (*ServerManagedSettingsAPI)(&r.common) r.MCP = (*ServerMCPAPI)(&r.common) r.Models = (*ServerModelsAPI)(&r.common) r.Plugins = (*ServerPluginsAPI)(&r.common) @@ -19966,6 +20217,9 @@ func (a *PermissionsAPI) GetAllowAll(ctx context.Context) (*AllowAllPermissionSt func (a *PermissionsAPI) HandlePendingPermissionRequest(ctx context.Context, params *PermissionDecisionRequest) (*PermissionRequestResult, error) { req := map[string]any{"sessionId": a.sessionID} if params != nil { + if params.DecisionContext != nil { + req["decisionContext"] = *params.DecisionContext + } req["requestId"] = params.RequestID req["result"] = params.Result } @@ -23936,6 +24190,23 @@ func RegisterClientSessionAPIHandlers(client *jsonrpc2.Client, getHandlers func( }) } +// Experimental: ExtensionLaunchProviderHandler contains experimental APIs that may change +// or be removed. +type ExtensionLaunchProviderHandler interface { + // Resolve asks the registered SDK client to resolve an opaque process launch profile for + // one discovered extension entrypoint immediately before launch or reload. The provider + // must respond within 15 seconds. + // + // RPC method: extensionLaunchProvider.resolve. + // + // Parameters: A discovered extension entrypoint that the registered integrator may classify + // and resolve to an opaque launch profile. + // + // Returns: The launch profile for a supported entrypoint. Omit launch when the provider + // does not support the entrypoint. + Resolve(request *ExtensionLaunchProviderResolveRequest) (*ExtensionLaunchProviderResolveResult, error) +} + // Experimental: GitHubTelemetryHandler contains experimental APIs that may change or be // removed. type GitHubTelemetryHandler interface { @@ -24002,9 +24273,10 @@ type LlmInferenceHandler interface { // Unlike client-session handlers these carry no implicit session id dispatch // key; a single set of handlers serves the entire connection. type ClientGlobalAPIHandlers struct { - GitHubTelemetry GitHubTelemetryHandler - Hooks HooksHandler - LlmInference LlmInferenceHandler + ExtensionLaunchProvider ExtensionLaunchProviderHandler + GitHubTelemetry GitHubTelemetryHandler + Hooks HooksHandler + LlmInference LlmInferenceHandler } func clientGlobalHandlerError(err error) *jsonrpc2.Error { @@ -24021,6 +24293,24 @@ func clientGlobalHandlerError(err error) *jsonrpc2.Error { // RegisterClientGlobalAPIHandlers registers handlers for server-to-client client-global API // calls. func RegisterClientGlobalAPIHandlers(client *jsonrpc2.Client, handlers *ClientGlobalAPIHandlers) { + client.SetRequestHandler("extensionLaunchProvider.resolve", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + var request ExtensionLaunchProviderResolveRequest + if err := json.Unmarshal(params, &request); err != nil { + return nil, &jsonrpc2.Error{Code: -32602, Message: fmt.Sprintf("Invalid params: %v", err)} + } + if handlers == nil || handlers.ExtensionLaunchProvider == nil { + return nil, &jsonrpc2.Error{Code: -32603, Message: "No extensionLaunchProvider client-global handler registered"} + } + result, err := handlers.ExtensionLaunchProvider.Resolve(&request) + if err != nil { + return nil, clientGlobalHandlerError(err) + } + raw, err := json.Marshal(result) + if err != nil { + return nil, &jsonrpc2.Error{Code: -32603, Message: fmt.Sprintf("Failed to marshal response: %v", err)} + } + return raw, nil + }) client.SetRequestHandler("gitHubTelemetry.event", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { var request GitHubTelemetryNotification if err := json.Unmarshal(params, &request); err != nil { diff --git a/go/rpc/zrpc_encoding.go b/go/rpc/zrpc_encoding.go index fdc76729b5..8b69c28c2a 100644 --- a/go/rpc/zrpc_encoding.go +++ b/go/rpc/zrpc_encoding.go @@ -2683,13 +2683,15 @@ func (r PermissionDecisionUserNotAvailable) MarshalJSON() ([]byte, error) { func (r *PermissionDecisionRequest) UnmarshalJSON(data []byte) error { type rawPermissionDecisionRequest struct { - RequestID string `json:"requestId"` - Result json.RawMessage `json:"result"` + DecisionContext *PermissionDecisionContext `json:"decisionContext,omitempty"` + RequestID string `json:"requestId"` + Result json.RawMessage `json:"result"` } var raw rawPermissionDecisionRequest if err := json.Unmarshal(data, &raw); err != nil { return err } + r.DecisionContext = raw.DecisionContext r.RequestID = raw.RequestID if raw.Result != nil { value, err := unmarshalPermissionDecision(raw.Result) @@ -3711,6 +3713,7 @@ func (r *SessionOpenOptions) UnmarshalJSON(data []byte) error { DetachedFromSpawningParentEngagementID *string `json:"detachedFromSpawningParentEngagementId,omitempty"` DetachedFromSpawningParentSessionID *string `json:"detachedFromSpawningParentSessionId,omitempty"` DisabledInstructionSources []string `json:"disabledInstructionSources,omitzero"` + DisabledMCPServers []string `json:"disabledMcpServers,omitzero"` DisabledSkills []string `json:"disabledSkills,omitzero"` EnableCitations *bool `json:"enableCitations,omitempty"` EnableFileChangeTracking *bool `json:"enableFileChangeTracking,omitempty"` @@ -3731,6 +3734,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"` @@ -3787,6 +3791,7 @@ func (r *SessionOpenOptions) UnmarshalJSON(data []byte) error { r.DetachedFromSpawningParentEngagementID = raw.DetachedFromSpawningParentEngagementID r.DetachedFromSpawningParentSessionID = raw.DetachedFromSpawningParentSessionID r.DisabledInstructionSources = raw.DisabledInstructionSources + r.DisabledMCPServers = raw.DisabledMCPServers r.DisabledSkills = raw.DisabledSkills r.EnableCitations = raw.EnableCitations r.EnableFileChangeTracking = raw.EnableFileChangeTracking @@ -3807,6 +3812,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/rpc/zsession_events.go b/go/rpc/zsession_events.go index 26e7b5f726..0034af6a7f 100644 --- a/go/rpc/zsession_events.go +++ b/go/rpc/zsession_events.go @@ -649,24 +649,26 @@ func (*PendingMessagesModifiedData) Type() SessionEventType { return SessionEventTypePendingMessagesModified } -// Enterprise managed-settings resolution: the effective managed settings the session applied and where they came from, so SDK clients can show users what is enterprise-managed and by which authority. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted; for a session-independent pull, use the SDK `getManagedSettings()` API, which returns the identical payload. Managed settings have a single authoritative source, so the highest-authority present layer (server > device) wins wholesale; `bypassPermissionsDisabled` is deny-wins across layers. Marked experimental while the managed-settings surface stabilizes. +// Enterprise managed-settings resolution: the effective managed settings the session applied and which channels contributed, so SDK clients can show users what is enterprise-managed. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted. Device values take precedence over server values per ordinary key, while permissions compose restrictively across device, server, and SDK-client layers. The account-scoped `getManagedSettings()` API does not include session-local client injection. Marked experimental while the managed-settings surface stabilizes. // Experimental: SessionManagedSettingsResolvedData is part of an experimental API and may change or be removed. type SessionManagedSettingsResolvedData struct { // Whether enterprise policy disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers, and forced on when `failClosed` is true. BypassPermissionsDisabled bool `json:"bypassPermissionsDisabled"` - // Whether the device (MDM/plist/registry/file) managed-settings layer was present + // Whether a session-local permissions layer injected by the SDK host was present + ClientManaged *bool `json:"clientManaged,omitempty"` + // Whether an actual device MDM/plist/registry/file managed-settings layer was present DeviceManaged bool `json:"deviceManaged"` // Whether managed policy could not be determined (e.g. a failed server fetch) and the session fell back to the fail-closed restriction. When true, restrictions such as disabling bypass-permissions are enforced even though `settings` may be absent. FailClosed bool `json:"failClosed"` // The setting keys under enterprise management in the effective managed settings (e.g. `model`, `enabledPlugins`, `permissions`). Empty when no managed settings are in force. ManagedKeys []string `json:"managedKeys"` - // Whether server and device each supplied a permission allowlist, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. + // Whether at least two managed sources supplied permission allowlists, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. PermissionsAllowIntersected *bool `json:"permissionsAllowIntersected,omitempty"` // Whether the server (account/org) managed-settings layer was present ServerManaged bool `json:"serverManaged"` // The effective (resolved) managed settings values, so clients can render exactly what is enforced. Absent when no managed policy is in force. Settings any `json:"settings,omitempty"` - // Which channel supplied the effective managed settings (the winning layer), or `none` when no policy is in force + // Channel summary: `server`, `device`, or `client` when exactly one channel contributed; `mixed` when multiple channels contributed; otherwise `none`. Consult the per-channel booleans for exact provenance. Source ManagedSettingsResolvedSource `json:"source"` } @@ -3571,6 +3573,8 @@ type ShutdownTokenDetail struct { type SkillsLoadedSkill struct { // Optional freeform hint describing the skill's expected arguments, from the `argument-hint` frontmatter field ArgumentHint *string `json:"argumentHint,omitempty"` + // Canonical slash command name used to invoke the skill, without the leading '/' + CommandName *string `json:"commandName,omitempty"` // Description of what the skill does Description string `json:"description"` // Whether the skill is currently enabled @@ -4354,15 +4358,19 @@ const ( ManagedSettingsEnforcedEscalationUnrestrictedURLs ManagedSettingsEnforcedEscalation = "unrestricted_urls" ) -// Which channel supplied the effective enterprise managed settings (highest-authority present layer wins wholesale) +// Summary of which managed-settings channels contributed to the effective session policy. Use the per-channel booleans for exact provenance. type ManagedSettingsResolvedSource string const ( - // Device-level MDM policy discovered from plist/registry/file (lower authority). + // Only session-local SDK-host injection contributed. + ManagedSettingsResolvedSourceClient ManagedSettingsResolvedSource = "client" + // Only the device MDM/plist/registry/file channel contributed. ManagedSettingsResolvedSourceDevice ManagedSettingsResolvedSource = "device" - // No managed policy is in force (no layer contributed). + // More than one channel contributed. Ordinary keys resolve device over server per key, while permissions compose restrictively across all present layers. + ManagedSettingsResolvedSourceMixed ManagedSettingsResolvedSource = "mixed" + // No managed policy is in force (no channel contributed). ManagedSettingsResolvedSourceNone ManagedSettingsResolvedSource = "none" - // Account/org policy self-fetched from the GitHub managed-settings endpoint (higher authority). + // Only the server/account channel contributed. ManagedSettingsResolvedSourceServer ManagedSettingsResolvedSource = "server" ) diff --git a/go/session_event_serialization_test.go b/go/session_event_serialization_test.go index bd47fdfbe2..ee9258b225 100644 --- a/go/session_event_serialization_test.go +++ b/go/session_event_serialization_test.go @@ -189,3 +189,69 @@ func TestRawSessionEventDataWithNilRawMarshalsAsNull(t *testing.T) { t.Fatalf("expected missing raw data to marshal as null, got %v", serialized["data"]) } } + +func TestManagedSettingsResolvedProvenanceRoundTrips(t *testing.T) { + sources := []ManagedSettingsResolvedSource{ + ManagedSettingsResolvedSourceServer, + ManagedSettingsResolvedSourceDevice, + ManagedSettingsResolvedSourceClient, + ManagedSettingsResolvedSourceMixed, + ManagedSettingsResolvedSourceNone, + } + expectedSources := []string{"server", "device", "client", "mixed", "none"} + for i, source := range sources { + if string(source) != expectedSources[i] { + t.Fatalf("expected source %q, got %q", expectedSources[i], source) + } + } + + clientManaged := true + resolved := SessionManagedSettingsResolvedData{ + BypassPermissionsDisabled: true, + ClientManaged: &clientManaged, + DeviceManaged: false, + FailClosed: false, + ManagedKeys: []string{"permissions"}, + ServerManaged: false, + Source: ManagedSettingsResolvedSourceClient, + } + data, err := json.Marshal(resolved) + if err != nil { + t.Fatalf("failed to marshal managed settings resolution: %v", err) + } + + var serialized map[string]any + if err := json.Unmarshal(data, &serialized); err != nil { + t.Fatalf("failed to inspect managed settings resolution: %v", err) + } + if serialized["source"] != "client" || serialized["clientManaged"] != true { + t.Fatalf("expected client provenance, got %v", serialized) + } + + var roundTripped SessionManagedSettingsResolvedData + if err := json.Unmarshal(data, &roundTripped); err != nil { + t.Fatalf("failed to round-trip managed settings resolution: %v", err) + } + if roundTripped.Source != ManagedSettingsResolvedSourceClient || + roundTripped.ClientManaged == nil || + !*roundTripped.ClientManaged { + t.Fatalf("expected client provenance to round-trip, got %#v", roundTripped) + } + + resolved.Source = ManagedSettingsResolvedSourceMixed + resolved.ClientManaged = nil + data, err = json.Marshal(resolved) + if err != nil { + t.Fatalf("failed to marshal mixed managed settings resolution: %v", err) + } + serialized = nil + if err := json.Unmarshal(data, &serialized); err != nil { + t.Fatalf("failed to inspect mixed managed settings resolution: %v", err) + } + if serialized["source"] != "mixed" { + t.Fatalf("expected mixed provenance, got %v", serialized["source"]) + } + if _, ok := serialized["clientManaged"]; ok { + t.Fatalf("expected absent clientManaged to be omitted, got %v", serialized) + } +} diff --git a/go/types.go b/go/types.go index 6669564bf4..6d6a877d30 100644 --- a/go/types.go +++ b/go/types.go @@ -1498,6 +1498,51 @@ 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"` +} + +// DisableBypassPermissionsMode is the managed bypass-permissions policy. +type DisableBypassPermissionsMode = rpc.DisableBypassPermissionsMode + +const ( + // DisableBypassPermissionsModeDisable turns off bypass-permissions mode. + DisableBypassPermissionsModeDisable = rpc.DisableBypassPermissionsModeDisable +) + +// 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 DisableBypassPermissionsMode `json:"disableBypassPermissionsMode,omitempty"` + // Deny lists operations that must always be denied. Unioned across layers. + Deny []string `json:"deny,omitzero"` + // Ask lists operations that must prompt for approval. Unioned across layers. + Ask []string `json:"ask,omitzero"` + // 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,omitzero"` } // ToolDefer controls whether a tool may be deferred (loaded lazily via tool @@ -1961,6 +2006,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 @@ -2423,6 +2473,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"` } @@ -2519,6 +2570,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/go/zsession_events.go b/go/zsession_events.go index fbc1002774..48ad42849b 100644 --- a/go/zsession_events.go +++ b/go/zsession_events.go @@ -469,7 +469,9 @@ const ( ManagedSettingsEnforcedEscalationAutoApproval = rpc.ManagedSettingsEnforcedEscalationAutoApproval ManagedSettingsEnforcedEscalationUnrestrictedPaths = rpc.ManagedSettingsEnforcedEscalationUnrestrictedPaths ManagedSettingsEnforcedEscalationUnrestrictedURLs = rpc.ManagedSettingsEnforcedEscalationUnrestrictedURLs + ManagedSettingsResolvedSourceClient = rpc.ManagedSettingsResolvedSourceClient ManagedSettingsResolvedSourceDevice = rpc.ManagedSettingsResolvedSourceDevice + ManagedSettingsResolvedSourceMixed = rpc.ManagedSettingsResolvedSourceMixed ManagedSettingsResolvedSourceNone = rpc.ManagedSettingsResolvedSourceNone ManagedSettingsResolvedSourceServer = rpc.ManagedSettingsResolvedSourceServer MCPHeadersRefreshCompletedOutcomeHeaders = rpc.MCPHeadersRefreshCompletedOutcomeHeaders diff --git a/java/pom.xml b/java/pom.xml index fcdd9cc21e..8080e2c49d 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -88,7 +88,7 @@ DO NOT EDIT MANUALLY. Updated by the update-copilot-dependency workflow. --> - ^1.0.78 + ^1.0.79-5 diff --git a/java/scripts/codegen/package-lock.json b/java/scripts/codegen/package-lock.json index c4a159b6e4..0f0d71b4e1 100644 --- a/java/scripts/codegen/package-lock.json +++ b/java/scripts/codegen/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "copilot-sdk-java-codegen", "dependencies": { - "@github/copilot": "^1.0.78", + "@github/copilot": "^1.0.79-5", "json-schema": "^0.4.0", "tsx": "^4.23.1" } @@ -428,9 +428,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.78.tgz", - "integrity": "sha512-jn+8HLZC3R7d6K1/1g9L1iWNKzBVS3JdVcx40r3aWyS5r+MLV1OPNp0fo5OfRMCDIm3NmEaaoqypi9sQkCXuiQ==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.79-5.tgz", + "integrity": "sha512-gQj87QGcWQpAx2YcBcBZ15q5ITA9y9aLDu0mIZb2daIRQqIK8VKtmY70G5d271eqrztTqd2b3Nu0mlB64E5ufg==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "detect-libc": "^2.1.2" @@ -439,20 +439,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.78", - "@github/copilot-darwin-x64": "1.0.78", - "@github/copilot-linux-arm64": "1.0.78", - "@github/copilot-linux-x64": "1.0.78", - "@github/copilot-linuxmusl-arm64": "1.0.78", - "@github/copilot-linuxmusl-x64": "1.0.78", - "@github/copilot-win32-arm64": "1.0.78", - "@github/copilot-win32-x64": "1.0.78" + "@github/copilot-darwin-arm64": "1.0.79-5", + "@github/copilot-darwin-x64": "1.0.79-5", + "@github/copilot-linux-arm64": "1.0.79-5", + "@github/copilot-linux-x64": "1.0.79-5", + "@github/copilot-linuxmusl-arm64": "1.0.79-5", + "@github/copilot-linuxmusl-x64": "1.0.79-5", + "@github/copilot-win32-arm64": "1.0.79-5", + "@github/copilot-win32-x64": "1.0.79-5" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.78.tgz", - "integrity": "sha512-P11+VyWg8ad0WlywGtO2d7AxqTLJv4hkUicFg6Ycth5lfk00aCu/74YOOZSPO6C2bBBJhAza7oAdmauM6KEojw==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.79-5.tgz", + "integrity": "sha512-pR/ZrznLn6oOUcIPPgzaOOHKDLmeznV1yHwzkmTrqJVwlFgfFYeqBGTD3YwAqSmGAtXdDk+16o3j0mCg0m3+9A==", "cpu": [ "arm64" ], @@ -466,9 +466,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.78.tgz", - "integrity": "sha512-stimP3WDFs2GU8nJzTJbtRpZViV4bsf80yg7QrFq+G4RISQ3Nihg/3/H0U6UQF1+txMJ/Ohmb5RFYxSw1Hj2sw==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.79-5.tgz", + "integrity": "sha512-L828i0YUiI7IAACsmMnKg7LKrgzu5KqgOyA72BmxPmIYUpRpIxYt8QU8oGeej52de+I6zRYUCSWkLAkKLa6oFw==", "cpu": [ "x64" ], @@ -482,12 +482,15 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.78.tgz", - "integrity": "sha512-K31PRKGTm252V1Lof7ypjg283R2QSm3BgoCvZfX2taos4wqC3SaTozSQKwW3dgrAx7A3G3SGEoilVCNqfigdZA==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.79-5.tgz", + "integrity": "sha512-r3GT8kHGOhxbf+QaHp4vOQnJTnrreYOM+eTkgyiVrDe9GR5RKOO7XHjr8F9ob29UP4qpci/2LpgcYIJ9yiszgg==", "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "SEE LICENSE IN LICENSE.md", "optional": true, "os": [ @@ -498,12 +501,15 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.78.tgz", - "integrity": "sha512-QK3oMtAn9dIv+1u1kx0xNpZNtZxdI+uZVIyLl7myp+Oh2Uj8BLagVv6a7uP0cDphO3TgfIdlvpepCe5MIcx0fw==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.79-5.tgz", + "integrity": "sha512-as7EcVDOC7TtoO9+bX8NrQsACPdPKg6t0HTaAaa7oRAftalGX8ZGhGouBOL6v13vpR2xtLaRRaP3YMwo//XQ9A==", "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "SEE LICENSE IN LICENSE.md", "optional": true, "os": [ @@ -514,12 +520,15 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.78.tgz", - "integrity": "sha512-F/0cTMsz6ug4yiXn3RKaCAMsLR261U5Njb6G9Y/HeAI7ES/tKEo2t5SHuvgXaIH4mYiZsRvfDKdX7c0WgBX/Jg==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.79-5.tgz", + "integrity": "sha512-znO0FerQz6kUj46GqjGACR+ernRrAL3iymYo3ipGdf/E71fZnhyZ4BReh0ajRk6b2O5b7dbItk6K0K3Vr/Wn4g==", "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "SEE LICENSE IN LICENSE.md", "optional": true, "os": [ @@ -530,12 +539,15 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.78.tgz", - "integrity": "sha512-YMaJaeBGbArGAFYel+yFaFW/0rFgh0Oqki2f2mUtlonTX/xHr8EB4+mTnMJkHYMFy4gOTC3OtSEEe1NaW/cBXQ==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.79-5.tgz", + "integrity": "sha512-6hDWKHNEyMwvsPwHYp+Yz3QIojFfheVzhpgz+zZde1uLSf7okiV3qP5Uyuwixv4qoissxnD+2o8eL6VAJl3fsg==", "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "SEE LICENSE IN LICENSE.md", "optional": true, "os": [ @@ -546,9 +558,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.78.tgz", - "integrity": "sha512-ktDkFXaaecEKD3hpM6ydM9lKOdoCfsQsXCmzLzE7DCmSpbbMCdfPfWfZ7MOclmKmpZ5/MNfr4U2l8CUqGerzYA==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.79-5.tgz", + "integrity": "sha512-kvt3YrwZ4/VhAMsUau/T1SCzg4e5/ki7gBF2bxJqszh3B+7DVDXZB/jaUPexLUAbGhxSAzB9c02X1tgzt3m4nw==", "cpu": [ "arm64" ], @@ -562,9 +574,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.78.tgz", - "integrity": "sha512-Gd8l2T4eqYEWlOEPd0SZznQ+YYgYrwOkE0QXodMkhCBbPdgu/uTzb7mnISWwnVAgqs7pONdF1GOpHkTo+ay8CQ==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.79-5.tgz", + "integrity": "sha512-e+5DJhN92vMvqrK0O4hV4KTCe5AP9F75K3PbGlSzEAjHhaXYxvPcx3hsL85SEMAoBrXItx64kHQMQm33Jo9K+Q==", "cpu": [ "x64" ], diff --git a/java/scripts/codegen/package.json b/java/scripts/codegen/package.json index 7547468e84..3e0e5af6ab 100644 --- a/java/scripts/codegen/package.json +++ b/java/scripts/codegen/package.json @@ -7,7 +7,7 @@ "generate:java": "tsx java.ts" }, "dependencies": { - "@github/copilot": "^1.0.78", + "@github/copilot": "^1.0.79-5", "json-schema": "^0.4.0", "tsx": "^4.23.1" } diff --git a/java/src/generated/java/com/github/copilot/generated/ManagedSettingsResolvedSource.java b/java/src/generated/java/com/github/copilot/generated/ManagedSettingsResolvedSource.java index 1ffa100635..32386f898a 100644 --- a/java/src/generated/java/com/github/copilot/generated/ManagedSettingsResolvedSource.java +++ b/java/src/generated/java/com/github/copilot/generated/ManagedSettingsResolvedSource.java @@ -10,7 +10,7 @@ import javax.annotation.processing.Generated; /** - * Which channel supplied the effective enterprise managed settings (highest-authority present layer wins wholesale) + * Summary of which managed-settings channels contributed to the effective session policy. Use the per-channel booleans for exact provenance. * * @since 1.0.0 */ @@ -20,6 +20,10 @@ public enum ManagedSettingsResolvedSource { SERVER("server"), /** The {@code device} variant. */ DEVICE("device"), + /** The {@code client} variant. */ + CLIENT("client"), + /** The {@code mixed} variant. */ + MIXED("mixed"), /** The {@code none} variant. */ NONE("none"); diff --git a/java/src/generated/java/com/github/copilot/generated/SessionManagedSettingsResolvedEvent.java b/java/src/generated/java/com/github/copilot/generated/SessionManagedSettingsResolvedEvent.java index 7c7a4df738..f935f44627 100644 --- a/java/src/generated/java/com/github/copilot/generated/SessionManagedSettingsResolvedEvent.java +++ b/java/src/generated/java/com/github/copilot/generated/SessionManagedSettingsResolvedEvent.java @@ -14,7 +14,7 @@ import javax.annotation.processing.Generated; /** - * Session event "session.managed_settings_resolved". Enterprise managed-settings resolution: the effective managed settings the session applied and where they came from, so SDK clients can show users what is enterprise-managed and by which authority. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted; for a session-independent pull, use the SDK `getManagedSettings()` API, which returns the identical payload. Managed settings have a single authoritative source, so the highest-authority present layer (server > device) wins wholesale; `bypassPermissionsDisabled` is deny-wins across layers. Marked experimental while the managed-settings surface stabilizes. + * Session event "session.managed_settings_resolved". Enterprise managed-settings resolution: the effective managed settings the session applied and which channels contributed, so SDK clients can show users what is enterprise-managed. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted. Device values take precedence over server values per ordinary key, while permissions compose restrictively across device, server, and SDK-client layers. The account-scoped `getManagedSettings()` API does not include session-local client injection. Marked experimental while the managed-settings surface stabilizes. * @since 1.0.0 */ @JsonIgnoreProperties(ignoreUnknown = true) @@ -35,17 +35,19 @@ public final class SessionManagedSettingsResolvedEvent extends SessionEvent { @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record SessionManagedSettingsResolvedEventData( - /** Which channel supplied the effective managed settings (the winning layer), or `none` when no policy is in force */ + /** Channel summary: `server`, `device`, or `client` when exactly one channel contributed; `mixed` when multiple channels contributed; otherwise `none`. Consult the per-channel booleans for exact provenance. */ @JsonProperty("source") ManagedSettingsResolvedSource source, /** Whether the server (account/org) managed-settings layer was present */ @JsonProperty("serverManaged") Boolean serverManaged, - /** Whether the device (MDM/plist/registry/file) managed-settings layer was present */ + /** Whether an actual device MDM/plist/registry/file managed-settings layer was present */ @JsonProperty("deviceManaged") Boolean deviceManaged, + /** Whether a session-local permissions layer injected by the SDK host was present */ + @JsonProperty("clientManaged") Boolean clientManaged, /** Whether managed policy could not be determined (e.g. a failed server fetch) and the session fell back to the fail-closed restriction. When true, restrictions such as disabling bypass-permissions are enforced even though `settings` may be absent. */ @JsonProperty("failClosed") Boolean failClosed, /** Whether enterprise policy disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers, and forced on when `failClosed` is true. */ @JsonProperty("bypassPermissionsDisabled") Boolean bypassPermissionsDisabled, - /** Whether server and device each supplied a permission allowlist, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. */ + /** Whether at least two managed sources supplied permission allowlists, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. */ @JsonProperty("permissionsAllowIntersected") Boolean permissionsAllowIntersected, /** The setting keys under enterprise management in the effective managed settings (e.g. `model`, `enabledPlugins`, `permissions`). Empty when no managed settings are in force. */ @JsonProperty("managedKeys") List managedKeys, diff --git a/java/src/generated/java/com/github/copilot/generated/SkillsLoadedSkill.java b/java/src/generated/java/com/github/copilot/generated/SkillsLoadedSkill.java index d3196c6bbf..932d9affe5 100644 --- a/java/src/generated/java/com/github/copilot/generated/SkillsLoadedSkill.java +++ b/java/src/generated/java/com/github/copilot/generated/SkillsLoadedSkill.java @@ -23,6 +23,8 @@ public record SkillsLoadedSkill( /** Unique identifier for the skill */ @JsonProperty("name") String name, + /** Canonical slash command name used to invoke the skill, without the leading '/' */ + @JsonProperty("commandName") String commandName, /** Description of what the skill does */ @JsonProperty("description") String description, /** Source location type (e.g., project, personal-copilot, plugin, builtin) */ diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/DisableBypassPermissionsMode.java b/java/src/generated/java/com/github/copilot/generated/rpc/DisableBypassPermissionsMode.java new file mode 100644 index 0000000000..1e6b1e7db6 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/DisableBypassPermissionsMode.java @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import javax.annotation.processing.Generated; + +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum DisableBypassPermissionsMode { + /** The {@code disable} variant. */ + DISABLE("disable"); + + private final String value; + DisableBypassPermissionsMode(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static DisableBypassPermissionsMode fromValue(String value) { + for (DisableBypassPermissionsMode v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown DisableBypassPermissionsMode value: " + value); + } +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProfile.java b/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProfile.java new file mode 100644 index 0000000000..e7590c7f9e --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProfile.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import java.util.Map; +import javax.annotation.processing.Generated; + +/** + * Opaque integrator-owned process launch profile for one extension entrypoint. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ExtensionLaunchProfile( + /** Executable used to launch the extension entrypoint. */ + @JsonProperty("executable") String executable, + /** Opaque integrator-defined arguments passed to the executable. The runtime does not append the extension entrypoint. */ + @JsonProperty("args") List args, + /** Opaque integrator-defined environment variables. Runtime-owned COPILOT_SDK_PATH, SESSION_ID, and COPILOT_EXTENSION_PARENT_PID values take precedence. */ + @JsonProperty("env") Map env +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProviderResolveRequest.java b/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProviderResolveRequest.java new file mode 100644 index 0000000000..7b520f9067 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProviderResolveRequest.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * A discovered extension entrypoint that the registered integrator may classify and resolve to an opaque launch profile. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ExtensionLaunchProviderResolveRequest( + /** Source-qualified extension identifier. */ + @JsonProperty("id") String id, + /** Human-readable extension name. */ + @JsonProperty("name") String name, + /** Absolute path to the discovered extension entrypoint. */ + @JsonProperty("modulePath") String modulePath, + /** Discovery source for the extension entrypoint. */ + @JsonProperty("source") ExtensionSource source +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProviderResolveResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProviderResolveResult.java new file mode 100644 index 0000000000..8a43ad4af3 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProviderResolveResult.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * The launch profile for a supported entrypoint. Omit launch when the provider does not support the entrypoint. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ExtensionLaunchProviderResolveResult( + /** Opaque launch profile, omitted when this provider does not support the entrypoint. */ + @JsonProperty("launch") ExtensionLaunchProfile launch +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsReadResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsReadResult.java new file mode 100644 index 0000000000..2018f62ce0 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsReadResult.java @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Validated device-managed settings discovered before a session exists. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ManagedSettingsReadResult( + /** Validated, canonical managed-settings JSON. Omitted when no managed settings were discovered or when discovered settings failed validation. */ + @JsonProperty("settingsJson") Object settingsJson, + /** Discovery or validation error text when managed settings could not be read safely. */ + @JsonProperty("errorMessage") String errorMessage +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionContext.java b/java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionContext.java new file mode 100644 index 0000000000..73934eea66 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionContext.java @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Optional informational context describing how and where the permission decision was made. This does not affect permission behavior. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record PermissionDecisionContext( + /** Disposition of the permission request as observed by the responding client. */ + @JsonProperty("outcome") PermissionDecisionOutcome outcome, + /** Controlled reason or actor responsible for the response. */ + @JsonProperty("source") PermissionDecisionSource source, + /** Client surface that submitted the response. */ + @JsonProperty("surface") PermissionDecisionSurface surface +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionOutcome.java b/java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionOutcome.java new file mode 100644 index 0000000000..d46c460a29 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionOutcome.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * Disposition of a permission request as observed by the responding client. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum PermissionDecisionOutcome { + /** The {@code auto_approved} variant. */ + AUTO_APPROVED("auto_approved"), + /** The {@code autopilot_denied} variant. */ + AUTOPILOT_DENIED("autopilot_denied"), + /** The {@code prompted_user} variant. */ + PROMPTED_USER("prompted_user"); + + private final String value; + PermissionDecisionOutcome(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static PermissionDecisionOutcome fromValue(String value) { + for (PermissionDecisionOutcome v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown PermissionDecisionOutcome value: " + value); + } +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionSource.java b/java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionSource.java new file mode 100644 index 0000000000..ee807b095f --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionSource.java @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * Controlled reason or actor responsible for a permission response. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum PermissionDecisionSource { + /** The {@code judge_recommendation} variant. */ + JUDGE_RECOMMENDATION("judge_recommendation"), + /** The {@code human_response} variant. */ + HUMAN_RESPONSE("human_response"), + /** The {@code host_policy} variant. */ + HOST_POLICY("host_policy"), + /** The {@code unattended_fallback} variant. */ + UNATTENDED_FALLBACK("unattended_fallback"); + + private final String value; + PermissionDecisionSource(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static PermissionDecisionSource fromValue(String value) { + for (PermissionDecisionSource v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown PermissionDecisionSource value: " + value); + } +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionSurface.java b/java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionSurface.java new file mode 100644 index 0000000000..2cf6348794 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionSurface.java @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * Client surface that submitted a permission response. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum PermissionDecisionSurface { + /** The {@code tui} variant. */ + TUI("tui"), + /** The {@code prompt_mode} variant. */ + PROMPT_MODE("prompt_mode"), + /** The {@code copilot_app} variant. */ + COPILOT_APP("copilot_app"), + /** The {@code sdk} variant. */ + SDK("sdk"); + + private final String value; + PermissionDecisionSurface(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static PermissionDecisionSurface fromValue(String value) { + for (PermissionDecisionSurface v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown PermissionDecisionSurface value: " + value); + } +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfig.java b/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfig.java index be4f2abe20..cfb2c23fff 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfig.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfig.java @@ -31,7 +31,7 @@ public record SandboxConfig( @JsonProperty("gitAuth") Boolean gitAuth, /** Whether to export `GH_TOKEN` so the `gh` CLI authenticates inside the sandbox without the OS keyring the sandbox blocks. Default: false (opt-in). */ @JsonProperty("ghAuth") Boolean ghAuth, - /** Whether to auto-grant read access to common developer-tool caches, registries, and toolchains in their default home locations (cargo, go, npm, Maven, and more), plus read-write access to (and, on Unix, up-front creation of) the scratch caches builds write on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so builds work without exporting CARGO_HOME/GOPATH/etc. Default: true (enabled by default; set to false to opt out). */ - @JsonProperty("allowDevToolCaches") Boolean allowDevToolCaches + /** Whether to auto-grant read access to common developer-tool caches, registries, and toolchains in their default home locations (cargo, go, npm, Maven, and more), plus read-write access to (and, on Unix, up-front creation of) the scratch caches builds write on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so builds work without extra configuration; a relocated CARGO_HOME additionally gets its Cargo lock files granted read-write. Default: true (enabled by default; set to false to opt out). */ + @JsonProperty("allowDevToolAccess") Boolean allowDevToolAccess ) { } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerManagedSettingsApi.java b/java/src/generated/java/com/github/copilot/generated/rpc/ServerManagedSettingsApi.java new file mode 100644 index 0000000000..e85b7b987a --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ServerManagedSettingsApi.java @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.github.copilot.CopilotExperimental; +import java.util.concurrent.CompletableFuture; +import javax.annotation.processing.Generated; + +/** + * API methods for the {@code managedSettings} namespace. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class ServerManagedSettingsApi { + + private final RpcCaller caller; + + /** @param caller the RPC transport function */ + ServerManagedSettingsApi(RpcCaller caller) { + this.caller = caller; + } + + /** + * Validated device-managed settings discovered before a session exists. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture read() { + return caller.invoke("managedSettings.read", java.util.Map.of(), ManagedSettingsReadResult.class); + } + +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java b/java/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java index e66f32737f..c01545a18b 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java @@ -49,6 +49,8 @@ public final class ServerRpc { public final ServerCommandsApi commands; /** API methods for the {@code user} namespace. */ public final ServerUserApi user; + /** API methods for the {@code managedSettings} namespace. */ + public final ServerManagedSettingsApi managedSettings; /** API methods for the {@code runtime} namespace. */ public final ServerRuntimeApi runtime; /** API methods for the {@code sessionFs} namespace. */ @@ -79,6 +81,7 @@ public ServerRpc(RpcCaller caller) { this.instructions = new ServerInstructionsApi(caller); this.commands = new ServerCommandsApi(caller); this.user = new ServerUserApi(caller); + this.managedSettings = new ServerManagedSettingsApi(caller); this.runtime = new ServerRuntimeApi(caller); this.sessionFs = new ServerSessionFsApi(caller); this.llmInference = new ServerLlmInferenceApi(caller); @@ -108,4 +111,15 @@ public CompletableFuture connect(ConnectParams params) { return caller.invoke("connect", params, ConnectResult.class); } + /** + * Invokes {@code registerExtensionLaunchProvider}. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture registerExtensionLaunchProvider() { + return caller.invoke("registerExtensionLaunchProvider", java.util.Map.of(), Void.class); + } + } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerSkill.java b/java/src/generated/java/com/github/copilot/generated/rpc/ServerSkill.java index 3498245262..b1d409d9d9 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/ServerSkill.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ServerSkill.java @@ -23,6 +23,8 @@ public record ServerSkill( /** Unique identifier for the skill */ @JsonProperty("name") String name, + /** Canonical slash command name used to invoke the skill, without the leading '/' */ + @JsonProperty("commandName") String commandName, /** Description of what the skill does */ @JsonProperty("description") String description, /** Source location type (e.g., project, personal-copilot, plugin, builtin) */ diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionManagedPermissions.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionManagedPermissions.java new file mode 100644 index 0000000000..79698b27c4 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionManagedPermissions.java @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Enterprise permission policy expressed with the runtime's managed permission-rule syntax. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SessionManagedPermissions( + /** When set to `disable`, prevents bypass/allow-all permission modes. */ + @JsonProperty("disableBypassPermissionsMode") DisableBypassPermissionsMode disableBypassPermissionsMode, + /** Permission rules that block matching operations. Deny has highest precedence. */ + @JsonProperty("deny") List deny, + /** Permission rules that require explicit human approval. */ + @JsonProperty("ask") List ask, + /** Permission rules that allow matching operations unless another managed source, deny, or ask rule restricts them. */ + @JsonProperty("allow") List allow +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionManagedSettings.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionManagedSettings.java new file mode 100644 index 0000000000..ddba69d8b4 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionManagedSettings.java @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Managed settings an SDK host may inject at session startup. Only permissions are accepted in this initial contract. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SessionManagedSettings( + @JsonProperty("permissions") SessionManagedPermissions permissions +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextAttributionResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextAttributionResult.java index ef7fba44d9..c27f37afb0 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextAttributionResult.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextAttributionResult.java @@ -36,12 +36,47 @@ public record SessionMetadataGetContextAttributionResult( public record SessionMetadataGetContextAttributionResultContextAttribution( /** Total token count of the current context window the entries are measured against (system message + conversation messages + tool definitions — the same total reported by /context). Divide an entry's `tokens` by this to derive its share. */ @JsonProperty("totalTokens") Long totalTokens, + /** The concrete model id the entire breakdown was tokenized against (feeds the per-model token multiplier). Under `Auto` (Free/Student) this is the resolved model, not the literal `auto` sentinel, so totals are not undercounted. A single-model approximation of a potentially multi-model Auto session. */ + @JsonProperty("modelId") String modelId, + /** How `modelId` was chosen. Not a closed set — tolerate unknown values. Known values today: `autoResolved` (the model Auto resolved to), `selected` (the user's explicitly selected model), `default` (a fallback before any model is known). */ + @JsonProperty("modelSource") String modelSource, + /** Maximum prompt tokens the resolved model accepts — the denominator for a `##k/###k` context-usage display. Mirrors `SessionContextInfo.promptTokenLimit`. */ + @JsonProperty("promptTokenLimit") Long promptTokenLimit, + /** Prompt limit plus the model's output reserve: the full context window `categories.freeSpace` and `categories.buffer` are measured against. Mirrors `SessionContextInfo.limit`. */ + @JsonProperty("limit") Long limit, + /** Output reserve plus the tokens past the buffer-exhaustion blocking threshold. Mirrors `SessionContextInfo.bufferTokens`. */ + @JsonProperty("bufferTokens") Long bufferTokens, + /** Token count at which background compaction starts. Mirrors `SessionContextInfo.compactionThreshold`. */ + @JsonProperty("compactionThreshold") Long compactionThreshold, + /** The six normalized `/context` header buckets, computed from the same tokenization as `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` describe window capacity rather than occupied context, so the values do not sum to `totalTokens`. */ + @JsonProperty("categories") SessionMetadataGetContextAttributionResultContextAttributionCategories categories, /** Flat list of per-source attribution entries. Group by `kind` and render unrecognized kinds generically. Nesting and rollups are expressed via `parentId`. */ @JsonProperty("entries") List entries, /** Successful compaction history for the session. */ @JsonProperty("compactions") SessionMetadataGetContextAttributionResultContextAttributionCompactions compactions ) { + /** The six normalized `/context` header buckets, computed from the same tokenization as `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` describe window capacity rather than occupied context, so the values do not sum to `totalTokens`. */ + @JsonIgnoreProperties(ignoreUnknown = true) + @JsonInclude(JsonInclude.Include.NON_NULL) + public record SessionMetadataGetContextAttributionResultContextAttributionCategories( + /** System prompt tokens, excluding custom instructions. */ + @JsonProperty("systemPrompt") Long systemPrompt, + /** Custom-instructions tokens (0 when none are configured). */ + @JsonProperty("customInstructions") Long customInstructions, + /** Non-MCP tool-definition tokens. */ + @JsonProperty("systemTools") Long systemTools, + /** MCP tool-definition tokens. */ + @JsonProperty("mcpTools") Long mcpTools, + /** Conversation (user/assistant/tool) message tokens. */ + @JsonProperty("messages") Long messages, + /** Remaining unused window capacity (clamped at 0). */ + @JsonProperty("freeSpace") Long freeSpace, + /** Output reserve plus post-blocking-threshold buffer. */ + @JsonProperty("buffer") Long buffer + ) { + } + @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record SessionMetadataGetContextAttributionResultContextAttributionEntriesItem( diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptions.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptions.java index a8108e8292..cf253bff00 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptions.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptions.java @@ -47,6 +47,8 @@ public record SessionOpenOptions( @JsonProperty("expAssignments") Object expAssignments, /** Opt-in: self-fetch and enforce enterprise managed settings at session bootstrap. */ @JsonProperty("enableManagedSettings") Boolean enableManagedSettings, + /** 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. */ + @JsonProperty("managedSettings") SessionManagedSettings managedSettings, /** 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. */ @JsonProperty("enableFileChangeTracking") Boolean enableFileChangeTracking, /** Feature-flag values resolved by the host. */ @@ -101,6 +103,8 @@ public record SessionOpenOptions( @JsonProperty("logInteractiveShells") Boolean logInteractiveShells, /** How MCP server environment values are interpreted. */ @JsonProperty("envValueMode") SessionOpenOptionsEnvValueMode envValueMode, + /** MCP server names disabled for this session. Disabled servers are not started or authenticated on create or cold resume. */ + @JsonProperty("disabledMcpServers") List disabledMcpServers, /** Whether to include instructions from every MCP server in the system prompt instead of only allowlisted servers. */ @JsonProperty("allowAllMcpServerInstructions") Boolean allowAllMcpServerInstructions, /** Additional directories to search for skills. */ diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsHandlePendingPermissionRequestParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsHandlePendingPermissionRequestParams.java index 075dcee26a..a4d2ba67e5 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsHandlePendingPermissionRequestParams.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsHandlePendingPermissionRequestParams.java @@ -29,6 +29,8 @@ public record SessionPermissionsHandlePendingPermissionRequestParams( /** Request ID of the pending permission request */ @JsonProperty("requestId") String requestId, /** The client's response to the pending permission prompt */ - @JsonProperty("result") Object result + @JsonProperty("result") Object result, + /** Optional informational context describing how and where this response was made. Omit it to preserve legacy behavior without attributing an origin. */ + @JsonProperty("decisionContext") PermissionDecisionContext decisionContext ) { } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/Skill.java b/java/src/generated/java/com/github/copilot/generated/rpc/Skill.java index 4ad4116871..88e7eb1169 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/Skill.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/Skill.java @@ -23,6 +23,8 @@ public record Skill( /** Unique identifier for the skill */ @JsonProperty("name") String name, + /** Canonical slash command name used to invoke the skill, without the leading '/' */ + @JsonProperty("commandName") String commandName, /** Description of what the skill does */ @JsonProperty("description") String description, /** Source location type (e.g., project, personal-copilot, plugin, builtin) */ diff --git a/java/src/main/java/com/github/copilot/CopilotSession.java b/java/src/main/java/com/github/copilot/CopilotSession.java index b977b69a12..4683fdf015 100644 --- a/java/src/main/java/com/github/copilot/CopilotSession.java +++ b/java/src/main/java/com/github/copilot/CopilotSession.java @@ -1025,8 +1025,8 @@ private void executePermissionAndRespondAsync(String requestId, PermissionReques return; } getRpc().permissions.handlePendingPermissionRequest( - new SessionPermissionsHandlePendingPermissionRequestParams(sessionId, requestId, - result)); + new SessionPermissionsHandlePendingPermissionRequestParams(sessionId, requestId, result, + null)); } catch (Exception e) { LOG.log(Level.WARNING, "Error sending permission result for requestId=" + requestId, e); } @@ -1036,8 +1036,8 @@ private void executePermissionAndRespondAsync(String requestId, PermissionReques PermissionRequestResult denied = new PermissionRequestResult(); denied.setKind(PermissionRequestResultKind.DENIED_COULD_NOT_REQUEST_FROM_USER); getRpc().permissions.handlePendingPermissionRequest( - new SessionPermissionsHandlePendingPermissionRequestParams(sessionId, requestId, - denied)); + new SessionPermissionsHandlePendingPermissionRequestParams(sessionId, requestId, denied, + null)); } catch (Exception e) { LOG.log(Level.WARNING, "Error sending permission denied for requestId=" + requestId, e); } @@ -1049,7 +1049,8 @@ private void executePermissionAndRespondAsync(String requestId, PermissionReques PermissionRequestResult denied = new PermissionRequestResult(); denied.setKind(PermissionRequestResultKind.DENIED_COULD_NOT_REQUEST_FROM_USER); getRpc().permissions.handlePendingPermissionRequest( - new SessionPermissionsHandlePendingPermissionRequestParams(sessionId, requestId, denied)); + new SessionPermissionsHandlePendingPermissionRequestParams(sessionId, requestId, denied, + null)); } catch (Exception sendEx) { LOG.log(Level.WARNING, "Error sending permission denied for requestId=" + requestId, sendEx); } diff --git a/java/src/main/java/com/github/copilot/SessionRequestBuilder.java b/java/src/main/java/com/github/copilot/SessionRequestBuilder.java index add4a79b66..23e4f77b41 100644 --- a/java/src/main/java/com/github/copilot/SessionRequestBuilder.java +++ b/java/src/main/java/com/github/copilot/SessionRequestBuilder.java @@ -201,6 +201,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; } @@ -337,6 +338,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; } @@ -374,7 +376,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()); } @@ -425,7 +428,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/main/java/com/github/copilot/rpc/CreateSessionRequest.java b/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java index 46f59a28cd..4c74e38ac2 100644 --- a/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java +++ b/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java @@ -234,6 +234,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; @@ -1095,4 +1099,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..39e8fcf55a --- /dev/null +++ b/java/src/main/java/com/github/copilot/rpc/ManagedSettings.java @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * 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..0923cea54a --- /dev/null +++ b/java/src/main/java/com/github/copilot/rpc/ManagedSettingsPermissions.java @@ -0,0 +1,90 @@ +/*--------------------------------------------------------------------------------------------- + * 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 com.github.copilot.generated.rpc.DisableBypassPermissionsMode; +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 DisableBypassPermissionsMode 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 DisableBypassPermissionsMode getDisableBypassPermissionsMode() { + return disableBypassPermissionsMode; + } + + /** + * Disables bypass/allow-all permission modes. + * + * @param value + * bypass-permissions policy + * @return this policy + */ + public ManagedSettingsPermissions setDisableBypassPermissionsMode(DisableBypassPermissionsMode value) { + 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 1e32ec847d..10641157b6 100644 --- a/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java +++ b/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java @@ -107,6 +107,7 @@ public class ResumeSessionConfig { private String remoteSession; private CopilotExpAssignmentResponse expAssignments; private Boolean enableManagedSettings; + private ManagedSettings managedSettings; /** * Gets the AI model to use. @@ -1910,6 +1911,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. *

@@ -1992,6 +2011,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 3fe17b182a..8c9d03ede2 100644 --- a/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java +++ b/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java @@ -236,6 +236,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; @@ -1110,4 +1114,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 ad62551fd8..3ccda690f0 100644 --- a/java/src/main/java/com/github/copilot/rpc/SessionConfig.java +++ b/java/src/main/java/com/github/copilot/rpc/SessionConfig.java @@ -108,6 +108,7 @@ public class SessionConfig { private CloudSessionOptions cloud; private CopilotExpAssignmentResponse expAssignments; private Boolean enableManagedSettings; + private ManagedSettings managedSettings; /** * Gets the custom session ID. @@ -2041,6 +2042,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. *

@@ -2128,6 +2152,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..dbd19f3c97 --- /dev/null +++ b/java/src/test/java/com/github/copilot/ManagedSettingsTest.java @@ -0,0 +1,90 @@ +/*--------------------------------------------------------------------------------------------- + * 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.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.copilot.generated.rpc.DisableBypassPermissionsMode; +import com.github.copilot.rpc.ManagedSettings; +import com.github.copilot.rpc.ManagedSettingsPermissions; +import com.github.copilot.rpc.PermissionRequestResult; +import com.github.copilot.rpc.ResumeSessionConfig; +import com.github.copilot.rpc.SessionConfig; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import org.junit.jupiter.api.Test; + +class ManagedSettingsTest { + @Test + void forwardsManagedSettingsOnCreateAndResume() throws Exception { + var permissions = new ManagedSettingsPermissions() + .setDisableBypassPermissionsMode(DisableBypassPermissionsMode.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); + assertTrue(json.contains("\"enableManagedSettings\":true")); + assertTrue(json.contains("\"managedSettings\":{\"permissions\"")); + assertTrue(json.contains("\"disableBypassPermissionsMode\":\"disable\"")); + } + + @Test + void preservesExplicitEmptyPermissionArrays() throws Exception { + // Security-critical: a present empty allow list admits nothing, while an + // absent (null) list imposes no such restriction. Jackson NON_NULL must + // emit an explicit empty array as `[]` and omit null fields, so the two + // remain distinguishable on the wire. + var permissions = new ManagedSettingsPermissions().setDeny(List.of()).setAsk(List.of()).setAllow(List.of()); + var managedSettings = new ManagedSettings().setPermissions(permissions); + var create = SessionRequestBuilder.buildCreateRequest(new SessionConfig().setManagedSettings(managedSettings), + "managed-empty"); + + var json = new ObjectMapper().writeValueAsString(create); + assertTrue(json.contains("\"deny\":[]"), json); + assertTrue(json.contains("\"ask\":[]"), json); + assertTrue(json.contains("\"allow\":[]"), json); + } + + @Test + void distinguishesExplicitEmptyAllowFromAbsentAllow() throws Exception { + // Present empty allow admits nothing; the null deny/ask must be omitted. + var permissions = new ManagedSettingsPermissions().setAllow(List.of()); + var managedSettings = new ManagedSettings().setPermissions(permissions); + var create = SessionRequestBuilder.buildCreateRequest(new SessionConfig().setManagedSettings(managedSettings), + "managed-mixed"); + + var json = new ObjectMapper().writeValueAsString(create); + assertTrue(json.contains("\"allow\":[]"), json); + assertFalse(json.contains("\"deny\""), json); + assertFalse(json.contains("\"ask\""), json); + } + + @Test + void directInjectionEnablesManagedSafeguards() throws Exception { + var session = new CopilotSession("session-1", null); + var settings = new ManagedSettings().setPermissions(new ManagedSettingsPermissions()); + var managedSettingsEnabled = new AtomicBoolean(); + var config = new SessionConfig().setManagedSettings(settings).setOnPermissionRequest((request, invocation) -> { + managedSettingsEnabled.set(invocation.isManagedSettingsEnabled()); + return CompletableFuture.completedFuture(PermissionRequestResult.noResult()); + }); + + SessionRequestBuilder.configureSession(session, config); + session.handlePermissionRequest(new ObjectMapper().readTree("{\"kind\":\"read\"}")).get(); + + assertTrue(managedSettingsEnabled.get()); + } +} diff --git a/java/src/test/java/com/github/copilot/SessionEventDeserializationTest.java b/java/src/test/java/com/github/copilot/SessionEventDeserializationTest.java index fc978ed65b..8d9b70a348 100644 --- a/java/src/test/java/com/github/copilot/SessionEventDeserializationTest.java +++ b/java/src/test/java/com/github/copilot/SessionEventDeserializationTest.java @@ -113,6 +113,54 @@ void testParseSessionIdleEvent() throws Exception { assertEquals("session.idle", event.getType()); } + @Test + void testManagedSettingsResolvedClientProvenance() throws Exception { + assertEquals("server", ManagedSettingsResolvedSource.SERVER.getValue()); + assertEquals("device", ManagedSettingsResolvedSource.DEVICE.getValue()); + assertEquals("client", ManagedSettingsResolvedSource.CLIENT.getValue()); + assertEquals("mixed", ManagedSettingsResolvedSource.MIXED.getValue()); + assertEquals("none", ManagedSettingsResolvedSource.NONE.getValue()); + + String clientJson = """ + { + "type": "session.managed_settings_resolved", + "data": { + "source": "client", + "serverManaged": false, + "deviceManaged": false, + "clientManaged": true, + "failClosed": false, + "bypassPermissionsDisabled": true, + "managedKeys": ["permissions"] + } + } + """; + + var clientEvent = assertInstanceOf(SessionManagedSettingsResolvedEvent.class, parseJson(clientJson)); + assertEquals(ManagedSettingsResolvedSource.CLIENT, clientEvent.getData().source()); + assertEquals(Boolean.TRUE, clientEvent.getData().clientManaged()); + assertTrue(MAPPER.writeValueAsString(clientEvent).contains("\"clientManaged\":true")); + + String mixedJson = """ + { + "type": "session.managed_settings_resolved", + "data": { + "source": "mixed", + "serverManaged": true, + "deviceManaged": true, + "failClosed": false, + "bypassPermissionsDisabled": true, + "managedKeys": ["permissions"] + } + } + """; + + var mixedEvent = assertInstanceOf(SessionManagedSettingsResolvedEvent.class, parseJson(mixedJson)); + assertEquals(ManagedSettingsResolvedSource.MIXED, mixedEvent.getData().source()); + assertNull(mixedEvent.getData().clientManaged()); + assertFalse(MAPPER.writeValueAsString(mixedEvent).contains("\"clientManaged\"")); + } + @Test void testParseSessionInfoEvent() throws Exception { String json = """ @@ -897,15 +945,16 @@ void testParseEmptyJson() throws Exception { @Test void testParseAllEventTypes() throws Exception { String[] types = {"session.start", "session.resume", "session.error", "session.idle", "session.info", - "session.model_change", "session.mode_changed", "session.plan_changed", - "session.workspace_file_changed", "session.handoff", "session.truncation", "session.snapshot_rewind", - "session.usage_info", "session.compaction_start", "session.compaction_complete", "user.message", - "pending_messages.modified", "assistant.turn_start", "assistant.intent", "assistant.reasoning", - "assistant.reasoning_delta", "assistant.message", "assistant.message_delta", "assistant.turn_end", - "assistant.usage", "abort", "tool.user_requested", "tool.execution_start", - "tool.execution_partial_result", "tool.execution_progress", "tool.execution_complete", - "subagent.started", "subagent.completed", "subagent.failed", "subagent.selected", "hook.start", - "hook.end", "system.message", "session.shutdown", "skill.invoked"}; + "session.model_change", "session.mode_changed", "session.managed_settings_resolved", + "session.managed_settings_enforced", "session.plan_changed", "session.workspace_file_changed", + "session.handoff", "session.truncation", "session.snapshot_rewind", "session.usage_info", + "session.compaction_start", "session.compaction_complete", "user.message", "pending_messages.modified", + "assistant.turn_start", "assistant.intent", "assistant.reasoning", "assistant.reasoning_delta", + "assistant.message", "assistant.message_delta", "assistant.turn_end", "assistant.usage", "abort", + "tool.user_requested", "tool.execution_start", "tool.execution_partial_result", + "tool.execution_progress", "tool.execution_complete", "subagent.started", "subagent.completed", + "subagent.failed", "subagent.selected", "hook.start", "hook.end", "system.message", "session.shutdown", + "skill.invoked"}; for (String type : types) { String json = """ diff --git a/java/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcApiCoverageTest.java b/java/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcApiCoverageTest.java index f3b3f98083..e92b0f968d 100644 --- a/java/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcApiCoverageTest.java +++ b/java/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcApiCoverageTest.java @@ -498,7 +498,7 @@ void sessionRpc_permissions_handlePendingPermissionRequest_merges_sessionId() { var stub = new StubCaller(); var session = new SessionRpc(stub, "sess-perm"); - var permParams = new SessionPermissionsHandlePendingPermissionRequestParams(null, "req-perm-1", "allow"); + var permParams = new SessionPermissionsHandlePendingPermissionRequestParams(null, "req-perm-1", "allow", null); session.permissions.handlePendingPermissionRequest(permParams); assertEquals(1, stub.calls.size()); diff --git a/java/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcRecordsCoverageTest.java b/java/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcRecordsCoverageTest.java index 3b8a716c7d..342bf5d2cb 100644 --- a/java/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcRecordsCoverageTest.java +++ b/java/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcRecordsCoverageTest.java @@ -338,7 +338,7 @@ void sessionModelSwitchToParams_record() { @Test void sessionPermissionsHandlePendingPermissionRequestParams_record() { - var params = new SessionPermissionsHandlePendingPermissionRequestParams("sess-33", "req-1", "allow"); + var params = new SessionPermissionsHandlePendingPermissionRequestParams("sess-33", "req-1", "allow", null); assertEquals("sess-33", params.sessionId()); assertEquals("req-1", params.requestId()); assertEquals("allow", params.result()); @@ -706,8 +706,8 @@ void sessionShellKillResult_record() { @Test void sessionSkillsListResult_nested() { - var item = new Skill("deploy", "Deploy the app", SkillSource.PROJECT, true, true, "/skills/deploy.md", null, - null); + var item = new Skill("deploy", "deploy", "Deploy the app", SkillSource.PROJECT, true, true, "/skills/deploy.md", + null, null); var result = new SessionSkillsListResult(List.of(item)); assertEquals(1, result.skills().size()); assertEquals("deploy", result.skills().get(0).name()); diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index b3c43e9275..5db6bebbcb 100644 --- a/nodejs/package-lock.json +++ b/nodejs/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0-dev", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.78", + "@github/copilot": "^1.0.79-5", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" @@ -700,9 +700,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.78.tgz", - "integrity": "sha512-jn+8HLZC3R7d6K1/1g9L1iWNKzBVS3JdVcx40r3aWyS5r+MLV1OPNp0fo5OfRMCDIm3NmEaaoqypi9sQkCXuiQ==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.79-5.tgz", + "integrity": "sha512-gQj87QGcWQpAx2YcBcBZ15q5ITA9y9aLDu0mIZb2daIRQqIK8VKtmY70G5d271eqrztTqd2b3Nu0mlB64E5ufg==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "detect-libc": "^2.1.2" @@ -711,20 +711,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.78", - "@github/copilot-darwin-x64": "1.0.78", - "@github/copilot-linux-arm64": "1.0.78", - "@github/copilot-linux-x64": "1.0.78", - "@github/copilot-linuxmusl-arm64": "1.0.78", - "@github/copilot-linuxmusl-x64": "1.0.78", - "@github/copilot-win32-arm64": "1.0.78", - "@github/copilot-win32-x64": "1.0.78" + "@github/copilot-darwin-arm64": "1.0.79-5", + "@github/copilot-darwin-x64": "1.0.79-5", + "@github/copilot-linux-arm64": "1.0.79-5", + "@github/copilot-linux-x64": "1.0.79-5", + "@github/copilot-linuxmusl-arm64": "1.0.79-5", + "@github/copilot-linuxmusl-x64": "1.0.79-5", + "@github/copilot-win32-arm64": "1.0.79-5", + "@github/copilot-win32-x64": "1.0.79-5" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.78.tgz", - "integrity": "sha512-P11+VyWg8ad0WlywGtO2d7AxqTLJv4hkUicFg6Ycth5lfk00aCu/74YOOZSPO6C2bBBJhAza7oAdmauM6KEojw==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.79-5.tgz", + "integrity": "sha512-pR/ZrznLn6oOUcIPPgzaOOHKDLmeznV1yHwzkmTrqJVwlFgfFYeqBGTD3YwAqSmGAtXdDk+16o3j0mCg0m3+9A==", "cpu": [ "arm64" ], @@ -738,9 +738,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.78.tgz", - "integrity": "sha512-stimP3WDFs2GU8nJzTJbtRpZViV4bsf80yg7QrFq+G4RISQ3Nihg/3/H0U6UQF1+txMJ/Ohmb5RFYxSw1Hj2sw==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.79-5.tgz", + "integrity": "sha512-L828i0YUiI7IAACsmMnKg7LKrgzu5KqgOyA72BmxPmIYUpRpIxYt8QU8oGeej52de+I6zRYUCSWkLAkKLa6oFw==", "cpu": [ "x64" ], @@ -754,12 +754,15 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.78.tgz", - "integrity": "sha512-K31PRKGTm252V1Lof7ypjg283R2QSm3BgoCvZfX2taos4wqC3SaTozSQKwW3dgrAx7A3G3SGEoilVCNqfigdZA==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.79-5.tgz", + "integrity": "sha512-r3GT8kHGOhxbf+QaHp4vOQnJTnrreYOM+eTkgyiVrDe9GR5RKOO7XHjr8F9ob29UP4qpci/2LpgcYIJ9yiszgg==", "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "SEE LICENSE IN LICENSE.md", "optional": true, "os": [ @@ -770,12 +773,15 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.78.tgz", - "integrity": "sha512-QK3oMtAn9dIv+1u1kx0xNpZNtZxdI+uZVIyLl7myp+Oh2Uj8BLagVv6a7uP0cDphO3TgfIdlvpepCe5MIcx0fw==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.79-5.tgz", + "integrity": "sha512-as7EcVDOC7TtoO9+bX8NrQsACPdPKg6t0HTaAaa7oRAftalGX8ZGhGouBOL6v13vpR2xtLaRRaP3YMwo//XQ9A==", "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "SEE LICENSE IN LICENSE.md", "optional": true, "os": [ @@ -786,12 +792,15 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.78.tgz", - "integrity": "sha512-F/0cTMsz6ug4yiXn3RKaCAMsLR261U5Njb6G9Y/HeAI7ES/tKEo2t5SHuvgXaIH4mYiZsRvfDKdX7c0WgBX/Jg==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.79-5.tgz", + "integrity": "sha512-znO0FerQz6kUj46GqjGACR+ernRrAL3iymYo3ipGdf/E71fZnhyZ4BReh0ajRk6b2O5b7dbItk6K0K3Vr/Wn4g==", "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "SEE LICENSE IN LICENSE.md", "optional": true, "os": [ @@ -802,12 +811,15 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.78.tgz", - "integrity": "sha512-YMaJaeBGbArGAFYel+yFaFW/0rFgh0Oqki2f2mUtlonTX/xHr8EB4+mTnMJkHYMFy4gOTC3OtSEEe1NaW/cBXQ==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.79-5.tgz", + "integrity": "sha512-6hDWKHNEyMwvsPwHYp+Yz3QIojFfheVzhpgz+zZde1uLSf7okiV3qP5Uyuwixv4qoissxnD+2o8eL6VAJl3fsg==", "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "SEE LICENSE IN LICENSE.md", "optional": true, "os": [ @@ -818,9 +830,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.78.tgz", - "integrity": "sha512-ktDkFXaaecEKD3hpM6ydM9lKOdoCfsQsXCmzLzE7DCmSpbbMCdfPfWfZ7MOclmKmpZ5/MNfr4U2l8CUqGerzYA==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.79-5.tgz", + "integrity": "sha512-kvt3YrwZ4/VhAMsUau/T1SCzg4e5/ki7gBF2bxJqszh3B+7DVDXZB/jaUPexLUAbGhxSAzB9c02X1tgzt3m4nw==", "cpu": [ "arm64" ], @@ -834,9 +846,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.78.tgz", - "integrity": "sha512-Gd8l2T4eqYEWlOEPd0SZznQ+YYgYrwOkE0QXodMkhCBbPdgu/uTzb7mnISWwnVAgqs7pONdF1GOpHkTo+ay8CQ==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.79-5.tgz", + "integrity": "sha512-e+5DJhN92vMvqrK0O4hV4KTCe5AP9F75K3PbGlSzEAjHhaXYxvPcx3hsL85SEMAoBrXItx64kHQMQm33Jo9K+Q==", "cpu": [ "x64" ], diff --git a/nodejs/package.json b/nodejs/package.json index ca259556ba..3aa1a72c70 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -56,7 +56,7 @@ "author": "GitHub", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.78", + "@github/copilot": "^1.0.79-5", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" diff --git a/nodejs/samples/package-lock.json b/nodejs/samples/package-lock.json index aed2d4465e..7a559a612b 100644 --- a/nodejs/samples/package-lock.json +++ b/nodejs/samples/package-lock.json @@ -18,7 +18,7 @@ "version": "0.0.0-dev", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.78", + "@github/copilot": "^1.0.79-5", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index 4ed139be70..c30b2207b8 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); @@ -1608,6 +1610,7 @@ export class CopilotClient { cloud: config.cloud, expAssignments: config.expAssignments, enableManagedSettings: config.enableManagedSettings, + managedSettings: config.managedSettings, }); const { @@ -1708,7 +1711,8 @@ export class CopilotClient { this.onGetTraceContext, { mcpAuthHandler: config.onMcpAuthRequest, - managedSettingsEnabled: config.enableManagedSettings, + managedSettingsEnabled: + config.enableManagedSettings === true || config.managedSettings !== undefined, } ); session.registerTools(config.tools); @@ -1855,6 +1859,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 cdd3b29eba..7a8c66909d 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 * @@ -1271,6 +1274,63 @@ export type SessionContextAttribution = { * Total token count of the current context window the entries are measured against (system message + conversation messages + tool definitions — the same total reported by /context). Divide an entry's `tokens` by this to derive its share. */ totalTokens: number; + /** + * The concrete model id the entire breakdown was tokenized against (feeds the per-model token multiplier). Under `Auto` (Free/Student) this is the resolved model, not the literal `auto` sentinel, so totals are not undercounted. A single-model approximation of a potentially multi-model Auto session. + */ + modelId: string; + /** + * How `modelId` was chosen. Not a closed set — tolerate unknown values. Known values today: `autoResolved` (the model Auto resolved to), `selected` (the user's explicitly selected model), `default` (a fallback before any model is known). + */ + modelSource: string; + /** + * Maximum prompt tokens the resolved model accepts — the denominator for a `##k/###k` context-usage display. Mirrors `SessionContextInfo.promptTokenLimit`. + */ + promptTokenLimit: number; + /** + * Prompt limit plus the model's output reserve: the full context window `categories.freeSpace` and `categories.buffer` are measured against. Mirrors `SessionContextInfo.limit`. + */ + limit: number; + /** + * Output reserve plus the tokens past the buffer-exhaustion blocking threshold. Mirrors `SessionContextInfo.bufferTokens`. + */ + bufferTokens: number; + /** + * Token count at which background compaction starts. Mirrors `SessionContextInfo.compactionThreshold`. + */ + compactionThreshold: number; + /** + * The six normalized `/context` header buckets, computed from the same tokenization as `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` describe window capacity rather than occupied context, so the values do not sum to `totalTokens`. + */ + categories: { + /** + * System prompt tokens, excluding custom instructions. + */ + systemPrompt: number; + /** + * Custom-instructions tokens (0 when none are configured). + */ + customInstructions: number; + /** + * Non-MCP tool-definition tokens. + */ + systemTools: number; + /** + * MCP tool-definition tokens. + */ + mcpTools: number; + /** + * Conversation (user/assistant/tool) message tokens. + */ + messages: number; + /** + * Remaining unused window capacity (clamped at 0). + */ + freeSpace: number; + /** + * Output reserve plus post-blocking-threshold buffer. + */ + buffer: number; + }; /** * Flat list of per-source attribution entries. Group by `kind` and render unrecognized kinds generically. Nesting and rollups are expressed via `parentId`. */ @@ -1619,6 +1679,52 @@ export type PermissionDecisionApproveForLocationApproval = | PermissionDecisionApproveForLocationApprovalExtensionManagement | PermissionDecisionApproveForLocationApprovalFactory | PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess; +/** + * Disposition of a permission request as observed by the responding client. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "PermissionDecisionOutcome". + */ +/** @experimental */ +export type PermissionDecisionOutcome = + /** The request was approved automatically without a new human decision. */ + | "auto_approved" + /** The request was denied without an interactive user decision; source records why. */ + | "autopilot_denied" + /** The response came from an interactive user prompt. */ + | "prompted_user"; +/** + * Controlled reason or actor responsible for a permission response. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "PermissionDecisionSource". + */ +/** @experimental */ +export type PermissionDecisionSource = + /** The response followed the auto-approval judge recommendation. */ + | "judge_recommendation" + /** A human supplied the response through an interactive prompt. */ + | "human_response" + /** The host applied a standing policy or override rather than a judge recommendation or human decision. */ + | "host_policy" + /** The host denied the request because no interactive user response was available. */ + | "unattended_fallback"; +/** + * Client surface that submitted a permission response. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "PermissionDecisionSurface". + */ +/** @experimental */ +export type PermissionDecisionSurface = + /** The interactive Copilot CLI terminal UI. */ + | "tui" + /** The non-interactive Copilot CLI prompt mode. */ + | "prompt_mode" + /** The Copilot App client. */ + | "copilot_app" + /** A generic Copilot SDK client. */ + | "sdk"; /** * Tool approval to persist and apply * @@ -5221,6 +5327,61 @@ export interface ExtensionContextPushInput { [k: string]: unknown | undefined; }; } +/** + * Opaque integrator-owned process launch profile for one extension entrypoint. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ExtensionLaunchProfile". + */ +/** @experimental */ +export interface ExtensionLaunchProfile { + /** + * Executable used to launch the extension entrypoint. + */ + executable: string; + /** + * Opaque integrator-defined arguments passed to the executable. The runtime does not append the extension entrypoint. + */ + args: string[]; + /** + * Opaque integrator-defined environment variables. Runtime-owned COPILOT_SDK_PATH, SESSION_ID, and COPILOT_EXTENSION_PARENT_PID values take precedence. + */ + env: { + [k: string]: string | undefined; + }; +} +/** + * A discovered extension entrypoint that the registered integrator may classify and resolve to an opaque launch profile. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ExtensionLaunchProviderResolveRequest". + */ +/** @experimental */ +export interface ExtensionLaunchProviderResolveRequest { + /** + * Source-qualified extension identifier. + */ + id: string; + /** + * Human-readable extension name. + */ + name: string; + /** + * Absolute path to the discovered extension entrypoint. + */ + modulePath: string; + source: ExtensionSource; +} +/** + * The launch profile for a supported entrypoint. Omit launch when the provider does not support the entrypoint. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ExtensionLaunchProviderResolveResult". + */ +/** @experimental */ +export interface ExtensionLaunchProviderResolveResult { + launch?: ExtensionLaunchProfile; +} /** * Extensions discovered for the session, with their current status. * @@ -7347,6 +7508,25 @@ export interface LspInitializeRequest { */ force?: boolean; } +/** + * Validated device-managed settings discovered before a session exists. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ManagedSettingsReadResult". + */ +/** @experimental */ +export interface ManagedSettingsReadResult { + /** + * Validated, canonical managed-settings JSON. Omitted when no managed settings were discovered or when discovered settings failed validation. + */ + settingsJson?: { + [k: string]: unknown | undefined; + }; + /** + * Discovery or validation error text when managed settings could not be read safely. + */ + errorMessage?: string; +} /** * Result of registering a new marketplace. * @@ -10351,6 +10531,18 @@ export interface PermissionDecisionDeniedByPermissionRequestHook { */ interrupt?: boolean; } +/** + * Optional informational context describing how and where the permission decision was made. This does not affect permission behavior. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "PermissionDecisionContext". + */ +/** @experimental */ +export interface PermissionDecisionContext { + outcome: PermissionDecisionOutcome; + source: PermissionDecisionSource; + surface: PermissionDecisionSurface; +} /** * Pending permission request ID and the decision to apply (approve/reject and scope). * @@ -10364,6 +10556,7 @@ export interface PermissionDecisionRequest { */ requestId: string; result: PermissionDecision; + decisionContext?: PermissionDecisionContext; } /** * Location-scoped tool approval to persist. @@ -13056,9 +13249,9 @@ export interface SandboxConfig { */ ghAuth?: boolean; /** - * Whether to auto-grant read access to common developer-tool caches, registries, and toolchains in their default home locations (cargo, go, npm, Maven, and more), plus read-write access to (and, on Unix, up-front creation of) the scratch caches builds write on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so builds work without exporting CARGO_HOME/GOPATH/etc. Default: true (enabled by default; set to false to opt out). + * Whether to auto-grant read access to common developer-tool caches, registries, and toolchains in their default home locations (cargo, go, npm, Maven, and more), plus read-write access to (and, on Unix, up-front creation of) the scratch caches builds write on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so builds work without extra configuration; a relocated CARGO_HOME additionally gets its Cargo lock files granted read-write. Default: true (enabled by default; set to false to opt out). */ - allowDevToolCaches?: boolean; + allowDevToolAccess?: boolean; } /** * User-managed sandbox policy fragment merged into the auto-discovered base policy. @@ -13663,6 +13856,10 @@ export interface ServerSkill { * Unique identifier for the skill */ name: string; + /** + * Canonical slash command name used to invoke the skill, without the leading '/' + */ + commandName?: string; /** * Description of what the skill does */ @@ -14477,6 +14674,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 * @@ -14628,6 +14857,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. */ @@ -14722,6 +14952,10 @@ export interface SessionOpenOptions { */ logInteractiveShells?: boolean; envValueMode?: SessionOpenOptionsEnvValueMode; + /** + * MCP server names disabled for this session. Disabled servers are not started or authenticated on create or cold resume. + */ + disabledMcpServers?: string[]; /** * Whether to include instructions from every MCP server in the system prompt instead of only allowlisted servers. */ @@ -16203,6 +16437,10 @@ export interface Skill { * Unique identifier for the skill */ name: string; + /** + * Canonical slash command name used to invoke the skill, without the leading '/' + */ + commandName?: string; /** * Description of what the skill does */ @@ -18718,6 +18956,13 @@ export function createServerRpc(connection: MessageConnection) { disable: async (params: DiscoveredExtensionsDisableRequest): Promise => connection.sendRequest("extensions.disable", params), }, + /** + * Registers the calling SDK client as the per-entrypoint extension launch provider. Call before creating any sessions. When omitted, the runtime temporarily falls back to its built-in Node launcher for backward compatibility. + * + * @experimental + */ + registerExtensionLaunchProvider: async (): Promise => + connection.sendRequest("registerExtensionLaunchProvider", {}), /** @experimental */ plugins: { /** @@ -18931,6 +19176,16 @@ export function createServerRpc(connection: MessageConnection) { }, }, /** @experimental */ + managedSettings: { + /** + * Discovers device-managed settings from production MDM and managed-file sources, validates them against the runtime-owned managed-settings schema, and returns the canonical JSON without requiring a session. + * + * @returns Validated device-managed settings discovered before a session exists. + */ + read: async (): Promise => + connection.sendRequest("managedSettings.read", {}), + }, + /** @experimental */ runtime: { /** * Gracefully shuts down an SDK-owned runtime. The response is sent only after cleanup completes; callers may then terminate the owned runtime process. @@ -21646,6 +21901,19 @@ export function registerClientSessionApiHandlers( }); } +/** Handler for `extensionLaunchProvider` client global API methods. */ +/** @experimental */ +export interface ExtensionLaunchProviderHandler { + /** + * Asks the registered SDK client to resolve an opaque process launch profile for one discovered extension entrypoint immediately before launch or reload. The provider must respond within 15 seconds. + * + * @param params A discovered extension entrypoint that the registered integrator may classify and resolve to an opaque launch profile. + * + * @returns The launch profile for a supported entrypoint. Omit launch when the provider does not support the entrypoint. + */ + resolve(params: ExtensionLaunchProviderResolveRequest): Promise; +} + /** Handler for `llmInference` client global API methods. */ /** @experimental */ export interface LlmInferenceHandler { @@ -21680,6 +21948,7 @@ export interface GitHubTelemetryHandler { /** All client global API handler groups. */ export interface ClientGlobalApiHandlers { + extensionLaunchProvider?: ExtensionLaunchProviderHandler; llmInference?: LlmInferenceHandler; gitHubTelemetry?: GitHubTelemetryHandler; } @@ -21695,6 +21964,11 @@ export function registerClientGlobalApiHandlers( connection: MessageConnection, handlers: ClientGlobalApiHandlers, ): void { + connection.onRequest("extensionLaunchProvider.resolve", async (params: ExtensionLaunchProviderResolveRequest) => { + const handler = handlers.extensionLaunchProvider; + if (!handler) throw new Error("No extensionLaunchProvider client-global handler registered"); + return handler.resolve(params); + }); connection.onRequest("llmInference.httpRequestStart", async (params: LlmInferenceHttpRequestStartRequest) => { const handler = handlers.llmInference; if (!handler) throw new Error("No llmInference client-global handler registered"); diff --git a/nodejs/src/generated/session-events.ts b/nodejs/src/generated/session-events.ts index 1dad4ab166..19d044d6d1 100644 --- a/nodejs/src/generated/session-events.ts +++ b/nodejs/src/generated/session-events.ts @@ -742,14 +742,18 @@ export type AutoModeResolvedReasoningBucket = /** The request looks high-reasoning; a stronger model is appropriate. */ | "high"; /** - * Which channel supplied the effective enterprise managed settings (highest-authority present layer wins wholesale) + * Summary of which managed-settings channels contributed to the effective session policy. Use the per-channel booleans for exact provenance. */ export type ManagedSettingsResolvedSource = - /** Account/org policy self-fetched from the GitHub managed-settings endpoint (higher authority). */ + /** Only the server/account channel contributed. */ | "server" - /** Device-level MDM policy discovered from plist/registry/file (lower authority). */ + /** Only the device MDM/plist/registry/file channel contributed. */ | "device" - /** No managed policy is in force (no layer contributed). */ + /** Only session-local SDK-host injection contributed. */ + | "client" + /** More than one channel contributed. Ordinary keys resolve device over server per key, while permissions compose restrictively across all present layers. */ + | "mixed" + /** No managed policy is in force (no channel contributed). */ | "none"; /** * The category of runtime action that enterprise managed settings governed (blocked or capped) @@ -8644,7 +8648,7 @@ export interface AutoModeResolvedData { stickyOverride?: boolean; } /** - * Session event "session.managed_settings_resolved". Enterprise managed-settings resolution: the effective managed settings the session applied and where they came from, so SDK clients can show users what is enterprise-managed and by which authority. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted; for a session-independent pull, use the SDK `getManagedSettings()` API, which returns the identical payload. Managed settings have a single authoritative source, so the highest-authority present layer (server > device) wins wholesale; `bypassPermissionsDisabled` is deny-wins across layers. Marked experimental while the managed-settings surface stabilizes. + * Session event "session.managed_settings_resolved". Enterprise managed-settings resolution: the effective managed settings the session applied and which channels contributed, so SDK clients can show users what is enterprise-managed. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted. Device values take precedence over server values per ordinary key, while permissions compose restrictively across device, server, and SDK-client layers. The account-scoped `getManagedSettings()` API does not include session-local client injection. Marked experimental while the managed-settings surface stabilizes. */ /** @experimental */ export interface ManagedSettingsResolvedEvent { @@ -8675,7 +8679,7 @@ export interface ManagedSettingsResolvedEvent { type: "session.managed_settings_resolved"; } /** - * Enterprise managed-settings resolution: the effective managed settings the session applied and where they came from, so SDK clients can show users what is enterprise-managed and by which authority. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted; for a session-independent pull, use the SDK `getManagedSettings()` API, which returns the identical payload. Managed settings have a single authoritative source, so the highest-authority present layer (server > device) wins wholesale; `bypassPermissionsDisabled` is deny-wins across layers. Marked experimental while the managed-settings surface stabilizes. + * Enterprise managed-settings resolution: the effective managed settings the session applied and which channels contributed, so SDK clients can show users what is enterprise-managed. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted. Device values take precedence over server values per ordinary key, while permissions compose restrictively across device, server, and SDK-client layers. The account-scoped `getManagedSettings()` API does not include session-local client injection. Marked experimental while the managed-settings surface stabilizes. */ /** @experimental */ export interface ManagedSettingsResolvedData { @@ -8684,7 +8688,11 @@ export interface ManagedSettingsResolvedData { */ bypassPermissionsDisabled: boolean; /** - * Whether the device (MDM/plist/registry/file) managed-settings layer was present + * Whether a session-local permissions layer injected by the SDK host was present + */ + clientManaged?: boolean; + /** + * Whether an actual device MDM/plist/registry/file managed-settings layer was present */ deviceManaged: boolean; /** @@ -8696,7 +8704,7 @@ export interface ManagedSettingsResolvedData { */ managedKeys: string[]; /** - * Whether server and device each supplied a permission allowlist, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. + * Whether at least two managed sources supplied permission allowlists, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. */ permissionsAllowIntersected?: boolean; /** @@ -9133,6 +9141,10 @@ export interface SkillsLoadedSkill { * Optional freeform hint describing the skill's expected arguments, from the `argument-hint` frontmatter field */ argumentHint?: string; + /** + * Canonical slash command name used to invoke the skill, without the leading '/' + */ + commandName?: string; /** * Description of what the skill does */ diff --git a/nodejs/src/index.ts b/nodejs/src/index.ts index f915a8707e..5ab53471a6 100644 --- a/nodejs/src/index.ts +++ b/nodejs/src/index.ts @@ -107,6 +107,8 @@ export type { DefaultAgentConfig, BearerTokenProvider, MessageOptions, + ManagedSettings, + ManagedSettingsPermissions, ModelBilling, ModelBillingTokenPrices, ModelBillingTokenPricesLongContext, diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index d77a3a97cf..4ff2791898 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -2088,6 +2088,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 @@ -2586,6 +2625,31 @@ 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 may ignore this additive field, so hosts must not rely on + * injected policy until they ship a compatible runtime. + */ + 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 962d90970e..01a97e9800 100644 --- a/nodejs/test/client.test.ts +++ b/nodejs/test/client.test.ts @@ -3659,3 +3659,101 @@ 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("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(); + }); + + 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/nodejs/test/session-event-types.test.ts b/nodejs/test/session-event-types.test.ts index fef7acdb2b..5a8f2ca521 100644 --- a/nodejs/test/session-event-types.test.ts +++ b/nodejs/test/session-event-types.test.ts @@ -22,6 +22,9 @@ import type { PermissionRequest, PermissionRequestedData, PermissionRequestedEvent, + ManagedSettingsResolvedData, + ManagedSettingsResolvedEvent, + ManagedSettingsResolvedSource, // *Data payload types from the v0.3.0 generated session-event schema. AssistantMessageData, @@ -163,6 +166,45 @@ describe("Session event type exports (#1156)", () => { expect(permissionEvent.data.permissionRequest.managedApprovalRequired).toBe(true); }); + it("exposes managed settings client and mixed provenance", () => { + const sources: ManagedSettingsResolvedSource[] = [ + "server", + "device", + "client", + "mixed", + "none", + ]; + expect(sources).toEqual(["server", "device", "client", "mixed", "none"]); + + const clientData: ManagedSettingsResolvedData = { + bypassPermissionsDisabled: true, + clientManaged: true, + deviceManaged: false, + failClosed: false, + managedKeys: ["permissions"], + serverManaged: false, + source: "client", + }; + const clientEvent: ManagedSettingsResolvedEvent = { + ephemeral: true, + id: "evt-managed-1", + parentId: null, + timestamp: "2026-01-01T00:00:00.000Z", + type: "session.managed_settings_resolved", + data: clientData, + }; + expect(clientEvent.data.source).toBe("client"); + expect(clientEvent.data.clientManaged).toBe(true); + + const { clientManaged: _, ...withoutClientManaged } = clientData; + const mixedData: ManagedSettingsResolvedData = { + ...withoutClientManaged, + source: "mixed", + }; + expect(mixedData.source).toBe("mixed"); + expect("clientManaged" in mixedData).toBe(false); + }); + it("rejects approveAll in managed settings sessions", () => { expect(() => approveAll( @@ -260,6 +302,7 @@ describe("Session event type exports (#1156)", () => { assertImportable(); assertImportable(); assertImportable(); + assertImportable(); assertImportable(); assertImportable(); @@ -270,6 +313,8 @@ describe("Session event type exports (#1156)", () => { assertImportable(); assertImportable(); assertImportable(); + assertImportable(); + assertImportable(); // Supporting auxiliary types referenced by the *Data shapes — these // must round-trip through the package root too, otherwise consumers diff --git a/python/copilot/__init__.py b/python/copilot/__init__.py index 678fffbf14..a7366db543 100644 --- a/python/copilot/__init__.py +++ b/python/copilot/__init__.py @@ -41,6 +41,8 @@ GetStatusResponse, InProcessRuntimeConnection, LogLevel, + ManagedSettings, + ManagedSettingsPermissions, ModelBilling, ModelCapabilities, ModelInfo, @@ -272,6 +274,8 @@ "McpAuthStaticClientConfig", "McpAuthToken", "McpAuthWwwAuthenticateParams", + "ManagedSettings", + "ManagedSettingsPermissions", "ModelBilling", "ModelBillingTokenPrices", "ModelBillingTokenPricesLongContext", diff --git a/python/copilot/client.py b/python/copilot/client.py index 7c273bd010..21ceb6eee1 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" @@ -2090,6 +2147,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. @@ -2241,6 +2299,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. @@ -2389,6 +2456,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 @@ -2575,7 +2646,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: @@ -2799,6 +2871,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. @@ -2951,6 +3024,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. @@ -3122,6 +3200,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 additional_directories: @@ -3234,7 +3316,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/copilot/generated/rpc.py b/python/copilot/generated/rpc.py index 41e27fc12d..a9511540cc 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: @@ -2096,6 +2102,8 @@ def to_dict(self) -> dict: class ExtensionSource(Enum): """Discovery source: project (.github/extensions/), user (~/.copilot/extensions/), plugin (installed plugin), or session (session-state//extensions/) + + Discovery source for the extension entrypoint. """ PLUGIN = "plugin" PROJECT = "project" @@ -2114,6 +2122,39 @@ class ExtensionStatus(Enum): class ExtensionContextPushInputType(Enum): EXTENSION_CONTEXT = "extension_context" +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class ExtensionLaunchProfile: + """Opaque integrator-owned process launch profile for one extension entrypoint. + + Opaque launch profile, omitted when this provider does not support the entrypoint. + """ + args: list[str] + """Opaque integrator-defined arguments passed to the executable. The runtime does not append + the extension entrypoint. + """ + env: dict[str, str] + """Opaque integrator-defined environment variables. Runtime-owned COPILOT_SDK_PATH, + SESSION_ID, and COPILOT_EXTENSION_PARENT_PID values take precedence. + """ + executable: str + """Executable used to launch the extension entrypoint.""" + + @staticmethod + def from_dict(obj: Any) -> 'ExtensionLaunchProfile': + assert isinstance(obj, dict) + args = from_list(from_str, obj.get("args")) + env = from_dict(from_str, obj.get("env")) + executable = from_str(obj.get("executable")) + return ExtensionLaunchProfile(args, env, executable) + + def to_dict(self) -> dict: + result: dict = {} + result["args"] = from_list(from_str, self.args) + result["env"] = from_dict(from_str, self.env) + result["executable"] = from_str(self.executable) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ExtensionsDisableRequest: @@ -3808,6 +3849,34 @@ def to_dict(self) -> dict: result["workingDirectory"] = from_union([from_str, from_none], self.working_directory) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class ManagedSettingsReadResult: + """Validated device-managed settings discovered before a session exists.""" + + error_message: str | None = None + """Discovery or validation error text when managed settings could not be read safely.""" + + settings_json: Any = None + """Validated, canonical managed-settings JSON. Omitted when no managed settings were + discovered or when discovered settings failed validation. + """ + + @staticmethod + def from_dict(obj: Any) -> 'ManagedSettingsReadResult': + assert isinstance(obj, dict) + error_message = from_union([from_str, from_none], obj.get("errorMessage")) + settings_json = obj.get("settingsJson") + return ManagedSettingsReadResult(error_message, settings_json) + + def to_dict(self) -> dict: + result: dict = {} + if self.error_message is not None: + result["errorMessage"] = from_union([from_str, from_none], self.error_message) + if self.settings_json is not None: + result["settingsJson"] = self.settings_json + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class MarketplaceAddResult: @@ -4944,6 +5013,57 @@ def to_dict(self) -> dict: result["enabled"] = from_bool(self.enabled) return result +@dataclass +class Categories: + """The six normalized `/context` header buckets, computed from the same tokenization as + `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` + describe window capacity rather than occupied context, so the values do not sum to + `totalTokens`. + """ + buffer: int + """Output reserve plus post-blocking-threshold buffer.""" + + custom_instructions: int + """Custom-instructions tokens (0 when none are configured).""" + + free_space: int + """Remaining unused window capacity (clamped at 0).""" + + mcp_tools: int + """MCP tool-definition tokens.""" + + messages: int + """Conversation (user/assistant/tool) message tokens.""" + + system_prompt: int + """System prompt tokens, excluding custom instructions.""" + + system_tools: int + """Non-MCP tool-definition tokens.""" + + @staticmethod + def from_dict(obj: Any) -> 'Categories': + assert isinstance(obj, dict) + buffer = from_int(obj.get("buffer")) + custom_instructions = from_int(obj.get("customInstructions")) + free_space = from_int(obj.get("freeSpace")) + mcp_tools = from_int(obj.get("mcpTools")) + messages = from_int(obj.get("messages")) + system_prompt = from_int(obj.get("systemPrompt")) + system_tools = from_int(obj.get("systemTools")) + return Categories(buffer, custom_instructions, free_space, mcp_tools, messages, system_prompt, system_tools) + + def to_dict(self) -> dict: + result: dict = {} + result["buffer"] = from_int(self.buffer) + result["customInstructions"] = from_int(self.custom_instructions) + result["freeSpace"] = from_int(self.free_space) + result["mcpTools"] = from_int(self.mcp_tools) + result["messages"] = from_int(self.messages) + result["systemPrompt"] = from_int(self.system_prompt) + result["systemTools"] = from_int(self.system_tools) + return result + @dataclass class Compactions: """Successful compaction history for the session.""" @@ -5862,6 +5982,38 @@ class PermissionDecisionApprovedForSessionKind(Enum): class PermissionDecisionCancelledKind(Enum): CANCELLED = "cancelled" +# Experimental: this type is part of an experimental API and may change or be removed. +class PermissionDecisionOutcome(Enum): + """Disposition of the permission request as observed by the responding client. + + Disposition of a permission request as observed by the responding client. + """ + AUTOPILOT_DENIED = "autopilot_denied" + AUTO_APPROVED = "auto_approved" + PROMPTED_USER = "prompted_user" + +# Experimental: this type is part of an experimental API and may change or be removed. +class PermissionDecisionSource(Enum): + """Controlled reason or actor responsible for the response. + + Controlled reason or actor responsible for a permission response. + """ + HOST_POLICY = "host_policy" + HUMAN_RESPONSE = "human_response" + JUDGE_RECOMMENDATION = "judge_recommendation" + UNATTENDED_FALLBACK = "unattended_fallback" + +# Experimental: this type is part of an experimental API and may change or be removed. +class PermissionDecisionSurface(Enum): + """Client surface that submitted the response. + + Client surface that submitted a permission response. + """ + COPILOT_APP = "copilot_app" + PROMPT_MODE = "prompt_mode" + SDK = "sdk" + TUI = "tui" + class PermissionDecisionDeniedByContentExclusionPolicyKind(Enum): DENIED_BY_CONTENT_EXCLUSION_POLICY = "denied-by-content-exclusion-policy" @@ -5880,30 +6032,6 @@ class PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUserKind(Enum) class PermissionDecisionRejectKind(Enum): REJECT = "reject" -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class PermissionDecisionRequest: - """Pending permission request ID and the decision to apply (approve/reject and scope).""" - - request_id: str - """Request ID of the pending permission request""" - - result: PermissionDecision - """The client's response to the pending permission prompt""" - - @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionRequest': - assert isinstance(obj, dict) - request_id = from_str(obj.get("requestId")) - result = _load_PermissionDecision(obj.get("result")) - return PermissionDecisionRequest(request_id, result) - - def to_dict(self) -> dict: - result: dict = {} - result["requestId"] = from_str(self.request_id) - result["result"] = (self.result).to_dict() - return result - class PermissionDecisionUserNotAvailableKind(Enum): USER_NOT_AVAILABLE = "user-not-available" @@ -8604,6 +8732,9 @@ class ServerSkill: """Optional freeform hint describing the skill's expected arguments, from the `argument-hint` frontmatter field """ + command_name: str | None = None + """Canonical slash command name used to invoke the skill, without the leading '/'""" + path: str | None = None """Absolute path to the skill file""" @@ -8619,9 +8750,10 @@ def from_dict(obj: Any) -> 'ServerSkill': source = SkillSource(obj.get("source")) user_invocable = from_bool(obj.get("userInvocable")) argument_hint = from_union([from_str, from_none], obj.get("argumentHint")) + command_name = from_union([from_str, from_none], obj.get("commandName")) path = from_union([from_str, from_none], obj.get("path")) project_path = from_union([from_str, from_none], obj.get("projectPath")) - return ServerSkill(description, enabled, name, source, user_invocable, argument_hint, path, project_path) + return ServerSkill(description, enabled, name, source, user_invocable, argument_hint, command_name, path, project_path) def to_dict(self) -> dict: result: dict = {} @@ -8632,6 +8764,8 @@ def to_dict(self) -> dict: result["userInvocable"] = from_bool(self.user_invocable) if self.argument_hint is not None: result["argumentHint"] = from_union([from_str, from_none], self.argument_hint) + if self.command_name is not None: + result["commandName"] = from_union([from_str, from_none], self.command_name) if self.path is not None: result["path"] = from_union([from_str, from_none], self.path) if self.project_path is not None: @@ -10794,6 +10928,9 @@ class Skill: """Optional freeform hint describing the skill's expected arguments, from the `argument-hint` frontmatter field """ + command_name: str | None = None + """Canonical slash command name used to invoke the skill, without the leading '/'""" + path: str | None = None """Absolute path to the skill file""" @@ -10809,9 +10946,10 @@ def from_dict(obj: Any) -> 'Skill': source = SkillSource(obj.get("source")) user_invocable = from_bool(obj.get("userInvocable")) argument_hint = from_union([from_str, from_none], obj.get("argumentHint")) + command_name = from_union([from_str, from_none], obj.get("commandName")) path = from_union([from_str, from_none], obj.get("path")) plugin_name = from_union([from_str, from_none], obj.get("pluginName")) - return Skill(description, enabled, name, source, user_invocable, argument_hint, path, plugin_name) + return Skill(description, enabled, name, source, user_invocable, argument_hint, command_name, path, plugin_name) def to_dict(self) -> dict: result: dict = {} @@ -10822,6 +10960,8 @@ def to_dict(self) -> dict: result["userInvocable"] = from_bool(self.user_invocable) if self.argument_hint is not None: result["argumentHint"] = from_union([from_str, from_none], self.argument_hint) + if self.command_name is not None: + result["commandName"] = from_union([from_str, from_none], self.command_name) if self.path is not None: result["path"] = from_union([from_str, from_none], self.path) if self.plugin_name is not None: @@ -13192,6 +13332,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: @@ -13382,6 +13561,41 @@ def to_dict(self) -> dict: result["hasMore"] = from_bool(self.has_more) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class ExtensionLaunchProviderResolveRequest: + """A discovered extension entrypoint that the registered integrator may classify and resolve + to an opaque launch profile. + """ + id: str + """Source-qualified extension identifier.""" + + module_path: str + """Absolute path to the discovered extension entrypoint.""" + + name: str + """Human-readable extension name.""" + + source: ExtensionSource + """Discovery source for the extension entrypoint.""" + + @staticmethod + def from_dict(obj: Any) -> 'ExtensionLaunchProviderResolveRequest': + assert isinstance(obj, dict) + id = from_str(obj.get("id")) + module_path = from_str(obj.get("modulePath")) + name = from_str(obj.get("name")) + source = ExtensionSource(obj.get("source")) + return ExtensionLaunchProviderResolveRequest(id, module_path, name, source) + + def to_dict(self) -> dict: + result: dict = {} + result["id"] = from_str(self.id) + result["modulePath"] = from_str(self.module_path) + result["name"] = from_str(self.name) + result["source"] = to_enum(ExtensionSource, self.source) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class Extension: @@ -13453,6 +13667,27 @@ def to_dict(self) -> dict: result["type"] = self.type return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class ExtensionLaunchProviderResolveResult: + """The launch profile for a supported entrypoint. Omit launch when the provider does not + support the entrypoint. + """ + launch: ExtensionLaunchProfile | None = None + """Opaque launch profile, omitted when this provider does not support the entrypoint.""" + + @staticmethod + def from_dict(obj: Any) -> 'ExtensionLaunchProviderResolveResult': + assert isinstance(obj, dict) + launch = from_union([ExtensionLaunchProfile.from_dict, from_none], obj.get("launch")) + return ExtensionLaunchProviderResolveResult(launch) + + def to_dict(self) -> dict: + result: dict = {} + if self.launch is not None: + result["launch"] = from_union([lambda x: to_class(ExtensionLaunchProfile, x), from_none], self.launch) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ExternalToolTextResultForLlmBinaryResultsForLlm: @@ -15869,13 +16104,47 @@ class SessionContextAttribution: """Per-source token attribution snapshot for the current context window. The heaviest individual messages are available separately via `metadata.getContextHeaviestMessages`. """ + buffer_tokens: int + """Output reserve plus the tokens past the buffer-exhaustion blocking threshold. Mirrors + `SessionContextInfo.bufferTokens`. + """ + categories: Categories + """The six normalized `/context` header buckets, computed from the same tokenization as + `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` + describe window capacity rather than occupied context, so the values do not sum to + `totalTokens`. + """ compactions: Compactions """Successful compaction history for the session.""" + compaction_threshold: int + """Token count at which background compaction starts. Mirrors + `SessionContextInfo.compactionThreshold`. + """ entries: list[Entry] """Flat list of per-source attribution entries. Group by `kind` and render unrecognized kinds generically. Nesting and rollups are expressed via `parentId`. """ + limit: int + """Prompt limit plus the model's output reserve: the full context window + `categories.freeSpace` and `categories.buffer` are measured against. Mirrors + `SessionContextInfo.limit`. + """ + model_id: str + """The concrete model id the entire breakdown was tokenized against (feeds the per-model + token multiplier). Under `Auto` (Free/Student) this is the resolved model, not the + literal `auto` sentinel, so totals are not undercounted. A single-model approximation of + a potentially multi-model Auto session. + """ + model_source: str + """How `modelId` was chosen. Not a closed set — tolerate unknown values. Known values today: + `autoResolved` (the model Auto resolved to), `selected` (the user's explicitly selected + model), `default` (a fallback before any model is known). + """ + prompt_token_limit: int + """Maximum prompt tokens the resolved model accepts — the denominator for a `##k/###k` + context-usage display. Mirrors `SessionContextInfo.promptTokenLimit`. + """ total_tokens: int """Total token count of the current context window the entries are measured against (system message + conversation messages + tool definitions — the same total reported by @@ -15885,15 +16154,29 @@ class SessionContextAttribution: @staticmethod def from_dict(obj: Any) -> 'SessionContextAttribution': assert isinstance(obj, dict) + buffer_tokens = from_int(obj.get("bufferTokens")) + categories = Categories.from_dict(obj.get("categories")) compactions = Compactions.from_dict(obj.get("compactions")) + compaction_threshold = from_int(obj.get("compactionThreshold")) entries = from_list(Entry.from_dict, obj.get("entries")) + limit = from_int(obj.get("limit")) + model_id = from_str(obj.get("modelId")) + model_source = from_str(obj.get("modelSource")) + prompt_token_limit = from_int(obj.get("promptTokenLimit")) total_tokens = from_int(obj.get("totalTokens")) - return SessionContextAttribution(compactions, entries, total_tokens) + return SessionContextAttribution(buffer_tokens, categories, compactions, compaction_threshold, entries, limit, model_id, model_source, prompt_token_limit, total_tokens) def to_dict(self) -> dict: result: dict = {} + result["bufferTokens"] = from_int(self.buffer_tokens) + result["categories"] = to_class(Categories, self.categories) result["compactions"] = to_class(Compactions, self.compactions) + result["compactionThreshold"] = from_int(self.compaction_threshold) result["entries"] = from_list(lambda x: to_class(Entry, x), self.entries) + result["limit"] = from_int(self.limit) + result["modelId"] = from_str(self.model_id) + result["modelSource"] = from_str(self.model_source) + result["promptTokenLimit"] = from_int(self.prompt_token_limit) result["totalTokens"] = from_int(self.total_tokens) return result @@ -17218,6 +17501,39 @@ def to_dict(self) -> dict: result["reason"] = from_union([from_str, from_none], self.reason) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class PermissionDecisionContext: + """Optional informational context describing how and where the permission decision was made. + This does not affect permission behavior. + + Optional informational context describing how and where this response was made. Omit it + to preserve legacy behavior without attributing an origin. + """ + outcome: PermissionDecisionOutcome + """Disposition of the permission request as observed by the responding client.""" + + source: PermissionDecisionSource + """Controlled reason or actor responsible for the response.""" + + surface: PermissionDecisionSurface + """Client surface that submitted the response.""" + + @staticmethod + def from_dict(obj: Any) -> 'PermissionDecisionContext': + assert isinstance(obj, dict) + outcome = PermissionDecisionOutcome(obj.get("outcome")) + source = PermissionDecisionSource(obj.get("source")) + surface = PermissionDecisionSurface(obj.get("surface")) + return PermissionDecisionContext(outcome, source, surface) + + def to_dict(self) -> dict: + result: dict = {} + result["outcome"] = to_enum(PermissionDecisionOutcome, self.outcome) + result["source"] = to_enum(PermissionDecisionSource, self.source) + result["surface"] = to_enum(PermissionDecisionSurface, self.surface) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class PermissionDecisionDeniedByContentExclusionPolicy: @@ -21430,6 +21746,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: @@ -23030,6 +23369,38 @@ def to_dict(self) -> dict: result["scope"] = to_enum(AdditionalContentExclusionPolicyScope, self.scope) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class PermissionDecisionRequest: + """Pending permission request ID and the decision to apply (approve/reject and scope).""" + + request_id: str + """Request ID of the pending permission request""" + + result: PermissionDecision + """The client's response to the pending permission prompt""" + + decision_context: PermissionDecisionContext | None = None + """Optional informational context describing how and where this response was made. Omit it + to preserve legacy behavior without attributing an origin. + """ + + @staticmethod + def from_dict(obj: Any) -> 'PermissionDecisionRequest': + assert isinstance(obj, dict) + request_id = from_str(obj.get("requestId")) + result = _load_PermissionDecision(obj.get("result")) + decision_context = from_union([PermissionDecisionContext.from_dict, from_none], obj.get("decisionContext")) + return PermissionDecisionRequest(request_id, result, decision_context) + + def to_dict(self) -> dict: + result: dict = {} + result["requestId"] = from_str(self.request_id) + result["result"] = (self.result).to_dict() + if self.decision_context is not None: + result["decisionContext"] = from_union([lambda x: to_class(PermissionDecisionContext, x), from_none], self.decision_context) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class PermissionsConfigureAdditionalContentExclusionPolicy: @@ -25267,13 +25638,14 @@ class SandboxConfig: add_current_working_directory: bool | None = None """Whether to auto-add the current working directory to readwritePaths. Default: true.""" - allow_dev_tool_caches: bool | None = None + allow_dev_tool_access: bool | None = None """Whether to auto-grant read access to common developer-tool caches, registries, and toolchains in their default home locations (cargo, go, npm, Maven, and more), plus read-write access to (and, on Unix, up-front creation of) the scratch caches builds write on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so - builds work without exporting CARGO_HOME/GOPATH/etc. Default: true (enabled by default; - set to false to opt out). + builds work without extra configuration; a relocated CARGO_HOME additionally gets its + Cargo lock files granted read-write. Default: true (enabled by default; set to false to + opt out). """ gh_auth: bool | None = None """Whether to export `GH_TOKEN` so the `gh` CLI authenticates inside the sandbox without the @@ -25292,19 +25664,19 @@ def from_dict(obj: Any) -> 'SandboxConfig': assert isinstance(obj, dict) enabled = from_bool(obj.get("enabled")) add_current_working_directory = from_union([from_bool, from_none], obj.get("addCurrentWorkingDirectory")) - allow_dev_tool_caches = from_union([from_bool, from_none], obj.get("allowDevToolCaches")) + allow_dev_tool_access = from_union([from_bool, from_none], obj.get("allowDevToolAccess")) gh_auth = from_union([from_bool, from_none], obj.get("ghAuth")) git_auth = from_union([from_bool, from_none], obj.get("gitAuth")) user_policy = from_union([SandboxConfigUserPolicy.from_dict, from_none], obj.get("userPolicy")) - return SandboxConfig(enabled, add_current_working_directory, allow_dev_tool_caches, gh_auth, git_auth, user_policy) + return SandboxConfig(enabled, add_current_working_directory, allow_dev_tool_access, gh_auth, git_auth, user_policy) def to_dict(self) -> dict: result: dict = {} result["enabled"] = from_bool(self.enabled) if self.add_current_working_directory is not None: result["addCurrentWorkingDirectory"] = from_union([from_bool, from_none], self.add_current_working_directory) - if self.allow_dev_tool_caches is not None: - result["allowDevToolCaches"] = from_union([from_bool, from_none], self.allow_dev_tool_caches) + if self.allow_dev_tool_access is not None: + result["allowDevToolAccess"] = from_union([from_bool, from_none], self.allow_dev_tool_access) if self.gh_auth is not None: result["ghAuth"] = from_union([from_bool, from_none], self.gh_auth) if self.git_auth is not None: @@ -25529,6 +25901,10 @@ class SessionOpenOptions: disabled_instruction_sources: list[str] | None = None """Instruction source IDs disabled for this session.""" + disabled_mcp_servers: list[str] | None = None + """MCP server names disabled for this session. Disabled servers are not started or + authenticated on create or cold resume. + """ disabled_skills: list[str] | None = None """Skill IDs disabled for this session.""" @@ -25618,6 +25994,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). @@ -25725,6 +26105,7 @@ def from_dict(obj: Any) -> 'SessionOpenOptions': detached_from_spawning_parent_engagement_id = from_union([from_str, from_none], obj.get("detachedFromSpawningParentEngagementId")) detached_from_spawning_parent_session_id = from_union([from_str, from_none], obj.get("detachedFromSpawningParentSessionId")) disabled_instruction_sources = from_union([lambda x: from_list(from_str, x), from_none], obj.get("disabledInstructionSources")) + disabled_mcp_servers = from_union([lambda x: from_list(from_str, x), from_none], obj.get("disabledMcpServers")) disabled_skills = from_union([lambda x: from_list(from_str, x), from_none], obj.get("disabledSkills")) enable_citations = from_union([from_bool, from_none], obj.get("enableCitations")) enable_file_change_tracking = from_union([from_bool, from_none], obj.get("enableFileChangeTracking")) @@ -25745,6 +26126,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")) @@ -25772,7 +26154,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_mcp_servers, 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 = {} @@ -25812,6 +26194,8 @@ def to_dict(self) -> dict: result["detachedFromSpawningParentSessionId"] = from_union([from_str, from_none], self.detached_from_spawning_parent_session_id) if self.disabled_instruction_sources is not None: result["disabledInstructionSources"] = from_union([lambda x: from_list(from_str, x), from_none], self.disabled_instruction_sources) + if self.disabled_mcp_servers is not None: + result["disabledMcpServers"] = from_union([lambda x: from_list(from_str, x), from_none], self.disabled_mcp_servers) if self.disabled_skills is not None: result["disabledSkills"] = from_union([lambda x: from_list(from_str, x), from_none], self.disabled_skills) if self.enable_citations is not None: @@ -25852,6 +26236,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: @@ -28448,6 +28834,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 @@ -28473,6 +28860,9 @@ class RPC: execute_command_result: ExecuteCommandResult extension: Extension extension_context_push_input: ExtensionContextPushInput + extension_launch_profile: ExtensionLaunchProfile + extension_launch_provider_resolve_request: ExtensionLaunchProviderResolveRequest + extension_launch_provider_resolve_result: ExtensionLaunchProviderResolveResult extension_list: ExtensionList extensions_disable_request: ExtensionsDisableRequest extensions_enable_request: ExtensionsEnableRequest @@ -28604,6 +28994,7 @@ class RPC: log_request: LogRequest log_result: LogResult lsp_initialize_request: LspInitializeRequest + managed_settings_read_result: ManagedSettingsReadResult marketplace_add_result: MarketplaceAddResult marketplace_browse_result: MarketplaceBrowseResult marketplace_info: MarketplaceInfo @@ -28798,13 +29189,17 @@ class RPC: permission_decision_approve_once: PermissionDecisionApproveOnce permission_decision_approve_permanently: PermissionDecisionApprovePermanently permission_decision_cancelled: PermissionDecisionCancelled + permission_decision_context: PermissionDecisionContext permission_decision_denied_by_content_exclusion_policy: PermissionDecisionDeniedByContentExclusionPolicy permission_decision_denied_by_permission_request_hook: PermissionDecisionDeniedByPermissionRequestHook permission_decision_denied_by_rules: PermissionDecisionDeniedByRules permission_decision_denied_interactively_by_user: PermissionDecisionDeniedInteractivelyByUser permission_decision_denied_no_approval_rule_and_could_not_request_from_user: PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser + permission_decision_outcome: PermissionDecisionOutcome permission_decision_reject: PermissionDecisionReject permission_decision_request: PermissionDecisionRequest + permission_decision_source: PermissionDecisionSource + permission_decision_surface: PermissionDecisionSurface permission_decision_user_not_available: PermissionDecisionUserNotAvailable permission_location_add_tool_approval_params: PermissionLocationAddToolApprovalParams permission_location_apply_params: PermissionLocationApplyParams @@ -29083,6 +29478,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 @@ -29449,6 +29846,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")) @@ -29474,6 +29872,9 @@ def from_dict(obj: Any) -> 'RPC': execute_command_result = ExecuteCommandResult.from_dict(obj.get("ExecuteCommandResult")) extension = Extension.from_dict(obj.get("Extension")) extension_context_push_input = ExtensionContextPushInput.from_dict(obj.get("ExtensionContextPushInput")) + extension_launch_profile = ExtensionLaunchProfile.from_dict(obj.get("ExtensionLaunchProfile")) + extension_launch_provider_resolve_request = ExtensionLaunchProviderResolveRequest.from_dict(obj.get("ExtensionLaunchProviderResolveRequest")) + extension_launch_provider_resolve_result = ExtensionLaunchProviderResolveResult.from_dict(obj.get("ExtensionLaunchProviderResolveResult")) extension_list = ExtensionList.from_dict(obj.get("ExtensionList")) extensions_disable_request = ExtensionsDisableRequest.from_dict(obj.get("ExtensionsDisableRequest")) extensions_enable_request = ExtensionsEnableRequest.from_dict(obj.get("ExtensionsEnableRequest")) @@ -29605,6 +30006,7 @@ def from_dict(obj: Any) -> 'RPC': log_request = LogRequest.from_dict(obj.get("LogRequest")) log_result = LogResult.from_dict(obj.get("LogResult")) lsp_initialize_request = LspInitializeRequest.from_dict(obj.get("LspInitializeRequest")) + managed_settings_read_result = ManagedSettingsReadResult.from_dict(obj.get("ManagedSettingsReadResult")) marketplace_add_result = MarketplaceAddResult.from_dict(obj.get("MarketplaceAddResult")) marketplace_browse_result = MarketplaceBrowseResult.from_dict(obj.get("MarketplaceBrowseResult")) marketplace_info = MarketplaceInfo.from_dict(obj.get("MarketplaceInfo")) @@ -29799,13 +30201,17 @@ def from_dict(obj: Any) -> 'RPC': permission_decision_approve_once = PermissionDecisionApproveOnce.from_dict(obj.get("PermissionDecisionApproveOnce")) permission_decision_approve_permanently = PermissionDecisionApprovePermanently.from_dict(obj.get("PermissionDecisionApprovePermanently")) permission_decision_cancelled = PermissionDecisionCancelled.from_dict(obj.get("PermissionDecisionCancelled")) + permission_decision_context = PermissionDecisionContext.from_dict(obj.get("PermissionDecisionContext")) permission_decision_denied_by_content_exclusion_policy = PermissionDecisionDeniedByContentExclusionPolicy.from_dict(obj.get("PermissionDecisionDeniedByContentExclusionPolicy")) permission_decision_denied_by_permission_request_hook = PermissionDecisionDeniedByPermissionRequestHook.from_dict(obj.get("PermissionDecisionDeniedByPermissionRequestHook")) permission_decision_denied_by_rules = PermissionDecisionDeniedByRules.from_dict(obj.get("PermissionDecisionDeniedByRules")) permission_decision_denied_interactively_by_user = PermissionDecisionDeniedInteractivelyByUser.from_dict(obj.get("PermissionDecisionDeniedInteractivelyByUser")) permission_decision_denied_no_approval_rule_and_could_not_request_from_user = PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser.from_dict(obj.get("PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser")) + permission_decision_outcome = PermissionDecisionOutcome(obj.get("PermissionDecisionOutcome")) permission_decision_reject = PermissionDecisionReject.from_dict(obj.get("PermissionDecisionReject")) permission_decision_request = PermissionDecisionRequest.from_dict(obj.get("PermissionDecisionRequest")) + permission_decision_source = PermissionDecisionSource(obj.get("PermissionDecisionSource")) + permission_decision_surface = PermissionDecisionSurface(obj.get("PermissionDecisionSurface")) permission_decision_user_not_available = PermissionDecisionUserNotAvailable.from_dict(obj.get("PermissionDecisionUserNotAvailable")) permission_location_add_tool_approval_params = PermissionLocationAddToolApprovalParams.from_dict(obj.get("PermissionLocationAddToolApprovalParams")) permission_location_apply_params = PermissionLocationApplyParams.from_dict(obj.get("PermissionLocationApplyParams")) @@ -30084,6 +30490,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")) @@ -30337,7 +30745,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_clear_context_request, history_clear_context_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_launch_profile, extension_launch_provider_resolve_request, extension_launch_provider_resolve_result, 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_clear_context_request, history_clear_context_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, managed_settings_read_result, 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_context, 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_outcome, permission_decision_reject, permission_decision_request, permission_decision_source, permission_decision_surface, 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 = {} @@ -30450,6 +30858,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) @@ -30475,6 +30884,9 @@ def to_dict(self) -> dict: result["ExecuteCommandResult"] = to_class(ExecuteCommandResult, self.execute_command_result) result["Extension"] = to_class(Extension, self.extension) result["ExtensionContextPushInput"] = to_class(ExtensionContextPushInput, self.extension_context_push_input) + result["ExtensionLaunchProfile"] = to_class(ExtensionLaunchProfile, self.extension_launch_profile) + result["ExtensionLaunchProviderResolveRequest"] = to_class(ExtensionLaunchProviderResolveRequest, self.extension_launch_provider_resolve_request) + result["ExtensionLaunchProviderResolveResult"] = to_class(ExtensionLaunchProviderResolveResult, self.extension_launch_provider_resolve_result) result["ExtensionList"] = to_class(ExtensionList, self.extension_list) result["ExtensionsDisableRequest"] = to_class(ExtensionsDisableRequest, self.extensions_disable_request) result["ExtensionsEnableRequest"] = to_class(ExtensionsEnableRequest, self.extensions_enable_request) @@ -30606,6 +31018,7 @@ def to_dict(self) -> dict: result["LogRequest"] = to_class(LogRequest, self.log_request) result["LogResult"] = to_class(LogResult, self.log_result) result["LspInitializeRequest"] = to_class(LspInitializeRequest, self.lsp_initialize_request) + result["ManagedSettingsReadResult"] = to_class(ManagedSettingsReadResult, self.managed_settings_read_result) result["MarketplaceAddResult"] = to_class(MarketplaceAddResult, self.marketplace_add_result) result["MarketplaceBrowseResult"] = to_class(MarketplaceBrowseResult, self.marketplace_browse_result) result["MarketplaceInfo"] = to_class(MarketplaceInfo, self.marketplace_info) @@ -30800,13 +31213,17 @@ def to_dict(self) -> dict: result["PermissionDecisionApproveOnce"] = to_class(PermissionDecisionApproveOnce, self.permission_decision_approve_once) result["PermissionDecisionApprovePermanently"] = to_class(PermissionDecisionApprovePermanently, self.permission_decision_approve_permanently) result["PermissionDecisionCancelled"] = to_class(PermissionDecisionCancelled, self.permission_decision_cancelled) + result["PermissionDecisionContext"] = to_class(PermissionDecisionContext, self.permission_decision_context) result["PermissionDecisionDeniedByContentExclusionPolicy"] = to_class(PermissionDecisionDeniedByContentExclusionPolicy, self.permission_decision_denied_by_content_exclusion_policy) result["PermissionDecisionDeniedByPermissionRequestHook"] = to_class(PermissionDecisionDeniedByPermissionRequestHook, self.permission_decision_denied_by_permission_request_hook) result["PermissionDecisionDeniedByRules"] = to_class(PermissionDecisionDeniedByRules, self.permission_decision_denied_by_rules) result["PermissionDecisionDeniedInteractivelyByUser"] = to_class(PermissionDecisionDeniedInteractivelyByUser, self.permission_decision_denied_interactively_by_user) result["PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser"] = to_class(PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser, self.permission_decision_denied_no_approval_rule_and_could_not_request_from_user) + result["PermissionDecisionOutcome"] = to_enum(PermissionDecisionOutcome, self.permission_decision_outcome) result["PermissionDecisionReject"] = to_class(PermissionDecisionReject, self.permission_decision_reject) result["PermissionDecisionRequest"] = to_class(PermissionDecisionRequest, self.permission_decision_request) + result["PermissionDecisionSource"] = to_enum(PermissionDecisionSource, self.permission_decision_source) + result["PermissionDecisionSurface"] = to_enum(PermissionDecisionSurface, self.permission_decision_surface) result["PermissionDecisionUserNotAvailable"] = to_class(PermissionDecisionUserNotAvailable, self.permission_decision_user_not_available) result["PermissionLocationAddToolApprovalParams"] = to_class(PermissionLocationAddToolApprovalParams, self.permission_location_add_tool_approval_params) result["PermissionLocationApplyParams"] = to_class(PermissionLocationApplyParams, self.permission_location_apply_params) @@ -31085,6 +31502,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) @@ -31960,6 +32379,16 @@ def __init__(self, client: "JsonRpcClient"): self.settings = ServerUserSettingsApi(client) +# Experimental: this API group is experimental and may change or be removed. +class ServerManagedSettingsApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + + async def read(self, *, timeout: float | None = None) -> ManagedSettingsReadResult: + "Discovers device-managed settings from production MDM and managed-file sources, validates them against the runtime-owned managed-settings schema, and returns the canonical JSON without requiring a session.\n\nReturns:\n Validated device-managed settings discovered before a session exists." + return ManagedSettingsReadResult.from_dict(await self._client.request("managedSettings.read", {}, **_timeout_kwargs(timeout))) + + # Experimental: this API group is experimental and may change or be removed. class ServerRuntimeApi: def __init__(self, client: "JsonRpcClient"): @@ -32147,6 +32576,7 @@ def __init__(self, client: "JsonRpcClient"): self.instructions = ServerInstructionsApi(client) self.commands = ServerCommandsApi(client) self.user = ServerUserApi(client) + self.managed_settings = ServerManagedSettingsApi(client) self.runtime = ServerRuntimeApi(client) self.session_fs = ServerSessionFsApi(client) self.llm_inference = ServerLlmInferenceApi(client) @@ -32158,6 +32588,10 @@ async def ping(self, params: PingRequest, *, timeout: float | None = None) -> Pi params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return PingResult.from_dict(await self._client.request("ping", params_dict, **_timeout_kwargs(timeout))) + async def register_extension_launch_provider(self, *, timeout: float | None = None) -> None: + "Registers the calling SDK client as the per-entrypoint extension launch provider. Call before creating any sessions. When omitted, the runtime temporarily falls back to its built-in Node launcher for backward compatibility.\n\n.. warning:: This API is experimental and may change or be removed in future versions." + await self._client.request("registerExtensionLaunchProvider", {}, **_timeout_kwargs(timeout)) + # Experimental: this API group is experimental and may change or be removed. class _InternalServerSessionsApi: @@ -34118,6 +34552,12 @@ async def invoke(self, params: _HookInvokeRequest) -> _HookInvokeResponse: "Dispatches one SDK callback hook from the runtime to the connection that registered it. Internal transport plumbing: clients opt in through session initialization and the Rust hook processor owns ordering, policy, timeout, and callback routing.\n\nArgs:\n params: Runtime-owned wire payload for a server-to-client hook callback invocation.\n\nReturns:\n Optional output returned by an SDK callback hook." pass +# Experimental: this API group is experimental and may change or be removed. +class ExtensionLaunchProviderHandler(Protocol): + async def resolve(self, params: ExtensionLaunchProviderResolveRequest) -> ExtensionLaunchProviderResolveResult: + "Asks the registered SDK client to resolve an opaque process launch profile for one discovered extension entrypoint immediately before launch or reload. The provider must respond within 15 seconds.\n\nArgs:\n params: A discovered extension entrypoint that the registered integrator may classify and resolve to an opaque launch profile.\n\nReturns:\n The launch profile for a supported entrypoint. Omit launch when the provider does not support the entrypoint." + pass + # Experimental: this API group is experimental and may change or be removed. class LlmInferenceHandler(Protocol): async def http_request_start(self, params: LlmInferenceHTTPRequestStartRequest) -> LlmInferenceHTTPRequestStartResult: @@ -34136,6 +34576,7 @@ async def event(self, params: GitHubTelemetryNotification) -> None: @dataclass class ClientGlobalApiHandlers: hooks: HooksHandler | None = None + extension_launch_provider: ExtensionLaunchProviderHandler | None = None llm_inference: LlmInferenceHandler | None = None git_hub_telemetry: GitHubTelemetryHandler | None = None @@ -34156,6 +34597,13 @@ async def handle_hooks_invoke(params: dict) -> dict | None: result = await handler.invoke(request) return result.to_dict() client.set_request_handler("hooks.invoke", handle_hooks_invoke) + async def handle_extension_launch_provider_resolve(params: dict) -> dict | None: + request = ExtensionLaunchProviderResolveRequest.from_dict(params) + handler = handlers.extension_launch_provider + if handler is None: raise RuntimeError("No extension_launch_provider client-global handler registered") + result = await handler.resolve(request) + return result.to_dict() + client.set_request_handler("extensionLaunchProvider.resolve", handle_extension_launch_provider_resolve) async def handle_llm_inference_http_request_start(params: dict) -> dict | None: request = LlmInferenceHTTPRequestStartRequest.from_dict(params) handler = handlers.llm_inference @@ -34258,6 +34706,7 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "CanvasProviderOpenResult", "CanvasSessionContext", "CapiSessionOptions", + "Categories", "ClientGlobalApiHandlers", "ClientSessionApiHandlers", "CommandList", @@ -34305,6 +34754,7 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "DebugCollectLogsResultKind", "DebugCollectLogsSkippedEntry", "DebugCollectLogsSource", + "DisableBypassPermissionsMode", "DiscoveredCanvas", "DiscoveredExtension", "DiscoveredExtensionMode", @@ -34334,6 +34784,10 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "Extension", "ExtensionContextPushInput", "ExtensionContextPushInputType", + "ExtensionLaunchProfile", + "ExtensionLaunchProviderHandler", + "ExtensionLaunchProviderResolveRequest", + "ExtensionLaunchProviderResolveResult", "ExtensionList", "ExtensionSource", "ExtensionStatus", @@ -34579,6 +35033,7 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "MCPToolUIVisibility", "MCPTools", "MCPUnregisterExternalClientRequest", + "ManagedSettingsReadResult", "MarketplaceAddResult", "MarketplaceBrowseResult", "MarketplaceInfo", @@ -34716,6 +35171,7 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "PermissionDecisionApprovedKind", "PermissionDecisionCancelled", "PermissionDecisionCancelledKind", + "PermissionDecisionContext", "PermissionDecisionDeniedByContentExclusionPolicy", "PermissionDecisionDeniedByContentExclusionPolicyKind", "PermissionDecisionDeniedByPermissionRequestHook", @@ -34727,9 +35183,12 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser", "PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUserKind", "PermissionDecisionKind", + "PermissionDecisionOutcome", "PermissionDecisionReject", "PermissionDecisionRejectKind", "PermissionDecisionRequest", + "PermissionDecisionSource", + "PermissionDecisionSurface", "PermissionDecisionUserNotAvailable", "PermissionDecisionUserNotAvailableKind", "PermissionLocationAddToolApprovalParams", @@ -34985,6 +35444,7 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "ServerInstructionSourceList", "ServerInstructionsApi", "ServerLlmInferenceApi", + "ServerManagedSettingsApi", "ServerMcpApi", "ServerMcpConfigApi", "ServerModelsApi", @@ -35071,6 +35531,8 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "SessionListFilter", "SessionLoadDeferredRepoHooksResult", "SessionLogLevel", + "SessionManagedPermissions", + "SessionManagedSettings", "SessionMcpAppsCallToolResult", "SessionMetadataSnapshot", "SessionModelList", diff --git a/python/copilot/generated/session_events.py b/python/copilot/generated/session_events.py index 6978a5a6d6..d11317a82b 100644 --- a/python/copilot/generated/session_events.py +++ b/python/copilot/generated/session_events.py @@ -1208,13 +1208,14 @@ def to_dict(self) -> dict: # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class SessionManagedSettingsResolvedData: - "Enterprise managed-settings resolution: the effective managed settings the session applied and where they came from, so SDK clients can show users what is enterprise-managed and by which authority. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted; for a session-independent pull, use the SDK `getManagedSettings()` API, which returns the identical payload. Managed settings have a single authoritative source, so the highest-authority present layer (server > device) wins wholesale; `bypassPermissionsDisabled` is deny-wins across layers. Marked experimental while the managed-settings surface stabilizes." + "Enterprise managed-settings resolution: the effective managed settings the session applied and which channels contributed, so SDK clients can show users what is enterprise-managed. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted. Device values take precedence over server values per ordinary key, while permissions compose restrictively across device, server, and SDK-client layers. The account-scoped `getManagedSettings()` API does not include session-local client injection. Marked experimental while the managed-settings surface stabilizes." bypass_permissions_disabled: bool device_managed: bool fail_closed: bool managed_keys: list[str] server_managed: bool source: ManagedSettingsResolvedSource + client_managed: bool | None = None permissions_allow_intersected: bool | None = None settings: Any = None @@ -1227,6 +1228,7 @@ def from_dict(obj: Any) -> "SessionManagedSettingsResolvedData": managed_keys = from_list(from_str, obj.get("managedKeys")) server_managed = from_bool(obj.get("serverManaged")) source = parse_enum(ManagedSettingsResolvedSource, obj.get("source")) + client_managed = from_union([from_none, from_bool], obj.get("clientManaged")) permissions_allow_intersected = from_union([from_none, from_bool], obj.get("permissionsAllowIntersected")) settings = obj.get("settings") return SessionManagedSettingsResolvedData( @@ -1236,6 +1238,7 @@ def from_dict(obj: Any) -> "SessionManagedSettingsResolvedData": managed_keys=managed_keys, server_managed=server_managed, source=source, + client_managed=client_managed, permissions_allow_intersected=permissions_allow_intersected, settings=settings, ) @@ -1248,6 +1251,8 @@ def to_dict(self) -> dict: result["managedKeys"] = from_list(from_str, self.managed_keys) result["serverManaged"] = from_bool(self.server_managed) result["source"] = to_enum(ManagedSettingsResolvedSource, self.source) + if self.client_managed is not None: + result["clientManaged"] = from_union([from_none, from_bool], self.client_managed) if self.permissions_allow_intersected is not None: result["permissionsAllowIntersected"] = from_union([from_none, from_bool], self.permissions_allow_intersected) if self.settings is not None: @@ -7784,6 +7789,7 @@ class SkillsLoadedSkill: source: SkillSource user_invocable: bool argument_hint: str | None = None + command_name: str | None = None path: str | None = None @staticmethod @@ -7795,6 +7801,7 @@ def from_dict(obj: Any) -> "SkillsLoadedSkill": source = parse_enum(SkillSource, obj.get("source")) user_invocable = from_bool(obj.get("userInvocable")) argument_hint = from_union([from_none, from_str], obj.get("argumentHint")) + command_name = from_union([from_none, from_str], obj.get("commandName")) path = from_union([from_none, from_str], obj.get("path")) return SkillsLoadedSkill( description=description, @@ -7803,6 +7810,7 @@ def from_dict(obj: Any) -> "SkillsLoadedSkill": source=source, user_invocable=user_invocable, argument_hint=argument_hint, + command_name=command_name, path=path, ) @@ -7815,6 +7823,8 @@ def to_dict(self) -> dict: result["userInvocable"] = from_bool(self.user_invocable) if self.argument_hint is not None: result["argumentHint"] = from_union([from_none, from_str], self.argument_hint) + if self.command_name is not None: + result["commandName"] = from_union([from_none, from_str], self.command_name) if self.path is not None: result["path"] = from_union([from_none, from_str], self.path) return result @@ -10134,12 +10144,16 @@ class ManagedSettingsEnforcedEscalation(Enum): class ManagedSettingsResolvedSource(Enum): - "Which channel supplied the effective enterprise managed settings (highest-authority present layer wins wholesale)" - # Account/org policy self-fetched from the GitHub managed-settings endpoint (higher authority). + "Summary of which managed-settings channels contributed to the effective session policy. Use the per-channel booleans for exact provenance." + # Only the server/account channel contributed. SERVER = "server" - # Device-level MDM policy discovered from plist/registry/file (lower authority). + # Only the device MDM/plist/registry/file channel contributed. DEVICE = "device" - # No managed policy is in force (no layer contributed). + # Only session-local SDK-host injection contributed. + CLIENT = "client" + # More than one channel contributed. Ordinary keys resolve device over server per key, while permissions compose restrictively across all present layers. + MIXED = "mixed" + # No managed policy is in force (no channel contributed). NONE = "none" diff --git a/python/test_client.py b/python/test_client.py index f101fc3968..2375bc98a9 100644 --- a/python/test_client.py +++ b/python/test_client.py @@ -28,6 +28,8 @@ CloudSessionRepository, CopilotExpAssignmentResponse, ExpConfigEntry, + ManagedSettings, + ManagedSettingsPermissions, ModelBilling, ModelCapabilities, ModelInfo, @@ -651,6 +653,61 @@ 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(**)"], + ) + ), + ) + resumed_session = await client.resume_session( + session.session_id, + on_permission_request=PermissionHandler.approve_all, + managed_settings=ManagedSettings( + permissions=ManagedSettingsPermissions(ask=["Domain(publish.example)"]) + ), + ) + + 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": { + "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: @@ -691,6 +748,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: @@ -698,7 +756,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: @@ -707,16 +765,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/python/test_event_forward_compatibility.py b/python/test_event_forward_compatibility.py index 1ffbd59c54..2e8015a97d 100644 --- a/python/test_event_forward_compatibility.py +++ b/python/test_event_forward_compatibility.py @@ -18,10 +18,12 @@ ElicitationCompletedAction, ElicitationRequestedMode, ElicitationRequestedSchema, + ManagedSettingsResolvedSource, PermissionPromptRequestMemory, PermissionRequestMemory, PermissionRequestMemoryAction, SessionEventType, + SessionManagedSettingsResolvedData, SessionTaskCompleteData, UserMessageAgentMode, session_event_from_dict, @@ -136,6 +138,42 @@ def test_explicit_generated_symbols_remain_available(self): ) assert schema.to_dict()["type"] == "object" + def test_managed_settings_client_provenance_round_trips(self): + """Managed settings events should preserve truthful client provenance.""" + assert [source.value for source in ManagedSettingsResolvedSource] == [ + "server", + "device", + "client", + "mixed", + "none", + ] + + client = SessionManagedSettingsResolvedData( + bypass_permissions_disabled=True, + client_managed=True, + device_managed=False, + fail_closed=False, + managed_keys=["permissions"], + server_managed=False, + source=ManagedSettingsResolvedSource.CLIENT, + ) + serialized = client.to_dict() + assert serialized["source"] == "client" + assert serialized["clientManaged"] is True + assert SessionManagedSettingsResolvedData.from_dict(serialized) == client + + mixed = SessionManagedSettingsResolvedData( + bypass_permissions_disabled=True, + device_managed=True, + fail_closed=False, + managed_keys=["permissions"], + server_managed=True, + source=ManagedSettingsResolvedSource.MIXED, + ) + serialized = mixed.to_dict() + assert serialized["source"] == "mixed" + assert "clientManaged" not in serialized + def test_data_shim_preserves_raw_mapping_values(self): """Compatibility Data should keep arbitrary nested mappings as plain dicts.""" parsed = Data.from_dict( diff --git a/rust/examples/manual_tool_resume.rs b/rust/examples/manual_tool_resume.rs index 85add798a1..ad8ad5a044 100644 --- a/rust/examples/manual_tool_resume.rs +++ b/rust/examples/manual_tool_resume.rs @@ -113,6 +113,7 @@ async fn main() -> Result<(), Box> { .rpc() .permissions() .handle_pending_permission_request(PermissionDecisionRequest { + decision_context: None, request_id: permission.request_id, result: PermissionDecision::ApproveOnce(PermissionDecisionApproveOnce { approved_interactively: None, diff --git a/rust/src/generated/api_types.rs b/rust/src/generated/api_types.rs index b64a13c4a5..c93f34cf64 100644 --- a/rust/src/generated/api_types.rs +++ b/rust/src/generated/api_types.rs @@ -62,6 +62,8 @@ pub mod rpc_methods { pub const EXTENSIONS_ENABLE: &str = "extensions.enable"; /// `extensions.disable` pub const EXTENSIONS_DISABLE: &str = "extensions.disable"; + /// `registerExtensionLaunchProvider` + pub const REGISTEREXTENSIONLAUNCHPROVIDER: &str = "registerExtensionLaunchProvider"; /// `plugins.list` pub const PLUGINS_LIST: &str = "plugins.list"; /// `plugins.install` @@ -108,6 +110,8 @@ pub mod rpc_methods { pub const USER_SETTINGS_GET: &str = "user.settings.get"; /// `user.settings.set` pub const USER_SETTINGS_SET: &str = "user.settings.set"; + /// `managedSettings.read` + pub const MANAGEDSETTINGS_READ: &str = "managedSettings.read"; /// `runtime.shutdown` pub const RUNTIME_SHUTDOWN: &str = "runtime.shutdown"; /// `sessionFs.setProvider` @@ -3706,6 +3710,62 @@ pub struct ExtensionContextPushInput { pub r#type: ExtensionContextPushInputType, } +/// Opaque integrator-owned process launch profile for one extension entrypoint. +/// +///

+/// +/// **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 ExtensionLaunchProfile { + /// Opaque integrator-defined arguments passed to the executable. The runtime does not append the extension entrypoint. + pub args: Vec, + /// Opaque integrator-defined environment variables. Runtime-owned COPILOT_SDK_PATH, SESSION_ID, and COPILOT_EXTENSION_PARENT_PID values take precedence. + pub env: HashMap, + /// Executable used to launch the extension entrypoint. + pub executable: String, +} + +/// A discovered extension entrypoint that the registered integrator may classify and resolve to an opaque launch profile. +/// +///
+/// +/// **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 ExtensionLaunchProviderResolveRequest { + /// Source-qualified extension identifier. + pub id: String, + /// Absolute path to the discovered extension entrypoint. + pub module_path: String, + /// Human-readable extension name. + pub name: String, + /// Discovery source for the extension entrypoint. + pub source: ExtensionSource, +} + +/// The launch profile for a supported entrypoint. Omit launch when the provider does not support the entrypoint. +/// +///
+/// +/// **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 ExtensionLaunchProviderResolveResult { + /// Opaque launch profile, omitted when this provider does not support the entrypoint. + #[serde(skip_serializing_if = "Option::is_none")] + pub launch: Option, +} + /// Extensions discovered for the session, with their current status. /// ///
@@ -5904,6 +5964,25 @@ pub struct LspInitializeRequest { pub working_directory: Option, } +/// Validated device-managed settings discovered before a session exists. +/// +///
+/// +/// **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 ManagedSettingsReadResult { + /// Discovery or validation error text when managed settings could not be read safely. + #[serde(skip_serializing_if = "Option::is_none")] + pub error_message: Option, + /// Validated, canonical managed-settings JSON. Omitted when no managed settings were discovered or when discovered settings failed validation. + #[serde(skip_serializing_if = "Option::is_none")] + pub settings_json: Option, +} + /// Result of registering a new marketplace. /// ///
@@ -7605,6 +7684,26 @@ pub struct MemoryConfiguration { pub enabled: bool, } +/// The six normalized `/context` header buckets, computed from the same tokenization as `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` describe window capacity rather than occupied context, so the values do not sum to `totalTokens`. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct MetadataContextAttributionResultContextAttributionCategories { + /// Output reserve plus post-blocking-threshold buffer. + pub buffer: i64, + /// Custom-instructions tokens (0 when none are configured). + pub custom_instructions: i64, + /// Remaining unused window capacity (clamped at 0). + pub free_space: i64, + /// MCP tool-definition tokens. + pub mcp_tools: i64, + /// Conversation (user/assistant/tool) message tokens. + pub messages: i64, + /// System prompt tokens, excluding custom instructions. + pub system_prompt: i64, + /// Non-MCP tool-definition tokens. + pub system_tools: i64, +} + /// Successful compaction history for the session. #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -7636,10 +7735,24 @@ pub struct MetadataContextAttributionResultContextAttributionEntriesItem { #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct MetadataContextAttributionResultContextAttribution { + /// Output reserve plus the tokens past the buffer-exhaustion blocking threshold. Mirrors `SessionContextInfo.bufferTokens`. + pub buffer_tokens: i64, + /// The six normalized `/context` header buckets, computed from the same tokenization as `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` describe window capacity rather than occupied context, so the values do not sum to `totalTokens`. + pub categories: MetadataContextAttributionResultContextAttributionCategories, /// Successful compaction history for the session. pub compactions: MetadataContextAttributionResultContextAttributionCompactions, + /// Token count at which background compaction starts. Mirrors `SessionContextInfo.compactionThreshold`. + pub compaction_threshold: i64, /// Flat list of per-source attribution entries. Group by `kind` and render unrecognized kinds generically. Nesting and rollups are expressed via `parentId`. pub entries: Vec, + /// Prompt limit plus the model's output reserve: the full context window `categories.freeSpace` and `categories.buffer` are measured against. Mirrors `SessionContextInfo.limit`. + pub limit: i64, + /// The concrete model id the entire breakdown was tokenized against (feeds the per-model token multiplier). Under `Auto` (Free/Student) this is the resolved model, not the literal `auto` sentinel, so totals are not undercounted. A single-model approximation of a potentially multi-model Auto session. + pub model_id: String, + /// How `modelId` was chosen. Not a closed set — tolerate unknown values. Known values today: `autoResolved` (the model Auto resolved to), `selected` (the user's explicitly selected model), `default` (a fallback before any model is known). + pub model_source: String, + /// Maximum prompt tokens the resolved model accepts — the denominator for a `##k/###k` context-usage display. Mirrors `SessionContextInfo.promptTokenLimit`. + pub prompt_token_limit: i64, /// Total token count of the current context window the entries are measured against (system message + conversation messages + tool definitions — the same total reported by /context). Divide an entry's `tokens` by this to derive its share. pub total_tokens: i64, } @@ -9262,6 +9375,25 @@ pub struct PermissionDecisionDeniedByPermissionRequestHook { pub message: Option, } +/// Optional informational context describing how and where the permission decision was made. This does not affect permission behavior. +/// +///
+/// +/// **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 PermissionDecisionContext { + /// Disposition of the permission request as observed by the responding client. + pub outcome: PermissionDecisionOutcome, + /// Controlled reason or actor responsible for the response. + pub source: PermissionDecisionSource, + /// Client surface that submitted the response. + pub surface: PermissionDecisionSurface, +} + /// Pending permission request ID and the decision to apply (approve/reject and scope). /// ///
@@ -9273,6 +9405,9 @@ pub struct PermissionDecisionDeniedByPermissionRequestHook { #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PermissionDecisionRequest { + /// Optional informational context describing how and where this response was made. Omit it to preserve legacy behavior without attributing an origin. + #[serde(skip_serializing_if = "Option::is_none")] + pub decision_context: Option, /// Request ID of the pending permission request pub request_id: RequestId, /// The client's response to the pending permission prompt @@ -12337,9 +12472,9 @@ pub struct SandboxConfig { /// Whether to auto-add the current working directory to readwritePaths. Default: true. #[serde(skip_serializing_if = "Option::is_none")] pub add_current_working_directory: Option, - /// Whether to auto-grant read access to common developer-tool caches, registries, and toolchains in their default home locations (cargo, go, npm, Maven, and more), plus read-write access to (and, on Unix, up-front creation of) the scratch caches builds write on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so builds work without exporting CARGO_HOME/GOPATH/etc. Default: true (enabled by default; set to false to opt out). + /// Whether to auto-grant read access to common developer-tool caches, registries, and toolchains in their default home locations (cargo, go, npm, Maven, and more), plus read-write access to (and, on Unix, up-front creation of) the scratch caches builds write on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so builds work without extra configuration; a relocated CARGO_HOME additionally gets its Cargo lock files granted read-write. Default: true (enabled by default; set to false to opt out). #[serde(skip_serializing_if = "Option::is_none")] - pub allow_dev_tool_caches: Option, + pub allow_dev_tool_access: Option, /// Whether sandboxing is enabled for the session. pub enabled: bool, /// Whether to export `GH_TOKEN` so the `gh` CLI authenticates inside the sandbox without the OS keyring the sandbox blocks. Default: false (opt-in). @@ -12842,6 +12977,9 @@ pub struct ServerSkill { /// Optional freeform hint describing the skill's expected arguments, from the `argument-hint` frontmatter field #[serde(skip_serializing_if = "Option::is_none")] pub argument_hint: Option, + /// Canonical slash command name used to invoke the skill, without the leading '/' + #[serde(skip_serializing_if = "Option::is_none")] + pub command_name: Option, /// Description of what the skill does pub description: String, /// Whether the skill is currently enabled (based on global config) @@ -12940,6 +13078,26 @@ pub struct SessionBulkDeleteResult { pub freed_bytes: HashMap, } +/// The six normalized `/context` header buckets, computed from the same tokenization as `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` describe window capacity rather than occupied context, so the values do not sum to `totalTokens`. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SessionContextAttributionCategories { + /// Output reserve plus post-blocking-threshold buffer. + pub buffer: i64, + /// Custom-instructions tokens (0 when none are configured). + pub custom_instructions: i64, + /// Remaining unused window capacity (clamped at 0). + pub free_space: i64, + /// MCP tool-definition tokens. + pub mcp_tools: i64, + /// Conversation (user/assistant/tool) message tokens. + pub messages: i64, + /// System prompt tokens, excluding custom instructions. + pub system_prompt: i64, + /// Non-MCP tool-definition tokens. + pub system_tools: i64, +} + /// Successful compaction history for the session. #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -12978,10 +13136,24 @@ pub struct SessionContextAttributionEntriesItem { #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SessionContextAttribution { + /// Output reserve plus the tokens past the buffer-exhaustion blocking threshold. Mirrors `SessionContextInfo.bufferTokens`. + pub buffer_tokens: i64, + /// The six normalized `/context` header buckets, computed from the same tokenization as `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` describe window capacity rather than occupied context, so the values do not sum to `totalTokens`. + pub categories: SessionContextAttributionCategories, /// Successful compaction history for the session. pub compactions: SessionContextAttributionCompactions, + /// Token count at which background compaction starts. Mirrors `SessionContextInfo.compactionThreshold`. + pub compaction_threshold: i64, /// Flat list of per-source attribution entries. Group by `kind` and render unrecognized kinds generically. Nesting and rollups are expressed via `parentId`. pub entries: Vec, + /// Prompt limit plus the model's output reserve: the full context window `categories.freeSpace` and `categories.buffer` are measured against. Mirrors `SessionContextInfo.limit`. + pub limit: i64, + /// The concrete model id the entire breakdown was tokenized against (feeds the per-model token multiplier). Under `Auto` (Free/Student) this is the resolved model, not the literal `auto` sentinel, so totals are not undercounted. A single-model approximation of a potentially multi-model Auto session. + pub model_id: String, + /// How `modelId` was chosen. Not a closed set — tolerate unknown values. Known values today: `autoResolved` (the model Auto resolved to), `selected` (the user's explicitly selected model), `default` (a fallback before any model is known). + pub model_source: String, + /// Maximum prompt tokens the resolved model accepts — the denominator for a `##k/###k` context-usage display. Mirrors `SessionContextInfo.promptTokenLimit`. + pub prompt_token_limit: i64, /// Total token count of the current context window the entries are measured against (system message + conversation messages + tool definitions — the same total reported by /context). Divide an entry's `tokens` by this to derive its share. pub total_tokens: i64, } @@ -13793,6 +13965,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")] @@ -14087,6 +14299,9 @@ pub struct SessionOpenOptions { /// Instruction source IDs disabled for this session. #[serde(skip_serializing_if = "Option::is_none")] pub disabled_instruction_sources: Option>, + /// MCP server names disabled for this session. Disabled servers are not started or authenticated on create or cold resume. + #[serde(skip_serializing_if = "Option::is_none")] + pub disabled_mcp_servers: Option>, /// Skill IDs disabled for this session. #[serde(skip_serializing_if = "Option::is_none")] pub disabled_skills: Option>, @@ -14155,6 +14370,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, @@ -15751,6 +15969,9 @@ pub struct Skill { /// Optional freeform hint describing the skill's expected arguments, from the `argument-hint` frontmatter field #[serde(skip_serializing_if = "Option::is_none")] pub argument_hint: Option, + /// Canonical slash command name used to invoke the skill, without the leading '/' + #[serde(skip_serializing_if = "Option::is_none")] + pub command_name: Option, /// Description of what the skill does pub description: String, /// Whether the skill is currently enabled @@ -22068,6 +22289,26 @@ pub struct SessionMetadataGetContextAttributionParams { pub session_id: SessionId, } +/// The six normalized `/context` header buckets, computed from the same tokenization as `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` describe window capacity rather than occupied context, so the values do not sum to `totalTokens`. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SessionMetadataGetContextAttributionResultContextAttributionCategories { + /// Output reserve plus post-blocking-threshold buffer. + pub buffer: i64, + /// Custom-instructions tokens (0 when none are configured). + pub custom_instructions: i64, + /// Remaining unused window capacity (clamped at 0). + pub free_space: i64, + /// MCP tool-definition tokens. + pub mcp_tools: i64, + /// Conversation (user/assistant/tool) message tokens. + pub messages: i64, + /// System prompt tokens, excluding custom instructions. + pub system_prompt: i64, + /// Non-MCP tool-definition tokens. + pub system_tools: i64, +} + /// Successful compaction history for the session. #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -22099,10 +22340,24 @@ pub struct SessionMetadataGetContextAttributionResultContextAttributionEntriesIt #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SessionMetadataGetContextAttributionResultContextAttribution { + /// Output reserve plus the tokens past the buffer-exhaustion blocking threshold. Mirrors `SessionContextInfo.bufferTokens`. + pub buffer_tokens: i64, + /// The six normalized `/context` header buckets, computed from the same tokenization as `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` describe window capacity rather than occupied context, so the values do not sum to `totalTokens`. + pub categories: SessionMetadataGetContextAttributionResultContextAttributionCategories, /// Successful compaction history for the session. pub compactions: SessionMetadataGetContextAttributionResultContextAttributionCompactions, + /// Token count at which background compaction starts. Mirrors `SessionContextInfo.compactionThreshold`. + pub compaction_threshold: i64, /// Flat list of per-source attribution entries. Group by `kind` and render unrecognized kinds generically. Nesting and rollups are expressed via `parentId`. pub entries: Vec, + /// Prompt limit plus the model's output reserve: the full context window `categories.freeSpace` and `categories.buffer` are measured against. Mirrors `SessionContextInfo.limit`. + pub limit: i64, + /// The concrete model id the entire breakdown was tokenized against (feeds the per-model token multiplier). Under `Auto` (Free/Student) this is the resolved model, not the literal `auto` sentinel, so totals are not undercounted. A single-model approximation of a potentially multi-model Auto session. + pub model_id: String, + /// How `modelId` was chosen. Not a closed set — tolerate unknown values. Known values today: `autoResolved` (the model Auto resolved to), `selected` (the user's explicitly selected model), `default` (a fallback before any model is known). + pub model_source: String, + /// Maximum prompt tokens the resolved model accepts — the denominator for a `##k/###k` context-usage display. Mirrors `SessionContextInfo.promptTokenLimit`. + pub prompt_token_limit: i64, /// Total token count of the current context window the entries are measured against (system message + conversation messages + tool definitions — the same total reported by /context). Divide an entry's `tokens` by this to derive its share. pub total_tokens: i64, } @@ -24223,6 +24478,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 /// ///
@@ -26224,6 +26496,87 @@ pub enum PermissionDecision { DeniedByPermissionRequestHook(PermissionDecisionDeniedByPermissionRequestHook), } +/// Disposition of a permission request as observed by the responding client. +/// +///
+/// +/// **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 PermissionDecisionOutcome { + /// The request was approved automatically without a new human decision. + #[serde(rename = "auto_approved")] + AutoApproved, + /// The request was denied without an interactive user decision; source records why. + #[serde(rename = "autopilot_denied")] + AutopilotDenied, + /// The response came from an interactive user prompt. + #[serde(rename = "prompted_user")] + PromptedUser, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + +/// Controlled reason or actor responsible for a permission response. +/// +///
+/// +/// **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 PermissionDecisionSource { + /// The response followed the auto-approval judge recommendation. + #[serde(rename = "judge_recommendation")] + JudgeRecommendation, + /// A human supplied the response through an interactive prompt. + #[serde(rename = "human_response")] + HumanResponse, + /// The host applied a standing policy or override rather than a judge recommendation or human decision. + #[serde(rename = "host_policy")] + HostPolicy, + /// The host denied the request because no interactive user response was available. + #[serde(rename = "unattended_fallback")] + UnattendedFallback, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + +/// Client surface that submitted a permission response. +/// +///
+/// +/// **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 PermissionDecisionSurface { + /// The interactive Copilot CLI terminal UI. + #[serde(rename = "tui")] + Tui, + /// The non-interactive Copilot CLI prompt mode. + #[serde(rename = "prompt_mode")] + PromptMode, + /// The Copilot App client. + #[serde(rename = "copilot_app")] + CopilotApp, + /// A generic Copilot SDK client. + #[serde(rename = "sdk")] + Sdk, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// Approval scoped to specific command identifiers. #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum PermissionsLocationsAddToolApprovalDetailsCommandsKind { diff --git a/rust/src/generated/rpc.rs b/rust/src/generated/rpc.rs index 02404c1129..db69aa1e84 100644 --- a/rust/src/generated/rpc.rs +++ b/rust/src/generated/rpc.rs @@ -71,6 +71,13 @@ impl<'a> ClientRpc<'a> { } } + /// `managedSettings.*` sub-namespace. + pub fn managed_settings(&self) -> ClientRpcManagedSettings<'a> { + ClientRpcManagedSettings { + client: self.client, + } + } + /// `mcp.*` sub-namespace. pub fn mcp(&self) -> ClientRpcMcp<'a> { ClientRpcMcp { @@ -196,6 +203,29 @@ impl<'a> ClientRpc<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + /// Registers the calling SDK client as the per-entrypoint extension launch provider. Call before creating any sessions. When omitted, the runtime temporarily falls back to its built-in Node launcher for backward compatibility. + /// + /// Wire method: `registerExtensionLaunchProvider`. + /// + ///
+ /// + /// **Experimental.** This API is part of an experimental wire-protocol surface + /// and may change or be removed in future SDK or CLI releases. Pin both the + /// SDK and CLI versions if your code depends on it. + /// + ///
+ pub async fn register_extension_launch_provider(&self) -> Result<(), Error> { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call( + rpc_methods::REGISTEREXTENSIONLAUNCHPROVIDER, + Some(wire_params), + ) + .await?; + Ok(()) + } } /// `account.*` RPCs. @@ -756,6 +786,38 @@ impl<'a> ClientRpcLlmInference<'a> { } } +/// `managedSettings.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcManagedSettings<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcManagedSettings<'a> { + /// Discovers device-managed settings from production MDM and managed-file sources, validates them against the runtime-owned managed-settings schema, and returns the canonical JSON without requiring a session. + /// + /// Wire method: `managedSettings.read`. + /// + /// # Returns + /// + /// Validated device-managed settings discovered before a session exists. + /// + ///
+ /// + /// **Experimental.** This API is part of an experimental wire-protocol surface + /// and may change or be removed in future SDK or CLI releases. Pin both the + /// SDK and CLI versions if your code depends on it. + /// + ///
+ pub async fn read(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::MANAGEDSETTINGS_READ, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } +} + /// `mcp.*` RPCs. #[derive(Clone, Copy)] pub struct ClientRpcMcp<'a> { diff --git a/rust/src/generated/session_events.rs b/rust/src/generated/session_events.rs index ef3c395b10..f5bfab83f4 100644 --- a/rust/src/generated/session_events.rs +++ b/rust/src/generated/session_events.rs @@ -4447,7 +4447,7 @@ pub struct SessionAutoModeResolvedData { pub sticky_override: Option, } -/// Session event "session.managed_settings_resolved". Enterprise managed-settings resolution: the effective managed settings the session applied and where they came from, so SDK clients can show users what is enterprise-managed and by which authority. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted; for a session-independent pull, use the SDK `getManagedSettings()` API, which returns the identical payload. Managed settings have a single authoritative source, so the highest-authority present layer (server > device) wins wholesale; `bypassPermissionsDisabled` is deny-wins across layers. Marked experimental while the managed-settings surface stabilizes. +/// Session event "session.managed_settings_resolved". Enterprise managed-settings resolution: the effective managed settings the session applied and which channels contributed, so SDK clients can show users what is enterprise-managed. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted. Device values take precedence over server values per ordinary key, while permissions compose restrictively across device, server, and SDK-client layers. The account-scoped `getManagedSettings()` API does not include session-local client injection. Marked experimental while the managed-settings surface stabilizes. /// ///
/// @@ -4460,13 +4460,16 @@ pub struct SessionAutoModeResolvedData { pub struct SessionManagedSettingsResolvedData { /// Whether enterprise policy disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers, and forced on when `failClosed` is true. pub bypass_permissions_disabled: bool, - /// Whether the device (MDM/plist/registry/file) managed-settings layer was present + /// Whether a session-local permissions layer injected by the SDK host was present + #[serde(skip_serializing_if = "Option::is_none")] + pub client_managed: Option, + /// Whether an actual device MDM/plist/registry/file managed-settings layer was present pub device_managed: bool, /// Whether managed policy could not be determined (e.g. a failed server fetch) and the session fell back to the fail-closed restriction. When true, restrictions such as disabling bypass-permissions are enforced even though `settings` may be absent. pub fail_closed: bool, /// The setting keys under enterprise management in the effective managed settings (e.g. `model`, `enabledPlugins`, `permissions`). Empty when no managed settings are in force. pub managed_keys: Vec, - /// Whether server and device each supplied a permission allowlist, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. + /// Whether at least two managed sources supplied permission allowlists, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. #[serde(skip_serializing_if = "Option::is_none")] pub permissions_allow_intersected: Option, /// Whether the server (account/org) managed-settings layer was present @@ -4474,7 +4477,7 @@ pub struct SessionManagedSettingsResolvedData { /// The effective (resolved) managed settings values, so clients can render exactly what is enforced. Absent when no managed policy is in force. #[serde(skip_serializing_if = "Option::is_none")] pub settings: Option, - /// Which channel supplied the effective managed settings (the winning layer), or `none` when no policy is in force + /// Channel summary: `server`, `device`, or `client` when exactly one channel contributed; `mixed` when multiple channels contributed; otherwise `none`. Consult the per-channel booleans for exact provenance. pub source: ManagedSettingsResolvedSource, } @@ -4617,6 +4620,9 @@ pub struct SkillsLoadedSkill { /// Optional freeform hint describing the skill's expected arguments, from the `argument-hint` frontmatter field #[serde(skip_serializing_if = "Option::is_none")] pub argument_hint: Option, + /// Canonical slash command name used to invoke the skill, without the leading '/' + #[serde(skip_serializing_if = "Option::is_none")] + pub command_name: Option, /// Description of what the skill does pub description: String, /// Whether the skill is currently enabled @@ -6335,16 +6341,22 @@ pub enum AutoModeResolvedReasoningBucket { Unknown, } -/// Which channel supplied the effective enterprise managed settings (highest-authority present layer wins wholesale) +/// Summary of which managed-settings channels contributed to the effective session policy. Use the per-channel booleans for exact provenance. #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum ManagedSettingsResolvedSource { - /// Account/org policy self-fetched from the GitHub managed-settings endpoint (higher authority). + /// Only the server/account channel contributed. #[serde(rename = "server")] Server, - /// Device-level MDM policy discovered from plist/registry/file (lower authority). + /// Only the device MDM/plist/registry/file channel contributed. #[serde(rename = "device")] Device, - /// No managed policy is in force (no layer contributed). + /// Only session-local SDK-host injection contributed. + #[serde(rename = "client")] + Client, + /// More than one channel contributed. Ordinary keys resolve device over server per key, while permissions compose restrictively across all present layers. + #[serde(rename = "mixed")] + Mixed, + /// No managed policy is in force (no channel contributed). #[serde(rename = "none")] None, /// Unknown variant for forward compatibility. 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 37d3b248bf..d3c4faa16b 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -1763,6 +1763,99 @@ pub struct CopilotExpAssignmentResponse { pub assignment_context: String, } +/// Controls whether bypass-permissions mode is available in a managed session. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +#[non_exhaustive] +pub enum DisableBypassPermissionsMode { + /// Turn off bypass-permissions mode. + Disable, +} + +/// 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")] +#[non_exhaustive] +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>, +} + +impl ManagedSettingsPermissions { + /// Sets the bypass-permissions policy for this managed layer. + pub fn with_disable_bypass_permissions_mode( + mut self, + value: DisableBypassPermissionsMode, + ) -> Self { + self.disable_bypass_permissions_mode = Some(value); + self + } + + /// Sets the rules that are always denied. + pub fn with_deny(mut self, rules: Vec) -> Self { + self.deny = Some(rules); + self + } + + /// Sets the rules that require explicit approval. + pub fn with_ask(mut self, rules: Vec) -> Self { + self.ask = Some(rules); + self + } + + /// Sets the rules that are allowed without prompting. + pub fn with_allow(mut self, rules: Vec) -> Self { + self.allow = Some(rules); + self + } +} + +/// 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 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")] +#[non_exhaustive] +pub struct ManagedSettings { + /// Permission rules for this managed-settings layer. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub permissions: Option, +} + +impl ManagedSettings { + /// Sets the permissions-only managed policy. + pub fn with_permissions(mut self, permissions: ManagedSettingsPermissions) -> Self { + self.permissions = Some(permissions); + self + } +} + /// Configuration for creating a new session via the `session.create` RPC. /// /// All fields are optional — the CLI applies sensible defaults. @@ -2055,6 +2148,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. @@ -2201,6 +2303,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(|_| ""), @@ -2312,6 +2415,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, @@ -2477,6 +2581,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 { @@ -3100,6 +3205,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_*` @@ -3292,6 +3406,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). @@ -3431,6 +3551,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(|_| ""), @@ -3588,6 +3709,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, }; @@ -3681,6 +3803,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, @@ -4285,6 +4408,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 3e19063fcc..53ea1c4480 100644 --- a/rust/src/wire.rs +++ b/rust/src/wire.rs @@ -186,6 +186,8 @@ pub(crate) struct SessionCreateWire { pub enable_managed_settings: Option, #[serde(skip_serializing_if = "Option::is_none")] pub is_experimental_mode: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub managed_settings: Option, } /// The exact JSON shape sent on the `session.resume` JSON-RPC request. @@ -335,4 +337,6 @@ pub(crate) struct SessionResumeWire { pub enable_managed_settings: Option, #[serde(skip_serializing_if = "Option::is_none")] pub is_experimental_mode: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub managed_settings: Option, } diff --git a/rust/tests/e2e/rpc_tasks_and_handlers.rs b/rust/tests/e2e/rpc_tasks_and_handlers.rs index b3010ab78a..b046687f42 100644 --- a/rust/tests/e2e/rpc_tasks_and_handlers.rs +++ b/rust/tests/e2e/rpc_tasks_and_handlers.rs @@ -404,6 +404,7 @@ async fn should_return_expected_results_for_missing_pending_handler_requestids() .rpc() .permissions() .handle_pending_permission_request(PermissionDecisionRequest { + decision_context: None, request_id: request_id.into(), result, }) diff --git a/rust/tests/session_test.rs b/rust/tests/session_test.rs index 41cae3e950..727911081d 100644 --- a/rust/tests/session_test.rs +++ b/rust/tests/session_test.rs @@ -17,12 +17,14 @@ use github_copilot_sdk::rpc::{ OpenCanvasInstance, }; use github_copilot_sdk::session_events::{ - McpOauthRequiredData, ReasoningSummary, SessionLimitsConfig, + ManagedSettingsResolvedSource, McpOauthRequiredData, ReasoningSummary, SessionLimitsConfig, + SessionManagedSettingsResolvedData, }; use github_copilot_sdk::types::{ CanvasProviderIdentity, CloudSessionOptions, CloudSessionRepository, CommandContext, - CommandDefinition, CommandHandler, DeliveryMode, ElicitationRequest, ElicitationResult, - ExitPlanModeData, ExtensionInfo, MessageOptions, RequestId, SessionConfig, SessionId, + CommandDefinition, CommandHandler, DeliveryMode, DisableBypassPermissionsMode, + ElicitationRequest, ElicitationResult, ExitPlanModeData, ExtensionInfo, ManagedSettings, + ManagedSettingsPermissions, MessageOptions, RequestId, SessionConfig, SessionId, SetModelOptions, Tool, ToolInvocation, ToolResult, }; use github_copilot_sdk::{Client, ContextTier, ErrorKind, ProtocolErrorKind, tool}; @@ -763,6 +765,135 @@ 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::default().with_permissions( + ManagedSettingsPermissions::default() + .with_disable_bypass_permissions_mode(DisableBypassPermissionsMode::Disable) + .with_deny(vec!["shell(rm*)".to_string()]) + .with_ask(vec!["write".to_string()]) + .with_allow(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(); +} + +#[test] +fn managed_settings_resolved_event_preserves_client_provenance() { + let sources = [ + (ManagedSettingsResolvedSource::Server, "server"), + (ManagedSettingsResolvedSource::Device, "device"), + (ManagedSettingsResolvedSource::Client, "client"), + (ManagedSettingsResolvedSource::Mixed, "mixed"), + (ManagedSettingsResolvedSource::None, "none"), + ]; + for (source, wire_value) in sources { + assert_eq!( + serde_json::to_value(source).unwrap(), + serde_json::json!(wire_value) + ); + } + + let with_client = SessionManagedSettingsResolvedData { + bypass_permissions_disabled: true, + client_managed: Some(true), + managed_keys: vec!["permissions".to_string()], + source: ManagedSettingsResolvedSource::Client, + ..Default::default() + }; + let serialized = serde_json::to_value(&with_client).unwrap(); + assert_eq!(serialized["source"], "client"); + assert_eq!(serialized["clientManaged"], true); + + let round_tripped: SessionManagedSettingsResolvedData = + serde_json::from_value(serialized).unwrap(); + assert_eq!(round_tripped.source, ManagedSettingsResolvedSource::Client); + assert_eq!(round_tripped.client_managed, Some(true)); + + let without_client = SessionManagedSettingsResolvedData { + bypass_permissions_disabled: true, + managed_keys: vec!["permissions".to_string()], + source: ManagedSettingsResolvedSource::Mixed, + ..Default::default() + }; + let serialized = serde_json::to_value(&without_client).unwrap(); + assert_eq!(serialized["source"], "mixed"); + assert!(serialized.get("clientManaged").is_none()); +} + fn make_client_with_telemetry( callback: github_copilot_sdk::github_telemetry::GitHubTelemetryCallback, ) -> (Client, tokio::io::DuplexStream, tokio::io::DuplexStream) { diff --git a/test/harness/package-lock.json b/test/harness/package-lock.json index 4702dbba63..9e3b76152a 100644 --- a/test/harness/package-lock.json +++ b/test/harness/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "devDependencies": { - "@github/copilot": "^1.0.78", + "@github/copilot": "^1.0.79-5", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "@types/node-forge": "^1.3.14", @@ -501,9 +501,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.78.tgz", - "integrity": "sha512-jn+8HLZC3R7d6K1/1g9L1iWNKzBVS3JdVcx40r3aWyS5r+MLV1OPNp0fo5OfRMCDIm3NmEaaoqypi9sQkCXuiQ==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.79-5.tgz", + "integrity": "sha512-gQj87QGcWQpAx2YcBcBZ15q5ITA9y9aLDu0mIZb2daIRQqIK8VKtmY70G5d271eqrztTqd2b3Nu0mlB64E5ufg==", "dev": true, "license": "SEE LICENSE IN LICENSE.md", "dependencies": { @@ -513,20 +513,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.78", - "@github/copilot-darwin-x64": "1.0.78", - "@github/copilot-linux-arm64": "1.0.78", - "@github/copilot-linux-x64": "1.0.78", - "@github/copilot-linuxmusl-arm64": "1.0.78", - "@github/copilot-linuxmusl-x64": "1.0.78", - "@github/copilot-win32-arm64": "1.0.78", - "@github/copilot-win32-x64": "1.0.78" + "@github/copilot-darwin-arm64": "1.0.79-5", + "@github/copilot-darwin-x64": "1.0.79-5", + "@github/copilot-linux-arm64": "1.0.79-5", + "@github/copilot-linux-x64": "1.0.79-5", + "@github/copilot-linuxmusl-arm64": "1.0.79-5", + "@github/copilot-linuxmusl-x64": "1.0.79-5", + "@github/copilot-win32-arm64": "1.0.79-5", + "@github/copilot-win32-x64": "1.0.79-5" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.78.tgz", - "integrity": "sha512-P11+VyWg8ad0WlywGtO2d7AxqTLJv4hkUicFg6Ycth5lfk00aCu/74YOOZSPO6C2bBBJhAza7oAdmauM6KEojw==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.79-5.tgz", + "integrity": "sha512-pR/ZrznLn6oOUcIPPgzaOOHKDLmeznV1yHwzkmTrqJVwlFgfFYeqBGTD3YwAqSmGAtXdDk+16o3j0mCg0m3+9A==", "cpu": [ "arm64" ], @@ -541,9 +541,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.78.tgz", - "integrity": "sha512-stimP3WDFs2GU8nJzTJbtRpZViV4bsf80yg7QrFq+G4RISQ3Nihg/3/H0U6UQF1+txMJ/Ohmb5RFYxSw1Hj2sw==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.79-5.tgz", + "integrity": "sha512-L828i0YUiI7IAACsmMnKg7LKrgzu5KqgOyA72BmxPmIYUpRpIxYt8QU8oGeej52de+I6zRYUCSWkLAkKLa6oFw==", "cpu": [ "x64" ], @@ -558,13 +558,16 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.78.tgz", - "integrity": "sha512-K31PRKGTm252V1Lof7ypjg283R2QSm3BgoCvZfX2taos4wqC3SaTozSQKwW3dgrAx7A3G3SGEoilVCNqfigdZA==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.79-5.tgz", + "integrity": "sha512-r3GT8kHGOhxbf+QaHp4vOQnJTnrreYOM+eTkgyiVrDe9GR5RKOO7XHjr8F9ob29UP4qpci/2LpgcYIJ9yiszgg==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "SEE LICENSE IN LICENSE.md", "optional": true, "os": [ @@ -575,13 +578,16 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.78.tgz", - "integrity": "sha512-QK3oMtAn9dIv+1u1kx0xNpZNtZxdI+uZVIyLl7myp+Oh2Uj8BLagVv6a7uP0cDphO3TgfIdlvpepCe5MIcx0fw==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.79-5.tgz", + "integrity": "sha512-as7EcVDOC7TtoO9+bX8NrQsACPdPKg6t0HTaAaa7oRAftalGX8ZGhGouBOL6v13vpR2xtLaRRaP3YMwo//XQ9A==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "SEE LICENSE IN LICENSE.md", "optional": true, "os": [ @@ -592,13 +598,16 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.78.tgz", - "integrity": "sha512-F/0cTMsz6ug4yiXn3RKaCAMsLR261U5Njb6G9Y/HeAI7ES/tKEo2t5SHuvgXaIH4mYiZsRvfDKdX7c0WgBX/Jg==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.79-5.tgz", + "integrity": "sha512-znO0FerQz6kUj46GqjGACR+ernRrAL3iymYo3ipGdf/E71fZnhyZ4BReh0ajRk6b2O5b7dbItk6K0K3Vr/Wn4g==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "SEE LICENSE IN LICENSE.md", "optional": true, "os": [ @@ -609,13 +618,16 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.78.tgz", - "integrity": "sha512-YMaJaeBGbArGAFYel+yFaFW/0rFgh0Oqki2f2mUtlonTX/xHr8EB4+mTnMJkHYMFy4gOTC3OtSEEe1NaW/cBXQ==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.79-5.tgz", + "integrity": "sha512-6hDWKHNEyMwvsPwHYp+Yz3QIojFfheVzhpgz+zZde1uLSf7okiV3qP5Uyuwixv4qoissxnD+2o8eL6VAJl3fsg==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "SEE LICENSE IN LICENSE.md", "optional": true, "os": [ @@ -626,9 +638,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.78.tgz", - "integrity": "sha512-ktDkFXaaecEKD3hpM6ydM9lKOdoCfsQsXCmzLzE7DCmSpbbMCdfPfWfZ7MOclmKmpZ5/MNfr4U2l8CUqGerzYA==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.79-5.tgz", + "integrity": "sha512-kvt3YrwZ4/VhAMsUau/T1SCzg4e5/ki7gBF2bxJqszh3B+7DVDXZB/jaUPexLUAbGhxSAzB9c02X1tgzt3m4nw==", "cpu": [ "arm64" ], @@ -643,9 +655,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.78.tgz", - "integrity": "sha512-Gd8l2T4eqYEWlOEPd0SZznQ+YYgYrwOkE0QXodMkhCBbPdgu/uTzb7mnISWwnVAgqs7pONdF1GOpHkTo+ay8CQ==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.79-5.tgz", + "integrity": "sha512-e+5DJhN92vMvqrK0O4hV4KTCe5AP9F75K3PbGlSzEAjHhaXYxvPcx3hsL85SEMAoBrXItx64kHQMQm33Jo9K+Q==", "cpu": [ "x64" ], diff --git a/test/harness/package.json b/test/harness/package.json index f1556534d0..4cb25e1065 100644 --- a/test/harness/package.json +++ b/test/harness/package.json @@ -14,7 +14,7 @@ "node": "^20.19.0 || >=22.12.0" }, "devDependencies": { - "@github/copilot": "^1.0.78", + "@github/copilot": "^1.0.79-5", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "@types/node-forge": "^1.3.14",