From 779167b72c2f42d7c0ebaa6722645f0a3ce5fb4e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 29 Jun 2026 17:59:35 +0000 Subject: [PATCH 1/5] Update @github/copilot to 1.0.66-2 - Updated nodejs and test harness dependencies - Re-ran code generators - Formatted generated code --- dotnet/src/Generated/Rpc.cs | 346 +++++++- dotnet/src/Generated/SessionEvents.cs | 87 +- go/rpc/zrpc.go | 410 ++++++++- go/rpc/zrpc_encoding.go | 21 +- go/rpc/zsession_encoding.go | 23 + go/rpc/zsession_events.go | 42 +- go/zsession_events.go | 6 +- java/pom.xml | 2 +- java/scripts/codegen/package-lock.json | 72 +- java/scripts/codegen/package.json | 2 +- ...tConfig.java => ResponseLimitsConfig.java} | 6 +- .../copilot/generated/SessionEvent.java | 2 + .../SessionResponseLimitsChangedEvent.java | 41 + .../copilot/generated/SessionResumeEvent.java | 4 +- .../copilot/generated/SessionStartEvent.java | 4 +- .../rpc/AccountGetCurrentAuthResult.java | 4 + .../generated/rpc/AccountGetQuotaResult.java | 4 + .../generated/rpc/AccountLoginParams.java | 4 + .../generated/rpc/AccountLoginResult.java | 4 + .../generated/rpc/AccountLogoutParams.java | 4 + .../generated/rpc/AccountLogoutResult.java | 4 + .../copilot/generated/rpc/ConnectParams.java | 4 + .../copilot/generated/rpc/ConnectResult.java | 4 + .../generated/rpc/McpConfigAddParams.java | 4 + .../generated/rpc/McpConfigDisableParams.java | 4 + .../generated/rpc/McpConfigEnableParams.java | 4 + .../generated/rpc/McpConfigListResult.java | 4 + .../generated/rpc/McpConfigRemoveParams.java | 4 + .../generated/rpc/McpConfigUpdateParams.java | 4 + .../generated/rpc/McpDiscoverParams.java | 4 + .../generated/rpc/McpDiscoverResult.java | 4 + .../generated/rpc/ModelsListResult.java | 4 + .../copilot/generated/rpc/PingParams.java | 4 + .../copilot/generated/rpc/PingResult.java | 4 + ...tConfig.java => ResponseLimitsConfig.java} | 6 +- .../rpc/SecretsAddFilterValuesParams.java | 4 + .../rpc/SecretsAddFilterValuesResult.java | 4 + .../generated/rpc/ServerAccountApi.java | 16 + .../copilot/generated/rpc/ServerMcpApi.java | 4 + .../generated/rpc/ServerMcpConfigApi.java | 22 + .../generated/rpc/ServerModelsApi.java | 4 + .../copilot/generated/rpc/ServerRpc.java | 7 + .../generated/rpc/ServerRuntimeApi.java | 4 + .../generated/rpc/ServerSecretsApi.java | 4 + .../generated/rpc/ServerSessionFsApi.java | 4 + .../generated/rpc/ServerSkillsApi.java | 3 + .../generated/rpc/ServerSkillsConfigApi.java | 4 + .../copilot/generated/rpc/ServerToolsApi.java | 4 + .../generated/rpc/ServerUserSettingsApi.java | 26 + .../rpc/SessionFsSetProviderParams.java | 4 + .../rpc/SessionFsSetProviderResult.java | 4 + .../rpc/SessionMetadataSnapshotResult.java | 2 + .../rpc/SessionOptionsUpdateParams.java | 4 +- .../copilot/generated/rpc/SessionRpc.java | 3 + .../generated/rpc/SessionVisibilityApi.java | 60 ++ .../rpc/SessionVisibilityGetParams.java | 30 + .../rpc/SessionVisibilityGetResult.java | 34 + .../rpc/SessionVisibilitySetParams.java | 32 + .../rpc/SessionVisibilitySetResult.java | 34 + .../rpc/SessionVisibilityStatus.java | 35 + .../SkillsConfigSetDisabledSkillsParams.java | 4 + .../generated/rpc/SkillsDiscoverParams.java | 4 + .../generated/rpc/SkillsDiscoverResult.java | 4 + .../generated/rpc/ToolsListParams.java | 4 + .../generated/rpc/ToolsListResult.java | 4 + .../generated/rpc/UserSettingMetadata.java | 31 + .../generated/rpc/UserSettingsGetResult.java | 31 + .../generated/rpc/UserSettingsSetParams.java | 30 + .../generated/rpc/UserSettingsSetResult.java | 31 + nodejs/package-lock.json | 72 +- nodejs/package.json | 2 +- nodejs/samples/package-lock.json | 2 +- nodejs/src/generated/rpc.ts | 304 ++++++- nodejs/src/generated/session-events.ts | 87 +- python/copilot/generated/rpc.py | 450 +++++++++- python/copilot/generated/session_events.py | 132 ++- rust/src/generated/api_types.rs | 783 +++++++++++++++++- rust/src/generated/rpc.rs | 325 ++++++++ rust/src/generated/session_events.rs | 61 +- test/harness/package-lock.json | 72 +- test/harness/package.json | 2 +- 81 files changed, 3679 insertions(+), 258 deletions(-) rename java/src/generated/java/com/github/copilot/generated/{ResponseBudgetConfig.java => ResponseLimitsConfig.java} (79%) create mode 100644 java/src/generated/java/com/github/copilot/generated/SessionResponseLimitsChangedEvent.java rename java/src/generated/java/com/github/copilot/generated/rpc/{ResponseBudgetConfig.java => ResponseLimitsConfig.java} (79%) create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityApi.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityGetParams.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityGetResult.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilitySetParams.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilitySetResult.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityStatus.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/UserSettingMetadata.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/UserSettingsGetResult.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetParams.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetResult.java diff --git a/dotnet/src/Generated/Rpc.cs b/dotnet/src/Generated/Rpc.cs index ba55e09e76..78969e8b77 100644 --- a/dotnet/src/Generated/Rpc.cs +++ b/dotnet/src/Generated/Rpc.cs @@ -19,6 +19,7 @@ namespace GitHub.Copilot.Rpc; /// Server liveness response, including the echoed message, current server timestamp, and protocol version. +[Experimental(Diagnostics.Experimental)] public sealed class PingResult { /// Echoed message (or default greeting). @@ -35,6 +36,7 @@ public sealed class PingResult } /// Optional message to echo back to the caller. +[Experimental(Diagnostics.Experimental)] internal sealed class PingRequest { /// Optional message to echo back. @@ -43,6 +45,7 @@ internal sealed class PingRequest } /// Handshake result reporting the server's protocol version and package version on success. +[Experimental(Diagnostics.Experimental)] internal sealed class ConnectResult { /// Always true on success. @@ -59,6 +62,7 @@ internal sealed class ConnectResult } /// Optional connection token presented by the SDK client during the handshake. +[Experimental(Diagnostics.Experimental)] internal sealed class ConnectRequest { /// Connection token; required when the server was started with COPILOT_CONNECTION_TOKEN. @@ -67,6 +71,7 @@ internal sealed class ConnectRequest } /// Long context tier pricing (available for models with extended context windows). +[Experimental(Diagnostics.Experimental)] public sealed class ModelBillingTokenPricesLongContext { /// Use cacheReadPrice instead. AI Credits cost per billing batch of cached tokens. @@ -103,6 +108,7 @@ public sealed class ModelBillingTokenPricesLongContext } /// Token-level pricing information for this model. +[Experimental(Diagnostics.Experimental)] public sealed class ModelBillingTokenPrices { /// Number of tokens per standard billing batch. @@ -147,6 +153,7 @@ public sealed class ModelBillingTokenPrices } /// Billing information. +[Experimental(Diagnostics.Experimental)] public sealed class ModelBilling { /// Whole-number percentage discount (0-100) applied to usage billed through this model. Populated for the synthetic `auto` model, where requests routed by auto-mode are billed at a reduced rate; absent for concrete models. @@ -163,6 +170,7 @@ public sealed class ModelBilling } /// Vision-specific limits. +[Experimental(Diagnostics.Experimental)] public sealed class ModelCapabilitiesLimitsVision { /// Maximum image size in bytes. @@ -179,6 +187,7 @@ public sealed class ModelCapabilitiesLimitsVision } /// Token limits for prompts, outputs, and context window. +[Experimental(Diagnostics.Experimental)] public sealed class ModelCapabilitiesLimits { /// Maximum total context window size in tokens. @@ -199,6 +208,7 @@ public sealed class ModelCapabilitiesLimits } /// Feature flags indicating what the model supports. +[Experimental(Diagnostics.Experimental)] public sealed class ModelCapabilitiesSupports { /// Whether this model supports reasoning effort configuration. @@ -211,6 +221,7 @@ public sealed class ModelCapabilitiesSupports } /// Model capabilities and limits. +[Experimental(Diagnostics.Experimental)] public sealed class ModelCapabilities { /// Token limits for prompts, outputs, and context window. @@ -223,6 +234,7 @@ public sealed class ModelCapabilities } /// Policy state (if applicable). +[Experimental(Diagnostics.Experimental)] public sealed class ModelPolicy { /// Current policy state for this model. @@ -235,6 +247,7 @@ public sealed class ModelPolicy } /// Schema for the `Model` type. +[Experimental(Diagnostics.Experimental)] public sealed class Model { /// Billing information. @@ -275,6 +288,7 @@ public sealed class Model } /// List of Copilot models available to the resolved user, including capabilities and billing metadata. +[Experimental(Diagnostics.Experimental)] public sealed class ModelList { /// List of available models with full metadata. @@ -283,6 +297,7 @@ public sealed class ModelList } /// RPC data type for ModelsList operations. +[Experimental(Diagnostics.Experimental)] internal sealed class ModelsListRequest { /// GitHub token for per-user model listing. When provided, resolves this token to determine the user's Copilot plan and available models instead of using the global auth. @@ -291,6 +306,7 @@ internal sealed class ModelsListRequest } /// Schema for the `Tool` type. +[Experimental(Diagnostics.Experimental)] public sealed class Tool { /// Description of what the tool does. @@ -315,6 +331,7 @@ public sealed class Tool } /// Built-in tools available for the requested model, with their parameters and instructions. +[Experimental(Diagnostics.Experimental)] public sealed class ToolList { /// List of available built-in tools with metadata. @@ -323,6 +340,7 @@ public sealed class ToolList } /// Optional model identifier whose tool overrides should be applied to the listing. +[Experimental(Diagnostics.Experimental)] internal sealed class ToolsListRequest { /// Optional model ID — when provided, the returned tool list reflects model-specific overrides. @@ -331,6 +349,7 @@ internal sealed class ToolsListRequest } /// Schema for the `AccountQuotaSnapshot` type. +[Experimental(Diagnostics.Experimental)] public sealed class AccountQuotaSnapshot { /// Number of requests included in the entitlement, or -1 for unlimited entitlements. @@ -367,6 +386,7 @@ public sealed class AccountQuotaSnapshot } /// Quota usage snapshots for the resolved user, keyed by quota type. +[Experimental(Diagnostics.Experimental)] public sealed class AccountGetQuotaResult { /// Quota snapshots keyed by type (e.g., chat, completions, premium_interactions). @@ -375,6 +395,7 @@ public sealed class AccountGetQuotaResult } /// RPC data type for AccountGetQuota operations. +[Experimental(Diagnostics.Experimental)] internal sealed class AccountGetQuotaRequest { /// GitHub token for per-user quota lookup. When provided, resolves this token to determine the user's quota instead of using the global auth. @@ -384,6 +405,7 @@ internal sealed class AccountGetQuotaRequest /// Initial authentication info for the session. /// Polymorphic base type discriminated by type. +[Experimental(Diagnostics.Experimental)] [JsonPolymorphic( TypeDiscriminatorPropertyName = "type", UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] @@ -403,6 +425,7 @@ public partial class AuthInfo /// Schema for the `CopilotUserResponseEndpoints` type. +[Experimental(Diagnostics.Experimental)] public sealed class CopilotUserResponseEndpoints { /// Gets or sets the api value. @@ -435,6 +458,7 @@ public sealed class CopilotUserResponseOrganizationListItem } /// Schema for the `CopilotUserResponseQuotaSnapshotsChat` type. +[Experimental(Diagnostics.Experimental)] public sealed class CopilotUserResponseQuotaSnapshotsChat { /// Gets or sets the entitlement value. @@ -487,6 +511,7 @@ public sealed class CopilotUserResponseQuotaSnapshotsChat } /// Schema for the `CopilotUserResponseQuotaSnapshotsCompletions` type. +[Experimental(Diagnostics.Experimental)] public sealed class CopilotUserResponseQuotaSnapshotsCompletions { /// Gets or sets the entitlement value. @@ -539,6 +564,7 @@ public sealed class CopilotUserResponseQuotaSnapshotsCompletions } /// Schema for the `CopilotUserResponseQuotaSnapshotsPremiumInteractions` type. +[Experimental(Diagnostics.Experimental)] public sealed class CopilotUserResponseQuotaSnapshotsPremiumInteractions { /// Gets or sets the entitlement value. @@ -591,6 +617,7 @@ public sealed class CopilotUserResponseQuotaSnapshotsPremiumInteractions } /// Schema for the `CopilotUserResponseQuotaSnapshots` type. +[Experimental(Diagnostics.Experimental)] public sealed class CopilotUserResponseQuotaSnapshots { /// Schema for the `CopilotUserResponseQuotaSnapshotsChat` type. @@ -607,6 +634,7 @@ public sealed class CopilotUserResponseQuotaSnapshots } /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. +[Experimental(Diagnostics.Experimental)] public sealed class CopilotUserResponse { /// Gets or sets the access_type_sku value. @@ -712,6 +740,7 @@ public sealed class CopilotUserResponse /// Schema for the `HMACAuthInfo` type. /// The hmac variant of . +[Experimental(Diagnostics.Experimental)] public partial class AuthInfoHmac : AuthInfo { /// @@ -734,6 +763,7 @@ public partial class AuthInfoHmac : AuthInfo /// Schema for the `EnvAuthInfo` type. /// The env variant of . +[Experimental(Diagnostics.Experimental)] public partial class AuthInfoEnv : AuthInfo { /// @@ -765,6 +795,7 @@ public partial class AuthInfoEnv : AuthInfo /// Schema for the `TokenAuthInfo` type. /// The token variant of . +[Experimental(Diagnostics.Experimental)] public partial class AuthInfoToken : AuthInfo { /// @@ -787,6 +818,7 @@ public partial class AuthInfoToken : AuthInfo /// Schema for the `CopilotApiTokenAuthInfo` type. /// The copilot-api-token variant of . +[Experimental(Diagnostics.Experimental)] public partial class AuthInfoCopilotApiToken : AuthInfo { /// @@ -805,6 +837,7 @@ public partial class AuthInfoCopilotApiToken : AuthInfo /// Schema for the `UserAuthInfo` type. /// The user variant of . +[Experimental(Diagnostics.Experimental)] public partial class AuthInfoUser : AuthInfo { /// @@ -827,6 +860,7 @@ public partial class AuthInfoUser : AuthInfo /// Schema for the `GhCliAuthInfo` type. /// The gh-cli variant of . +[Experimental(Diagnostics.Experimental)] public partial class AuthInfoGhCli : AuthInfo { /// @@ -853,6 +887,7 @@ public partial class AuthInfoGhCli : AuthInfo /// Schema for the `ApiKeyAuthInfo` type. /// The api-key variant of . +[Experimental(Diagnostics.Experimental)] public partial class AuthInfoApiKey : AuthInfo { /// @@ -874,6 +909,7 @@ public partial class AuthInfoApiKey : AuthInfo } /// Current authentication state. +[Experimental(Diagnostics.Experimental)] public sealed class AccountGetCurrentAuthResult { /// Authentication errors from the last auth attempt, if any. @@ -886,6 +922,7 @@ public sealed class AccountGetCurrentAuthResult } /// Schema for the `AccountAllUsers` type. +[Experimental(Diagnostics.Experimental)] public sealed class AccountAllUsers { /// Authentication information for this user. @@ -898,6 +935,7 @@ public sealed class AccountAllUsers } /// Result of a successful login; throws on failure. +[Experimental(Diagnostics.Experimental)] public sealed class AccountLoginResult { /// Whether the credential was persisted to a secure store (system keychain, or the config file when plaintext storage is enabled). False when no secure store was available and the token was not saved, so the consumer can decide how to proceed. @@ -906,6 +944,7 @@ public sealed class AccountLoginResult } /// Credentials to store after successful authentication. +[Experimental(Diagnostics.Experimental)] internal sealed class AccountLoginRequest { /// GitHub host URL. @@ -922,6 +961,7 @@ internal sealed class AccountLoginRequest } /// Logout result indicating if more users remain. +[Experimental(Diagnostics.Experimental)] public sealed class AccountLogoutResult { /// Whether other authenticated users remain after logout. @@ -930,6 +970,7 @@ public sealed class AccountLogoutResult } /// User to log out. +[Experimental(Diagnostics.Experimental)] internal sealed class AccountLogoutRequest { /// Authentication information for the user to log out. @@ -938,6 +979,7 @@ internal sealed class AccountLogoutRequest } /// Confirmation that the secret values were registered. +[Experimental(Diagnostics.Experimental)] public sealed class SecretsAddFilterValuesResult { /// Whether the values were successfully registered. @@ -946,6 +988,7 @@ public sealed class SecretsAddFilterValuesResult } /// Secret values to add to the redaction filter. +[Experimental(Diagnostics.Experimental)] internal sealed class SecretsAddFilterValuesRequest { /// Raw secret values to register for redaction. @@ -954,6 +997,7 @@ internal sealed class SecretsAddFilterValuesRequest } /// Schema for the `DiscoveredMcpServer` type. +[Experimental(Diagnostics.Experimental)] public sealed class DiscoveredMcpServer { /// Whether the server is enabled (not in the disabled list). @@ -985,6 +1029,7 @@ public sealed class DiscoveredMcpServer } /// MCP servers discovered from user, workspace, plugin, and built-in sources. +[Experimental(Diagnostics.Experimental)] public sealed class McpDiscoverResult { /// MCP servers discovered from all sources. @@ -993,6 +1038,7 @@ public sealed class McpDiscoverResult } /// Optional working directory used as context for MCP server discovery. +[Experimental(Diagnostics.Experimental)] internal sealed class McpDiscoverRequest { /// Working directory used as context for discovery (e.g., plugin resolution). @@ -1001,6 +1047,7 @@ internal sealed class McpDiscoverRequest } /// User-configured MCP servers, keyed by server name. +[Experimental(Diagnostics.Experimental)] public sealed class McpConfigList { /// All MCP servers from user config, keyed by name. @@ -1009,6 +1056,7 @@ public sealed class McpConfigList } /// MCP server name and configuration to add to user configuration. +[Experimental(Diagnostics.Experimental)] internal sealed class McpConfigAddRequest { /// MCP server configuration (stdio process or remote HTTP/SSE). @@ -1024,6 +1072,7 @@ internal sealed class McpConfigAddRequest } /// MCP server name and replacement configuration to write to user configuration. +[Experimental(Diagnostics.Experimental)] internal sealed class McpConfigUpdateRequest { /// MCP server configuration (stdio process or remote HTTP/SSE). @@ -1039,6 +1088,7 @@ internal sealed class McpConfigUpdateRequest } /// MCP server name to remove from user configuration. +[Experimental(Diagnostics.Experimental)] internal sealed class McpConfigRemoveRequest { /// Name of the MCP server to remove. @@ -1050,6 +1100,7 @@ internal sealed class McpConfigRemoveRequest } /// MCP server names to enable for new sessions. +[Experimental(Diagnostics.Experimental)] internal sealed class McpConfigEnableRequest { /// Names of MCP servers to enable. Each server is removed from the persisted disabled list so new sessions spawn it. Unknown or already-enabled names are ignored. @@ -1058,6 +1109,7 @@ internal sealed class McpConfigEnableRequest } /// MCP server names to disable for new sessions. +[Experimental(Diagnostics.Experimental)] internal sealed class McpConfigDisableRequest { /// Names of MCP servers to disable. Each server is added to the persisted disabled list so new sessions skip it. Already-disabled names are ignored. Active sessions keep their current connections until they end. @@ -1369,6 +1421,7 @@ internal sealed class PluginsMarketplacesRefreshRequest } /// Schema for the `ServerSkill` type. +[Experimental(Diagnostics.Experimental)] public sealed class ServerSkill { /// Optional freeform hint describing the skill's expected arguments, from the `argument-hint` frontmatter field. @@ -1405,6 +1458,7 @@ public sealed class ServerSkill } /// Skills discovered across global and project sources. +[Experimental(Diagnostics.Experimental)] public sealed class ServerSkillList { /// All discovered skills across all sources. @@ -1413,6 +1467,7 @@ public sealed class ServerSkillList } /// Optional project paths and additional skill directories to include in discovery. +[Experimental(Diagnostics.Experimental)] internal sealed class SkillsDiscoverRequest { /// When true, omit skills from the host's global sources (personal, custom, plugin, and built-in), returning only project-scoped skills. For multitenant deployments. @@ -1472,6 +1527,7 @@ internal sealed class SkillsGetDiscoveryPathsRequest } /// Skill names to mark as disabled in global configuration, replacing any previous list. +[Experimental(Diagnostics.Experimental)] internal sealed class SkillsConfigSetDisabledSkillsRequest { /// List of skill names to disable. @@ -1708,7 +1764,52 @@ internal sealed class InstructionsGetDiscoveryPathsRequest public IList? ProjectPaths { get; set; } } +/// A single user setting's effective value alongside its default, so consumers can render settings left at their default. +[Experimental(Diagnostics.Experimental)] +public sealed class UserSettingMetadata +{ + /// The centrally-known default for this setting (null when no default is registered). + [JsonPropertyName("default")] + public JsonElement Default { get; set; } + + /// True when the user has not set an explicit value for this setting (i.e. it is left at its default). Reflects whether the user has overridden the key, not whether the effective value happens to equal the default — a key explicitly set to a value identical to the default still reports false. + [JsonPropertyName("isDefault")] + public bool IsDefault { get; set; } + + /// The effective value: the user's value if set, otherwise the default. + [JsonPropertyName("value")] + public JsonElement Value { get; set; } +} + +/// Per-key metadata for every known user setting (settings.json overlaid with the legacy config.json, config.json wins), including settings left at their default. Excludes repository- and enterprise-managed overrides. +[Experimental(Diagnostics.Experimental)] +public sealed class UserSettingsGetResult +{ + /// Every known user setting keyed by setting name, each with its effective value, default, and whether it is at the default. + [JsonPropertyName("settings")] + public IDictionary Settings { get => field ??= new Dictionary(); set; } +} + +/// Outcome of writing user settings. +[Experimental(Diagnostics.Experimental)] +public sealed class UserSettingsSetResult +{ + /// Top-level keys whose write landed in settings.json but is shadowed by a value still present in the legacy config.json (config.json wins on read). The write does not take effect until the legacy value is removed. + [JsonPropertyName("shadowedKeys")] + public IList ShadowedKeys { get => field ??= []; set; } +} + +/// Partial user settings to write to settings.json. Each top-level key is written individually, replacing the existing value; a key whose value is null is removed. +[Experimental(Diagnostics.Experimental)] +internal sealed class UserSettingsSetRequest +{ + /// Partial user settings to write, as a free-form object keyed by setting name. + [JsonPropertyName("settings")] + public JsonElement Settings { get; set; } +} + /// Indicates whether the calling client was registered as the session filesystem provider. +[Experimental(Diagnostics.Experimental)] public sealed class SessionFsSetProviderResult { /// Whether the provider was set successfully. @@ -1717,6 +1818,7 @@ public sealed class SessionFsSetProviderResult } /// Optional capabilities declared by the provider. +[Experimental(Diagnostics.Experimental)] public sealed class SessionFsSetProviderCapabilities { /// Whether the provider supports SQLite query/exists operations. @@ -1725,6 +1827,7 @@ public sealed class SessionFsSetProviderCapabilities } /// Initial working directory, session-state path layout, and path conventions used to register the calling SDK client as the session filesystem provider. +[Experimental(Diagnostics.Experimental)] internal sealed class SessionFsSetProviderRequest { /// Optional capabilities declared by the provider. @@ -6775,9 +6878,9 @@ internal sealed class SessionUpdateOptionsParams [JsonPropertyName("reasoningSummary")] public OptionsUpdateReasoningSummary? ReasoningSummary { get; set; } - /// Optional response budget limits. Pass null to clear the response budget. - [JsonPropertyName("responseBudget")] - public ResponseBudgetConfig? ResponseBudget { get; set; } + /// Optional response limits. Pass null to clear the response limits. + [JsonPropertyName("responseLimits")] + public ResponseLimitsConfig? ResponseLimits { get; set; } /// Whether the session is running in an interactive UI. [JsonPropertyName("runningInInteractiveMode")] @@ -9703,6 +9806,10 @@ public sealed class SessionMetadataSnapshot [JsonPropertyName("remoteMetadata")] public MetadataSnapshotRemoteMetadata? RemoteMetadata { get; set; } + /// Current response limits for the session, or null when no limits are active. + [JsonPropertyName("responseLimits")] + public ResponseLimitsConfig? ResponseLimits { get; set; } + /// Currently selected model identifier, if any. [JsonPropertyName("selectedModel")] public string? SelectedModel { get; set; } @@ -10628,6 +10735,66 @@ internal sealed class RemoteNotifySteerableChangedRequest public string SessionId { get; set; } = string.Empty; } +/// Current sharing status and shareable GitHub URL for a session. +[Experimental(Diagnostics.Experimental)] +public sealed class VisibilityGetResult +{ + /// Shareable GitHub URL for the session. Present when the session is synced and the URL can be resolved. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("shareUrl")] + public string? ShareUrl { get; set; } + + /// Current sharing status. Absent when the session is not synced or the status could not be retrieved (e.g. the user is not authenticated). + [JsonPropertyName("status")] + public SessionVisibilityStatus? Status { get; set; } + + /// Whether the session has been synced to Mission Control (i.e. has a GitHub task). When false, the session cannot be shared and `status`/`shareUrl` are absent. + [JsonPropertyName("synced")] + public bool Synced { get; set; } +} + +/// Identifies the target session. +[Experimental(Diagnostics.Experimental)] +internal sealed class SessionVisibilityGetRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Effective sharing status and shareable GitHub URL after updating session visibility. +[Experimental(Diagnostics.Experimental)] +public sealed class VisibilitySetResult +{ + /// Shareable GitHub URL for the session. Present when the session is synced and the URL can be resolved. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("shareUrl")] + public string? ShareUrl { get; set; } + + /// Effective sharing status after the update. May differ from the requested status for task types that are already visible to repository readers by default. Absent when the update could not be applied (e.g. the session is not synced or the user is not authenticated). + [JsonPropertyName("status")] + public SessionVisibilityStatus? Status { get; set; } + + /// Whether the session has been synced to Mission Control (i.e. has a GitHub task). When false, the visibility change could not be applied and `status`/`shareUrl` are absent. + [JsonPropertyName("synced")] + public bool Synced { get; set; } +} + +/// Desired sharing status for the session. +[Experimental(Diagnostics.Experimental)] +internal sealed class VisibilitySetRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; + + /// Sharing status to apply. "repo" makes the session visible to repository readers; "unshared" restricts it to the creator and collaborators. + [JsonPropertyName("status")] + public SessionVisibilityStatus Status { get; set; } +} + /// Schema for the `ScheduleEntry` type. [Experimental(Diagnostics.Experimental)] public sealed class ScheduleEntry @@ -10724,6 +10891,7 @@ public sealed class ProviderTokenAcquireResult } /// Asks the SDK client to acquire a bearer token for a BYOK provider whose config set `hasBearerTokenProvider: true`. Issued by the runtime before each outbound model request; the runtime does no caching, so this is sent once per request. +[Experimental(Diagnostics.Experimental)] public sealed class ProviderTokenAcquireRequest { /// Name of the BYOK provider needing a token. For the legacy whole-session `provider` this is the implicit provider name; for named providers it is `NamedProviderConfig.name`. @@ -10762,6 +10930,7 @@ public sealed class SessionFsReadFileResult } /// Path of the file to read from the client-provided session filesystem. +[Experimental(Diagnostics.Experimental)] public sealed class SessionFsReadFileRequest { /// Path using SessionFs conventions. @@ -10774,6 +10943,7 @@ public sealed class SessionFsReadFileRequest } /// File path, content to write, and optional mode for the client-provided session filesystem. +[Experimental(Diagnostics.Experimental)] public sealed class SessionFsWriteFileRequest { /// Content to write. @@ -10794,6 +10964,7 @@ public sealed class SessionFsWriteFileRequest } /// File path, content to append, and optional mode for the client-provided session filesystem. +[Experimental(Diagnostics.Experimental)] public sealed class SessionFsAppendFileRequest { /// Content to append. @@ -10823,6 +10994,7 @@ public sealed class SessionFsExistsResult } /// Path to test for existence in the client-provided session filesystem. +[Experimental(Diagnostics.Experimental)] public sealed class SessionFsExistsRequest { /// Path using SessionFs conventions. @@ -10864,6 +11036,7 @@ public sealed class SessionFsStatResult } /// Path whose metadata should be returned from the client-provided session filesystem. +[Experimental(Diagnostics.Experimental)] public sealed class SessionFsStatRequest { /// Path using SessionFs conventions. @@ -10876,6 +11049,7 @@ public sealed class SessionFsStatRequest } /// Directory path to create in the client-provided session filesystem, with options for recursive creation and POSIX mode. +[Experimental(Diagnostics.Experimental)] public sealed class SessionFsMkdirRequest { /// Optional POSIX-style mode for newly created directories. @@ -10909,6 +11083,7 @@ public sealed class SessionFsReaddirResult } /// Directory path whose entries should be listed from the client-provided session filesystem. +[Experimental(Diagnostics.Experimental)] public sealed class SessionFsReaddirRequest { /// Path using SessionFs conventions. @@ -10947,6 +11122,7 @@ public sealed class SessionFsReaddirWithTypesResult } /// Directory path whose entries (with type information) should be listed from the client-provided session filesystem. +[Experimental(Diagnostics.Experimental)] public sealed class SessionFsReaddirWithTypesRequest { /// Path using SessionFs conventions. @@ -10959,6 +11135,7 @@ public sealed class SessionFsReaddirWithTypesRequest } /// Path to remove from the client-provided session filesystem, with options for recursive removal and force. +[Experimental(Diagnostics.Experimental)] public sealed class SessionFsRmRequest { /// Ignore errors if the path does not exist. @@ -10979,6 +11156,7 @@ public sealed class SessionFsRmRequest } /// Source and destination paths for renaming or moving an entry in the client-provided session filesystem. +[Experimental(Diagnostics.Experimental)] public sealed class SessionFsRenameRequest { /// Destination path using SessionFs conventions. @@ -11020,6 +11198,7 @@ public sealed class SessionFsSqliteQueryResult } /// SQL query, query type, and optional bind parameters for executing a SQLite query against the per-session database. +[Experimental(Diagnostics.Experimental)] public sealed class SessionFsSqliteQueryRequest { /// Optional named bind parameters. @@ -11270,6 +11449,7 @@ public sealed class LlmInferenceHttpRequestChunkRequest } /// Model capability category for grouping in the model picker. +[Experimental(Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] public readonly struct ModelPickerCategory : IEquatable @@ -11335,6 +11515,7 @@ public override void Write(Utf8JsonWriter writer, ModelPickerCategory value, Jso /// Relative cost tier for token-based billing users. +[Experimental(Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] public readonly struct ModelPickerPriceCategory : IEquatable @@ -11403,6 +11584,7 @@ public override void Write(Utf8JsonWriter writer, ModelPickerPriceCategory value /// Current policy state for this model. +[Experimental(Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] public readonly struct ModelPolicyState : IEquatable @@ -11468,6 +11650,7 @@ public override void Write(Utf8JsonWriter writer, ModelPolicyState value, JsonSe /// Server transport type: stdio, http, sse (deprecated), or memory. +[Experimental(Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] public readonly struct DiscoveredMcpServerType : IEquatable @@ -12022,6 +12205,7 @@ public override void Write(Utf8JsonWriter writer, InstructionDiscoveryPathLocati /// Path conventions used by this filesystem. +[Experimental(Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] public readonly struct SessionFsSetProviderConventions : IEquatable @@ -16769,6 +16953,69 @@ public override void Write(Utf8JsonWriter writer, RemoteSessionMode value, JsonS } +/// Sharing status for a synced session. "repo" makes the session visible to anyone with read access to the repository; "unshared" restricts it to the creator and collaborators. +[Experimental(Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct SessionVisibilityStatus : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public SessionVisibilityStatus(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// The session is visible to repository readers. + public static SessionVisibilityStatus Repo { get; } = new("repo"); + + /// The session is restricted to its creator and collaborators. + public static SessionVisibilityStatus Unshared { get; } = new("unshared"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SessionVisibilityStatus left, SessionVisibilityStatus right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SessionVisibilityStatus left, SessionVisibilityStatus right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is SessionVisibilityStatus other && Equals(other); + + /// + public bool Equals(SessionVisibilityStatus 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 SessionVisibilityStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, SessionVisibilityStatus value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionVisibilityStatus)); + } + } +} + + /// Error classification. [Experimental(Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] @@ -17038,6 +17285,7 @@ internal ServerRpc(JsonRpc rpc) /// Optional message to echo back. /// The to monitor for cancellation requests. The default is . /// Server liveness response, including the echoed message, current server timestamp, and protocol version. + [Experimental(Diagnostics.Experimental)] public async Task PingAsync(string? message = null, CancellationToken cancellationToken = default) { var request = new PingRequest { Message = message }; @@ -17048,6 +17296,7 @@ public async Task PingAsync(string? message = null, CancellationToke /// Connection token; required when the server was started with COPILOT_CONNECTION_TOKEN. /// The to monitor for cancellation requests. The default is . /// Handshake result reporting the server's protocol version and package version on success. + [Experimental(Diagnostics.Experimental)] internal async Task ConnectAsync(string? token = null, CancellationToken cancellationToken = default) { var request = new ConnectRequest { Token = token }; @@ -17146,6 +17395,7 @@ internal async Task ConnectAsync(string? token = null, Cancellati } /// Provides server-scoped Models APIs. +[Experimental(Diagnostics.Experimental)] public sealed class ServerModelsApi { private readonly JsonRpc _rpc; @@ -17167,6 +17417,7 @@ public async Task ListAsync(string? gitHubToken = null, CancellationT } /// Provides server-scoped Tools APIs. +[Experimental(Diagnostics.Experimental)] public sealed class ServerToolsApi { private readonly JsonRpc _rpc; @@ -17188,6 +17439,7 @@ public async Task ListAsync(string? model = null, CancellationToken ca } /// Provides server-scoped Account APIs. +[Experimental(Diagnostics.Experimental)] public sealed class ServerAccountApi { private readonly JsonRpc _rpc; @@ -17253,6 +17505,7 @@ public async Task LogoutAsync(AuthInfo authInfo, Cancellati } /// Provides server-scoped Secrets APIs. +[Experimental(Diagnostics.Experimental)] public sealed class ServerSecretsApi { private readonly JsonRpc _rpc; @@ -17276,6 +17529,7 @@ public async Task AddFilterValuesAsync(IListProvides server-scoped Mcp APIs. +[Experimental(Diagnostics.Experimental)] public sealed class ServerMcpApi { private readonly JsonRpc _rpc; @@ -17303,6 +17557,7 @@ public async Task DiscoverAsync(string? workingDirectory = nu } /// Provides server-scoped McpConfig APIs. +[Experimental(Diagnostics.Experimental)] public sealed class ServerMcpConfigApi { private readonly JsonRpc _rpc; @@ -17548,6 +17803,7 @@ public async Task RefreshAsync(string? name = null, Ca } /// Provides server-scoped Skills APIs. +[Experimental(Diagnostics.Experimental)] public sealed class ServerSkillsApi { private readonly JsonRpc _rpc; @@ -17574,7 +17830,6 @@ public async Task DiscoverAsync(IList? projectPaths = n /// When true, omit the host's personal and custom skill directories, leaving only project directories. For multitenant deployments. /// The to monitor for cancellation requests. The default is . /// Canonical locations where skills can be created so the runtime will recognize them. - [Experimental(Diagnostics.Experimental)] public async Task GetDiscoveryPathsAsync(IList? projectPaths = null, bool? excludeHostSkills = null, CancellationToken cancellationToken = default) { var request = new SkillsGetDiscoveryPathsRequest { ProjectPaths = projectPaths, ExcludeHostSkills = excludeHostSkills }; @@ -17589,6 +17844,7 @@ public async Task GetDiscoveryPathsAsync(IList? } /// Provides server-scoped SkillsConfig APIs. +[Experimental(Diagnostics.Experimental)] public sealed class ServerSkillsConfigApi { private readonly JsonRpc _rpc; @@ -17679,6 +17935,7 @@ public async Task GetDiscoveryPathsAsync(IListProvides server-scoped User APIs. +[Experimental(Diagnostics.Experimental)] public sealed class ServerUserApi { private readonly JsonRpc _rpc; @@ -17696,6 +17953,7 @@ internal ServerUserApi(JsonRpc rpc) } /// Provides server-scoped UserSettings APIs. +[Experimental(Diagnostics.Experimental)] public sealed class ServerUserSettingsApi { private readonly JsonRpc _rpc; @@ -17711,9 +17969,30 @@ public async Task ReloadAsync(CancellationToken cancellationToken = default) { await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.reload", [], cancellationToken); } + + /// Lists every known user setting (settings.json overlaid with the legacy config.json, config.json wins), each with its effective value, its default, and whether it is at the default — so settings the user has never set still appear with their default value. Does not include repository- or enterprise-managed overrides that the runtime layers on top at session time. + /// The to monitor for cancellation requests. The default is . + /// Per-key metadata for every known user setting (settings.json overlaid with the legacy config.json, config.json wins), including settings left at their default. Excludes repository- and enterprise-managed overrides. + public async Task GetAsync(CancellationToken cancellationToken = default) + { + return await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.get", [], cancellationToken); + } + + /// Writes one or more user settings to settings.json, replacing each provided top-level key. A key whose value is null is removed. Returns the keys whose new value is shadowed by a legacy config.json entry (config.json wins on read), which the runtime leaves in place — such writes do not take effect until the legacy value is removed. + /// Partial user settings to write, as a free-form object keyed by setting name. + /// The to monitor for cancellation requests. The default is . + /// Outcome of writing user settings. + public async Task SetAsync(object settings, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(settings); + + var request = new UserSettingsSetRequest { Settings = CopilotClient.ToJsonElementForWire(settings)!.Value }; + return await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.set", [request], cancellationToken); + } } /// Provides server-scoped Runtime APIs. +[Experimental(Diagnostics.Experimental)] public sealed class ServerRuntimeApi { private readonly JsonRpc _rpc; @@ -17732,6 +18011,7 @@ public async Task ShutdownAsync(CancellationToken cancellationToken = default) } /// Provides server-scoped SessionFs APIs. +[Experimental(Diagnostics.Experimental)] public sealed class ServerSessionFsApi { private readonly JsonRpc _rpc; @@ -18386,6 +18666,12 @@ internal SessionRpc(CopilotSession session) Interlocked.CompareExchange(ref field, new(_session), null) ?? field; + /// Visibility APIs. + public VisibilityApi Visibility => + field ?? + Interlocked.CompareExchange(ref field, new(_session), null) ?? + field; + /// Schedule APIs. public ScheduleApi Schedule => field ?? @@ -19849,14 +20135,14 @@ internal OptionsApi(CopilotSession session) /// Whether to enable cross-session store writes and reads. /// Whether to enable skill directory scanning and loading. Falls back to enableConfigDiscovery when unset. /// Context tier for models with tiered pricing. The session uses this to derive effective `modelCapabilitiesOverrides` so compaction, truncation, token display, and request limits honor the selected tier. - /// Optional response budget limits. Pass null to clear the response budget. + /// Optional response limits. Pass null to clear the response limits. /// The to monitor for cancellation requests. The default is . /// Indicates whether the session options patch was applied successfully. - public async Task UpdateAsync(string? model = null, ModelCapabilitiesOverride? modelCapabilitiesOverrides = null, string? reasoningEffort = null, OptionsUpdateReasoningSummary? reasoningSummary = null, string? clientName = null, string? lspClientName = null, string? integrationId = null, IDictionary? featureFlags = null, bool? isExperimentalMode = null, ProviderConfig? provider = null, CapiSessionOptions? capi = null, string? workingDirectory = null, IList? availableTools = null, IList? excludedTools = null, OptionsUpdateToolFilterPrecedence? toolFilterPrecedence = null, bool? enableScriptSafety = null, string? shellInitProfile = null, IList? shellProcessFlags = null, SandboxConfig? sandboxConfig = null, bool? logInteractiveShells = null, OptionsUpdateEnvValueMode? envValueMode = null, bool? allowAllMcpServerInstructions = null, IList? skillDirectories = null, IList? disabledSkills = null, bool? enableOnDemandInstructionDiscovery = null, long? maxInlineBinaryBytes = null, IList? installedPlugins = null, bool? customAgentsLocalOnly = null, bool? suppressCustomAgentPrompt = null, bool? skipCustomInstructions = null, IList? disabledInstructionSources = null, bool? coauthorEnabled = null, string? trajectoryFile = null, bool? enableStreaming = null, string? copilotUrl = null, bool? askUserDisabled = null, bool? continueOnAutoMode = null, bool? runningInInteractiveMode = null, bool? enableReasoningSummaries = null, string? agentContext = null, string? eventsLogDirectory = null, IList? additionalContentExclusionPolicies = null, bool? manageScheduleEnabled = null, IList? sessionCapabilities = null, bool? skipEmbeddingRetrieval = null, string? organizationCustomInstructions = null, bool? enableFileHooks = null, bool? enableHostGitOperations = null, bool? enableSessionStore = null, bool? enableSkills = null, OptionsUpdateContextTier? contextTier = null, ResponseBudgetConfig? responseBudget = null, CancellationToken cancellationToken = default) + public async Task UpdateAsync(string? model = null, ModelCapabilitiesOverride? modelCapabilitiesOverrides = null, string? reasoningEffort = null, OptionsUpdateReasoningSummary? reasoningSummary = null, string? clientName = null, string? lspClientName = null, string? integrationId = null, IDictionary? featureFlags = null, bool? isExperimentalMode = null, ProviderConfig? provider = null, CapiSessionOptions? capi = null, string? workingDirectory = null, IList? availableTools = null, IList? excludedTools = null, OptionsUpdateToolFilterPrecedence? toolFilterPrecedence = null, bool? enableScriptSafety = null, string? shellInitProfile = null, IList? shellProcessFlags = null, SandboxConfig? sandboxConfig = null, bool? logInteractiveShells = null, OptionsUpdateEnvValueMode? envValueMode = null, bool? allowAllMcpServerInstructions = null, IList? skillDirectories = null, IList? disabledSkills = null, bool? enableOnDemandInstructionDiscovery = null, long? maxInlineBinaryBytes = null, IList? installedPlugins = null, bool? customAgentsLocalOnly = null, bool? suppressCustomAgentPrompt = null, bool? skipCustomInstructions = null, IList? disabledInstructionSources = null, bool? coauthorEnabled = null, string? trajectoryFile = null, bool? enableStreaming = null, string? copilotUrl = null, bool? askUserDisabled = null, bool? continueOnAutoMode = null, bool? runningInInteractiveMode = null, bool? enableReasoningSummaries = null, string? agentContext = null, string? eventsLogDirectory = null, IList? additionalContentExclusionPolicies = null, bool? manageScheduleEnabled = null, IList? sessionCapabilities = null, bool? skipEmbeddingRetrieval = null, string? organizationCustomInstructions = null, bool? enableFileHooks = null, bool? enableHostGitOperations = null, bool? enableSessionStore = null, bool? enableSkills = null, OptionsUpdateContextTier? contextTier = null, ResponseLimitsConfig? responseLimits = null, CancellationToken cancellationToken = default) { _session.ThrowIfDisposed(); - var request = new SessionUpdateOptionsParams { SessionId = _session.SessionId, Model = model, ModelCapabilitiesOverrides = modelCapabilitiesOverrides, ReasoningEffort = reasoningEffort, ReasoningSummary = reasoningSummary, ClientName = clientName, LspClientName = lspClientName, IntegrationId = integrationId, FeatureFlags = featureFlags, IsExperimentalMode = isExperimentalMode, Provider = provider, Capi = capi, WorkingDirectory = workingDirectory, AvailableTools = availableTools, ExcludedTools = excludedTools, ToolFilterPrecedence = toolFilterPrecedence, EnableScriptSafety = enableScriptSafety, ShellInitProfile = shellInitProfile, ShellProcessFlags = shellProcessFlags, SandboxConfig = sandboxConfig, LogInteractiveShells = logInteractiveShells, EnvValueMode = envValueMode, AllowAllMcpServerInstructions = allowAllMcpServerInstructions, SkillDirectories = skillDirectories, DisabledSkills = disabledSkills, EnableOnDemandInstructionDiscovery = enableOnDemandInstructionDiscovery, MaxInlineBinaryBytes = maxInlineBinaryBytes, InstalledPlugins = installedPlugins, CustomAgentsLocalOnly = customAgentsLocalOnly, SuppressCustomAgentPrompt = suppressCustomAgentPrompt, SkipCustomInstructions = skipCustomInstructions, DisabledInstructionSources = disabledInstructionSources, CoauthorEnabled = coauthorEnabled, TrajectoryFile = trajectoryFile, EnableStreaming = enableStreaming, CopilotUrl = copilotUrl, AskUserDisabled = askUserDisabled, ContinueOnAutoMode = continueOnAutoMode, RunningInInteractiveMode = runningInInteractiveMode, EnableReasoningSummaries = enableReasoningSummaries, AgentContext = agentContext, EventsLogDirectory = eventsLogDirectory, AdditionalContentExclusionPolicies = additionalContentExclusionPolicies, ManageScheduleEnabled = manageScheduleEnabled, SessionCapabilities = sessionCapabilities, SkipEmbeddingRetrieval = skipEmbeddingRetrieval, OrganizationCustomInstructions = organizationCustomInstructions, EnableFileHooks = enableFileHooks, EnableHostGitOperations = enableHostGitOperations, EnableSessionStore = enableSessionStore, EnableSkills = enableSkills, ContextTier = contextTier, ResponseBudget = responseBudget }; + var request = new SessionUpdateOptionsParams { SessionId = _session.SessionId, Model = model, ModelCapabilitiesOverrides = modelCapabilitiesOverrides, ReasoningEffort = reasoningEffort, ReasoningSummary = reasoningSummary, ClientName = clientName, LspClientName = lspClientName, IntegrationId = integrationId, FeatureFlags = featureFlags, IsExperimentalMode = isExperimentalMode, Provider = provider, Capi = capi, WorkingDirectory = workingDirectory, AvailableTools = availableTools, ExcludedTools = excludedTools, ToolFilterPrecedence = toolFilterPrecedence, EnableScriptSafety = enableScriptSafety, ShellInitProfile = shellInitProfile, ShellProcessFlags = shellProcessFlags, SandboxConfig = sandboxConfig, LogInteractiveShells = logInteractiveShells, EnvValueMode = envValueMode, AllowAllMcpServerInstructions = allowAllMcpServerInstructions, SkillDirectories = skillDirectories, DisabledSkills = disabledSkills, EnableOnDemandInstructionDiscovery = enableOnDemandInstructionDiscovery, MaxInlineBinaryBytes = maxInlineBinaryBytes, InstalledPlugins = installedPlugins, CustomAgentsLocalOnly = customAgentsLocalOnly, SuppressCustomAgentPrompt = suppressCustomAgentPrompt, SkipCustomInstructions = skipCustomInstructions, DisabledInstructionSources = disabledInstructionSources, CoauthorEnabled = coauthorEnabled, TrajectoryFile = trajectoryFile, EnableStreaming = enableStreaming, CopilotUrl = copilotUrl, AskUserDisabled = askUserDisabled, ContinueOnAutoMode = continueOnAutoMode, RunningInInteractiveMode = runningInInteractiveMode, EnableReasoningSummaries = enableReasoningSummaries, AgentContext = agentContext, EventsLogDirectory = eventsLogDirectory, AdditionalContentExclusionPolicies = additionalContentExclusionPolicies, ManageScheduleEnabled = manageScheduleEnabled, SessionCapabilities = sessionCapabilities, SkipEmbeddingRetrieval = skipEmbeddingRetrieval, OrganizationCustomInstructions = organizationCustomInstructions, EnableFileHooks = enableFileHooks, EnableHostGitOperations = enableHostGitOperations, EnableSessionStore = enableSessionStore, EnableSkills = enableSkills, ContextTier = contextTier, ResponseLimits = responseLimits }; return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.options.update", [request], cancellationToken); } } @@ -21058,6 +21344,41 @@ public async Task NotifySteerableChangedAsyn } } +/// Provides session-scoped Visibility APIs. +[Experimental(Diagnostics.Experimental)] +public sealed class VisibilityApi +{ + private readonly CopilotSession _session; + + internal VisibilityApi(CopilotSession session) + { + _session = session; + } + + /// Returns the session's current Mission Control sharing status and shareable GitHub URL. Reflects whether the synced session is visible to repository readers ("repo") or restricted to its creator and collaborators ("unshared"). + /// The to monitor for cancellation requests. The default is . + /// Current sharing status and shareable GitHub URL for a session. + public async Task GetAsync(CancellationToken cancellationToken = default) + { + _session.ThrowIfDisposed(); + + var request = new SessionVisibilityGetRequest { SessionId = _session.SessionId }; + return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.visibility.get", [request], cancellationToken); + } + + /// Sets the session's Mission Control sharing status, controlling whether the synced session is visible to repository readers. Returns the effective status and shareable GitHub URL after the change. + /// Sharing status to apply. "repo" makes the session visible to repository readers; "unshared" restricts it to the creator and collaborators. + /// The to monitor for cancellation requests. The default is . + /// Effective sharing status and shareable GitHub URL after updating session visibility. + public async Task SetAsync(SessionVisibilityStatus status, CancellationToken cancellationToken = default) + { + _session.ThrowIfDisposed(); + + var request = new VisibilitySetRequest { SessionId = _session.SessionId, Status = status }; + return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.visibility.set", [request], cancellationToken); + } +} + /// Provides session-scoped Schedule APIs. [Experimental(Diagnostics.Experimental)] public sealed class ScheduleApi @@ -21563,7 +21884,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(GitHub.Copilot.PersistedBinaryResult), TypeInfoPropertyName = "SessionEventsPersistedBinaryResult")] [JsonSerializable(typeof(GitHub.Copilot.PlanChangedOperation), TypeInfoPropertyName = "SessionEventsPlanChangedOperation")] [JsonSerializable(typeof(GitHub.Copilot.ReasoningSummary), TypeInfoPropertyName = "SessionEventsReasoningSummary")] -[JsonSerializable(typeof(GitHub.Copilot.ResponseBudgetConfig), TypeInfoPropertyName = "SessionEventsResponseBudgetConfig")] +[JsonSerializable(typeof(GitHub.Copilot.ResponseLimitsConfig), TypeInfoPropertyName = "SessionEventsResponseLimitsConfig")] [JsonSerializable(typeof(GitHub.Copilot.SamplingCompletedData), TypeInfoPropertyName = "SessionEventsSamplingCompletedData")] [JsonSerializable(typeof(GitHub.Copilot.SamplingCompletedEvent), TypeInfoPropertyName = "SessionEventsSamplingCompletedEvent")] [JsonSerializable(typeof(GitHub.Copilot.SamplingRequestedData), TypeInfoPropertyName = "SessionEventsSamplingRequestedData")] @@ -21614,6 +21935,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(GitHub.Copilot.ToolExecutionCompleteContentResourceLink), TypeInfoPropertyName = "SessionEventsToolExecutionCompleteContentResourceLink")] [JsonSerializable(typeof(GitHub.Copilot.ToolExecutionCompleteContentResourceLinkIcon), TypeInfoPropertyName = "SessionEventsToolExecutionCompleteContentResourceLinkIcon")] [JsonSerializable(typeof(GitHub.Copilot.ToolExecutionCompleteContentResourceLinkIconTheme), TypeInfoPropertyName = "SessionEventsToolExecutionCompleteContentResourceLinkIconTheme")] +[JsonSerializable(typeof(GitHub.Copilot.ToolExecutionCompleteContentShellExit), TypeInfoPropertyName = "SessionEventsToolExecutionCompleteContentShellExit")] [JsonSerializable(typeof(GitHub.Copilot.ToolExecutionCompleteContentTerminal), TypeInfoPropertyName = "SessionEventsToolExecutionCompleteContentTerminal")] [JsonSerializable(typeof(GitHub.Copilot.ToolExecutionCompleteContentText), TypeInfoPropertyName = "SessionEventsToolExecutionCompleteContentText")] [JsonSerializable(typeof(GitHub.Copilot.ToolExecutionCompleteData), TypeInfoPropertyName = "SessionEventsToolExecutionCompleteData")] @@ -22129,6 +22451,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(SessionUpdateOptionsParams))] [JsonSerializable(typeof(SessionUpdateOptionsResult))] [JsonSerializable(typeof(SessionUsageGetMetricsRequest))] +[JsonSerializable(typeof(SessionVisibilityGetRequest))] [JsonSerializable(typeof(SessionWorkingDirectoryContext))] [JsonSerializable(typeof(SessionWorkspacesGetWorkspaceRequest))] [JsonSerializable(typeof(SessionWorkspacesListCheckpointsRequest))] @@ -22251,6 +22574,13 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(UsageMetricsModelMetricUsage))] [JsonSerializable(typeof(UsageMetricsTokenDetail))] [JsonSerializable(typeof(UserRequestedShellCommandResult))] +[JsonSerializable(typeof(UserSettingMetadata))] +[JsonSerializable(typeof(UserSettingsGetResult))] +[JsonSerializable(typeof(UserSettingsSetRequest))] +[JsonSerializable(typeof(UserSettingsSetResult))] +[JsonSerializable(typeof(VisibilityGetResult))] +[JsonSerializable(typeof(VisibilitySetRequest))] +[JsonSerializable(typeof(VisibilitySetResult))] [JsonSerializable(typeof(WorkspaceDiffFileChange))] [JsonSerializable(typeof(WorkspaceDiffResult))] [JsonSerializable(typeof(WorkspacesCheckpoints))] diff --git a/dotnet/src/Generated/SessionEvents.cs b/dotnet/src/Generated/SessionEvents.cs index a2333f52d0..7f55dde080 100644 --- a/dotnet/src/Generated/SessionEvents.cs +++ b/dotnet/src/Generated/SessionEvents.cs @@ -90,6 +90,7 @@ namespace GitHub.Copilot; [JsonDerivedType(typeof(SessionPermissionsChangedEvent), "session.permissions_changed")] [JsonDerivedType(typeof(SessionPlanChangedEvent), "session.plan_changed")] [JsonDerivedType(typeof(SessionRemoteSteerableChangedEvent), "session.remote_steerable_changed")] +[JsonDerivedType(typeof(SessionResponseLimitsChangedEvent), "session.response_limits_changed")] [JsonDerivedType(typeof(SessionResumeEvent), "session.resume")] [JsonDerivedType(typeof(SessionScheduleCancelledEvent), "session.schedule_cancelled")] [JsonDerivedType(typeof(SessionScheduleCreatedEvent), "session.schedule_created")] @@ -346,6 +347,19 @@ public sealed partial class SessionModeChangedEvent : SessionEvent public required SessionModeChangedData Data { get; set; } } +/// Response limits update details. Null clears the limits. +/// Represents the session.response_limits_changed event. +public sealed partial class SessionResponseLimitsChangedEvent : SessionEvent +{ + /// + [JsonIgnore] + public override string Type => "session.response_limits_changed"; + + /// The session.response_limits_changed event payload. + [JsonPropertyName("data")] + public required SessionResponseLimitsChangedData Data { get; set; } +} + /// Permissions change details carrying the aggregate allow-all boolean transition. /// Represents the session.permissions_changed event. public sealed partial class SessionPermissionsChangedEvent : SessionEvent @@ -1491,10 +1505,10 @@ public sealed partial class SessionStartData [JsonPropertyName("remoteSteerable")] public bool? RemoteSteerable { get; set; } - /// Response budget limits configured at session creation time, if any. + /// Response limits configured at session creation time, if any. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("responseBudget")] - public ResponseBudgetConfig? ResponseBudget { get; set; } + [JsonPropertyName("responseLimits")] + public ResponseLimitsConfig? ResponseLimits { get; set; } /// Model selected at session creation time, if any. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] @@ -1561,10 +1575,10 @@ public sealed partial class SessionResumeData [JsonPropertyName("remoteSteerable")] public bool? RemoteSteerable { get; set; } - /// Response budget limits currently configured at resume time; null when no budget is active. + /// Response limits currently configured at resume time; null when no limits are active. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("responseBudget")] - public ResponseBudgetConfig? ResponseBudget { get; set; } + [JsonPropertyName("responseLimits")] + public ResponseLimitsConfig? ResponseLimits { get; set; } /// ISO 8601 timestamp when the session was resumed. [JsonPropertyName("resumeTime")] @@ -1833,6 +1847,14 @@ public sealed partial class SessionModeChangedData public required SessionMode PreviousMode { get; set; } } +/// Response limits update details. Null clears the limits. +public sealed partial class SessionResponseLimitsChangedData +{ + /// Current response limits for the session, or null when no limits are active. + [JsonPropertyName("responseLimits")] + public ResponseLimitsConfig? ResponseLimits { get; set; } +} + /// Permissions change details carrying the aggregate allow-all boolean transition. public sealed partial class SessionPermissionsChangedData { @@ -3907,19 +3929,14 @@ public sealed partial class WorkingDirectoryContext public string? RepositoryHost { get; set; } } -/// Optional response budget limits. -/// Nested data type for ResponseBudgetConfig. -public sealed partial class ResponseBudgetConfig +/// Optional response limits. +/// Nested data type for ResponseLimitsConfig. +public sealed partial class ResponseLimitsConfig { /// Maximum AI Credits allowed while responding to one top-level user message. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("maxAiCredits")] public double? MaxAiCredits { get; set; } - - /// Maximum model-call iterations allowed while responding to one top-level user message. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("maxModelIterations")] - public long? MaxModelIterations { get; set; } } /// Repository context for the handed-off session. @@ -5340,8 +5357,10 @@ public sealed partial class ToolExecutionCompleteContentText : ToolExecutionComp public required string Text { get; set; } } -/// Terminal/shell output content block with optional exit code and working directory. +/// Deprecated for shell command exit metadata. Use ToolExecutionCompleteContentShellExit instead. /// The terminal variant of . +[EditorBrowsable(EditorBrowsableState.Never)] +[Obsolete("This member is deprecated and will be removed in a future version.")] public sealed partial class ToolExecutionCompleteContentTerminal : ToolExecutionCompleteContent { /// @@ -5363,6 +5382,38 @@ public sealed partial class ToolExecutionCompleteContentTerminal : ToolExecution public required string Text { get; set; } } +/// Shell command exit metadata with optional output preview. +/// The shell_exit variant of . +public sealed partial class ToolExecutionCompleteContentShellExit : ToolExecutionCompleteContent +{ + /// + [JsonIgnore] + public override string Type => "shell_exit"; + + /// Working directory where the shell command was executed. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("cwd")] + public string? Cwd { get; set; } + + /// Exit code from the completed shell command. + [JsonPropertyName("exitCode")] + public required long ExitCode { get; set; } + + /// Output associated with this shell command, if available. May be partial, truncated, or a preview; not guaranteed to be full output. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("outputPreview")] + public string? OutputPreview { get; set; } + + /// Whether outputPreview is known to be incomplete or truncated. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("outputTruncated")] + public bool? OutputTruncated { get; set; } + + /// Shell id, as assigned by Copilot runtime. + [JsonPropertyName("shellId")] + public required string ShellId { get; set; } +} + /// Image content block with base64-encoded data. /// The image variant of . public sealed partial class ToolExecutionCompleteContentImage : ToolExecutionCompleteContent @@ -5600,6 +5651,7 @@ public sealed partial class ToolExecutionCompleteContentResource : ToolExecution UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] [JsonDerivedType(typeof(ToolExecutionCompleteContentText), "text")] [JsonDerivedType(typeof(ToolExecutionCompleteContentTerminal), "terminal")] +[JsonDerivedType(typeof(ToolExecutionCompleteContentShellExit), "shell_exit")] [JsonDerivedType(typeof(ToolExecutionCompleteContentImage), "image")] [JsonDerivedType(typeof(ToolExecutionCompleteContentAudio), "audio")] [JsonDerivedType(typeof(ToolExecutionCompleteContentResourceLink), "resource_link")] @@ -10500,7 +10552,7 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(PermissionRule))] [JsonSerializable(typeof(PersistedBinaryImage))] [JsonSerializable(typeof(PersistedBinaryResult))] -[JsonSerializable(typeof(ResponseBudgetConfig))] +[JsonSerializable(typeof(ResponseLimitsConfig))] [JsonSerializable(typeof(SamplingCompletedData))] [JsonSerializable(typeof(SamplingCompletedEvent))] [JsonSerializable(typeof(SamplingRequestedData))] @@ -10560,6 +10612,8 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(SessionPlanChangedEvent))] [JsonSerializable(typeof(SessionRemoteSteerableChangedData))] [JsonSerializable(typeof(SessionRemoteSteerableChangedEvent))] +[JsonSerializable(typeof(SessionResponseLimitsChangedData))] +[JsonSerializable(typeof(SessionResponseLimitsChangedEvent))] [JsonSerializable(typeof(SessionResumeData))] [JsonSerializable(typeof(SessionResumeEvent))] [JsonSerializable(typeof(SessionScheduleCancelledData))] @@ -10630,6 +10684,7 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(ToolExecutionCompleteContentResourceDetails))] [JsonSerializable(typeof(ToolExecutionCompleteContentResourceLink))] [JsonSerializable(typeof(ToolExecutionCompleteContentResourceLinkIcon))] +[JsonSerializable(typeof(ToolExecutionCompleteContentShellExit))] [JsonSerializable(typeof(ToolExecutionCompleteContentTerminal))] [JsonSerializable(typeof(ToolExecutionCompleteContentText))] [JsonSerializable(typeof(ToolExecutionCompleteData))] diff --git a/go/rpc/zrpc.go b/go/rpc/zrpc.go index fa6de97204..69569144f6 100644 --- a/go/rpc/zrpc.go +++ b/go/rpc/zrpc.go @@ -29,6 +29,7 @@ type AbortResult struct { } // Schema for the `AccountAllUsers` type. +// Experimental: AccountAllUsers is part of an experimental API and may change or be removed. type AccountAllUsers struct { // Authentication information for this user AuthInfo AuthInfo `json:"authInfo"` @@ -37,9 +38,13 @@ type AccountAllUsers struct { } // List of all authenticated users +// Experimental: AccountGetAllUsersResult is part of an experimental API and may change or +// be removed. type AccountGetAllUsersResult []AccountAllUsers // Current authentication state +// Experimental: AccountGetCurrentAuthResult is part of an experimental API and may change +// or be removed. type AccountGetCurrentAuthResult struct { // Authentication errors from the last auth attempt, if any AuthErrors []string `json:"authErrors,omitzero"` @@ -47,6 +52,8 @@ type AccountGetCurrentAuthResult struct { AuthInfo AuthInfo `json:"authInfo,omitempty"` } +// Experimental: AccountGetQuotaRequest is part of an experimental API and may change or be +// removed. type AccountGetQuotaRequest struct { // GitHub token for per-user quota lookup. When provided, resolves this token to determine // the user's quota instead of using the global auth. @@ -54,12 +61,16 @@ type AccountGetQuotaRequest struct { } // Quota usage snapshots for the resolved user, keyed by quota type. +// Experimental: AccountGetQuotaResult is part of an experimental API and may change or be +// removed. type AccountGetQuotaResult struct { // Quota snapshots keyed by type (e.g., chat, completions, premium_interactions) QuotaSnapshots map[string]AccountQuotaSnapshot `json:"quotaSnapshots"` } // Credentials to store after successful authentication +// Experimental: AccountLoginRequest is part of an experimental API and may change or be +// removed. type AccountLoginRequest struct { // GitHub host URL Host string `json:"host"` @@ -70,6 +81,8 @@ type AccountLoginRequest struct { } // Result of a successful login; throws on failure +// Experimental: AccountLoginResult is part of an experimental API and may change or be +// removed. type AccountLoginResult struct { // Whether the credential was persisted to a secure store (system keychain, or the config // file when plaintext storage is enabled). False when no secure store was available and the @@ -78,18 +91,24 @@ type AccountLoginResult struct { } // User to log out +// Experimental: AccountLogoutRequest is part of an experimental API and may change or be +// removed. type AccountLogoutRequest struct { // Authentication information for the user to log out AuthInfo AuthInfo `json:"authInfo"` } // Logout result indicating if more users remain +// Experimental: AccountLogoutResult is part of an experimental API and may change or be +// removed. type AccountLogoutResult struct { // Whether other authenticated users remain after logout HasMoreUsers bool `json:"hasMoreUsers"` } // Schema for the `AccountQuotaSnapshot` type. +// Experimental: AccountQuotaSnapshot is part of an experimental API and may change or be +// removed. type AccountQuotaSnapshot struct { // Number of requests included in the entitlement, or -1 for unlimited entitlements EntitlementRequests int64 `json:"entitlementRequests"` @@ -817,6 +836,7 @@ type AttachmentSelectionDetailsStart struct { } // Initial authentication info for the session. +// Experimental: AuthInfo is part of an experimental API and may change or be removed. type AuthInfo interface { authInfo() Type() AuthInfoType @@ -833,6 +853,7 @@ func (r RawAuthInfoData) Type() AuthInfoType { } // Schema for the `ApiKeyAuthInfo` type. +// Experimental: APIKeyAuthInfo is part of an experimental API and may change or be removed. type APIKeyAuthInfo struct { // The API key. Treat as a secret. APIKey string `json:"apiKey"` @@ -850,6 +871,8 @@ func (APIKeyAuthInfo) Type() AuthInfoType { } // Schema for the `CopilotApiTokenAuthInfo` type. +// Experimental: CopilotAPITokenAuthInfo is part of an experimental API and may change or be +// removed. type CopilotAPITokenAuthInfo struct { // Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the // GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this @@ -865,6 +888,7 @@ func (CopilotAPITokenAuthInfo) Type() AuthInfoType { } // Schema for the `EnvAuthInfo` type. +// Experimental: EnvAuthInfo is part of an experimental API and may change or be removed. type EnvAuthInfo struct { // Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the // GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this @@ -887,6 +911,7 @@ func (EnvAuthInfo) Type() AuthInfoType { } // Schema for the `GhCliAuthInfo` type. +// Experimental: GhCLIAuthInfo is part of an experimental API and may change or be removed. type GhCLIAuthInfo struct { // Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the // GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this @@ -906,6 +931,7 @@ func (GhCLIAuthInfo) Type() AuthInfoType { } // Schema for the `HMACAuthInfo` type. +// Experimental: HMACAuthInfo is part of an experimental API and may change or be removed. type HMACAuthInfo struct { // Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the // GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this @@ -923,6 +949,7 @@ func (HMACAuthInfo) Type() AuthInfoType { } // Schema for the `TokenAuthInfo` type. +// Experimental: TokenAuthInfo is part of an experimental API and may change or be removed. type TokenAuthInfo struct { // Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the // GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this @@ -940,6 +967,7 @@ func (TokenAuthInfo) Type() AuthInfoType { } // Schema for the `UserAuthInfo` type. +// Experimental: UserAuthInfo is part of an experimental API and may change or be removed. type UserAuthInfo struct { // Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the // GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this @@ -1284,6 +1312,7 @@ type ConnectRemoteSessionParams struct { } // Optional connection token presented by the SDK client during the handshake. +// Experimental: ConnectRequest is part of an experimental API and may change or be removed. // Internal: ConnectRequest is an internal SDK API and is not part of the public surface. type ConnectRequest struct { // Connection token; required when the server was started with COPILOT_CONNECTION_TOKEN @@ -1291,6 +1320,7 @@ type ConnectRequest struct { } // Handshake result reporting the server's protocol version and package version on success. +// Experimental: ConnectResult is part of an experimental API and may change or be removed. // Internal: ConnectResult is an internal SDK API and is not part of the public surface. type ConnectResult struct { // Always true on success @@ -1304,6 +1334,8 @@ type ConnectResult struct { // Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the // GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this // verbatim and does not re-fetch when set. +// Experimental: CopilotUserResponse is part of an experimental API and may change or be +// removed. type CopilotUserResponse struct { AccessTypeSku *string `json:"access_type_sku,omitempty"` AnalyticsTrackingID *string `json:"analytics_tracking_id,omitempty"` @@ -1335,6 +1367,8 @@ type CopilotUserResponse struct { } // Schema for the `CopilotUserResponseEndpoints` type. +// Experimental: CopilotUserResponseEndpoints is part of an experimental API and may change +// or be removed. type CopilotUserResponseEndpoints struct { API *string `json:"api,omitempty"` OriginTracker *string `json:"origin-tracker,omitempty"` @@ -1348,6 +1382,8 @@ type CopilotUserResponseOrganizationListItem struct { } // Schema for the `CopilotUserResponseQuotaSnapshots` type. +// Experimental: CopilotUserResponseQuotaSnapshots is part of an experimental API and may +// change or be removed. type CopilotUserResponseQuotaSnapshots struct { // Schema for the `CopilotUserResponseQuotaSnapshotsChat` type. Chat *CopilotUserResponseQuotaSnapshotsChat `json:"chat,omitempty"` @@ -1358,6 +1394,8 @@ type CopilotUserResponseQuotaSnapshots struct { } // Schema for the `CopilotUserResponseQuotaSnapshotsChat` type. +// Experimental: CopilotUserResponseQuotaSnapshotsChat is part of an experimental API and +// may change or be removed. type CopilotUserResponseQuotaSnapshotsChat struct { Entitlement *float64 `json:"entitlement,omitempty"` HasQuota *bool `json:"has_quota,omitempty"` @@ -1374,6 +1412,8 @@ type CopilotUserResponseQuotaSnapshotsChat struct { } // Schema for the `CopilotUserResponseQuotaSnapshotsCompletions` type. +// Experimental: CopilotUserResponseQuotaSnapshotsCompletions is part of an experimental API +// and may change or be removed. type CopilotUserResponseQuotaSnapshotsCompletions struct { Entitlement *float64 `json:"entitlement,omitempty"` HasQuota *bool `json:"has_quota,omitempty"` @@ -1390,6 +1430,8 @@ type CopilotUserResponseQuotaSnapshotsCompletions struct { } // Schema for the `CopilotUserResponseQuotaSnapshotsPremiumInteractions` type. +// Experimental: CopilotUserResponseQuotaSnapshotsPremiumInteractions is part of an +// experimental API and may change or be removed. type CopilotUserResponseQuotaSnapshotsPremiumInteractions struct { Entitlement *float64 `json:"entitlement,omitempty"` HasQuota *bool `json:"has_quota,omitempty"` @@ -1461,6 +1503,8 @@ type DiscoveredCanvas struct { } // Schema for the `DiscoveredMcpServer` type. +// Experimental: DiscoveredMCPServer is part of an experimental API and may change or be +// removed. type DiscoveredMCPServer struct { // Whether the server is enabled (not in the disabled list) Enabled bool `json:"enabled"` @@ -1766,6 +1810,28 @@ func (ExternalToolTextResultForLlmContentResourceLink) Type() ExternalToolTextRe return ExternalToolTextResultForLlmContentTypeResourceLink } +// Shell command exit metadata with optional output preview +// Experimental: ExternalToolTextResultForLlmContentShellExit is part of an experimental API +// and may change or be removed. +type ExternalToolTextResultForLlmContentShellExit struct { + // Working directory where the shell command was executed + Cwd *string `json:"cwd,omitempty"` + // Exit code from the completed shell command + ExitCode int64 `json:"exitCode"` + // Output associated with this shell command, if available. May be partial, truncated, or a + // preview; not guaranteed to be full output. + OutputPreview *string `json:"outputPreview,omitempty"` + // Whether outputPreview is known to be incomplete or truncated + OutputTruncated *bool `json:"outputTruncated,omitempty"` + // Shell id, as assigned by Copilot runtime + ShellID string `json:"shellId"` +} + +func (ExternalToolTextResultForLlmContentShellExit) externalToolTextResultForLlmContent() {} +func (ExternalToolTextResultForLlmContentShellExit) Type() ExternalToolTextResultForLlmContentType { + return ExternalToolTextResultForLlmContentTypeShellExit +} + // Terminal/shell output content block with optional exit code and working directory // Experimental: ExternalToolTextResultForLlmContentTerminal is part of an experimental API // and may change or be removed. @@ -1854,6 +1920,7 @@ type ExternalToolTextResultForLlmContentResourceLinkIcon struct { // Content filtering mode to apply to all tools, or a map of tool name to content filtering // mode. +// Experimental: FilterMapping is part of an experimental API and may change or be removed. type FilterMapping interface { filterMapping() } @@ -2666,6 +2733,8 @@ type MCPCancelSamplingExecutionResult struct { } // MCP server name and configuration to add to user configuration. +// Experimental: MCPConfigAddRequest is part of an experimental API and may change or be +// removed. type MCPConfigAddRequest struct { // MCP server configuration (stdio process or remote HTTP/SSE) Config MCPServerConfig `json:"config"` @@ -2673,10 +2742,14 @@ type MCPConfigAddRequest struct { Name string `json:"name"` } +// Experimental: MCPConfigAddResult is part of an experimental API and may change or be +// removed. type MCPConfigAddResult struct { } // MCP server names to disable for new sessions. +// Experimental: MCPConfigDisableRequest is part of an experimental API and may change or be +// removed. type MCPConfigDisableRequest struct { // Names of MCP servers to disable. Each server is added to the persisted disabled list so // new sessions skip it. Already-disabled names are ignored. Active sessions keep their @@ -2684,38 +2757,53 @@ type MCPConfigDisableRequest struct { Names []string `json:"names"` } +// Experimental: MCPConfigDisableResult is part of an experimental API and may change or be +// removed. type MCPConfigDisableResult struct { } // MCP server names to enable for new sessions. +// Experimental: MCPConfigEnableRequest is part of an experimental API and may change or be +// removed. type MCPConfigEnableRequest struct { // Names of MCP servers to enable. Each server is removed from the persisted disabled list // so new sessions spawn it. Unknown or already-enabled names are ignored. Names []string `json:"names"` } +// Experimental: MCPConfigEnableResult is part of an experimental API and may change or be +// removed. type MCPConfigEnableResult struct { } // User-configured MCP servers, keyed by server name. +// Experimental: MCPConfigList is part of an experimental API and may change or be removed. type MCPConfigList struct { // All MCP servers from user config, keyed by name Servers map[string]MCPServerConfig `json:"servers"` } +// Experimental: MCPConfigReloadResult is part of an experimental API and may change or be +// removed. type MCPConfigReloadResult struct { } // MCP server name to remove from user configuration. +// Experimental: MCPConfigRemoveRequest is part of an experimental API and may change or be +// removed. type MCPConfigRemoveRequest struct { // Name of the MCP server to remove Name string `json:"name"` } +// Experimental: MCPConfigRemoveResult is part of an experimental API and may change or be +// removed. type MCPConfigRemoveResult struct { } // MCP server name and replacement configuration to write to user configuration. +// Experimental: MCPConfigUpdateRequest is part of an experimental API and may change or be +// removed. type MCPConfigUpdateRequest struct { // MCP server configuration (stdio process or remote HTTP/SSE) Config MCPServerConfig `json:"config"` @@ -2723,6 +2811,8 @@ type MCPConfigUpdateRequest struct { Name string `json:"name"` } +// Experimental: MCPConfigUpdateResult is part of an experimental API and may change or be +// removed. type MCPConfigUpdateResult struct { } @@ -2754,12 +2844,16 @@ type MCPDisableRequest struct { } // Optional working directory used as context for MCP server discovery. +// Experimental: MCPDiscoverRequest is part of an experimental API and may change or be +// removed. type MCPDiscoverRequest struct { // Working directory used as context for discovery (e.g., plugin resolution) WorkingDirectory *string `json:"workingDirectory,omitempty"` } // MCP servers discovered from user, workspace, plugin, and built-in sources. +// Experimental: MCPDiscoverResult is part of an experimental API and may change or be +// removed. type MCPDiscoverResult struct { // MCP servers discovered from all sources Servers []DiscoveredMCPServer `json:"servers"` @@ -3153,6 +3247,8 @@ type MCPServer struct { } // Set to `true` to use defaults, or provide an object with additional auth or OIDC settings. +// Experimental: MCPServerAuthConfig is part of an experimental API and may change or be +// removed. type MCPServerAuthConfig interface { mcpServerAuthConfig() } @@ -3164,12 +3260,15 @@ func (MCPServerAuthConfigBoolean) mcpServerAuthConfig() {} func (MCPServerAuthConfigRedirectPort) mcpServerAuthConfig() {} // Authentication settings with optional redirect port configuration. +// Experimental: MCPServerAuthConfigRedirectPort is part of an experimental API and may +// change or be removed. type MCPServerAuthConfigRedirectPort struct { // Fixed port for the OAuth redirect callback server. RedirectPort *int32 `json:"redirectPort,omitempty"` } // MCP server configuration (stdio process or remote HTTP/SSE) +// Experimental: MCPServerConfig is part of an experimental API and may change or be removed. type MCPServerConfig interface { mcpServerConfig() } @@ -3181,6 +3280,8 @@ type RawMCPServerConfigData struct { func (RawMCPServerConfigData) mcpServerConfig() {} // Remote MCP server configuration accessed over HTTP or SSE. +// Experimental: MCPServerConfigHTTP is part of an experimental API and may change or be +// removed. type MCPServerConfigHTTP struct { // Set to `true` to use defaults, or provide an object with additional auth or OIDC settings. Auth MCPServerAuthConfig `json:"auth,omitempty"` @@ -3216,6 +3317,8 @@ type MCPServerConfigHTTP struct { func (MCPServerConfigHTTP) mcpServerConfig() {} // Stdio MCP server configuration launched as a child process. +// Experimental: MCPServerConfigStdio is part of an experimental API and may change or be +// removed. type MCPServerConfigStdio struct { // Command-line arguments passed to the Stdio MCP server process. Args []string `json:"args,omitzero"` @@ -3476,6 +3579,7 @@ type MetadataSnapshotRemoteMetadataRepository struct { } // Schema for the `Model` type. +// Experimental: Model is part of an experimental API and may change or be removed. type Model struct { // Billing information Billing *ModelBilling `json:"billing,omitempty"` @@ -3498,6 +3602,7 @@ type Model struct { } // Billing information +// Experimental: ModelBilling is part of an experimental API and may change or be removed. type ModelBilling struct { // Whole-number percentage discount (0-100) applied to usage billed through this model. // Populated for the synthetic `auto` model, where requests routed by auto-mode are billed @@ -3510,6 +3615,8 @@ type ModelBilling struct { } // Token-level pricing information for this model +// Experimental: ModelBillingTokenPrices is part of an experimental API and may change or be +// removed. type ModelBillingTokenPrices struct { // Number of tokens per standard billing batch BatchSize *int64 `json:"batchSize,omitempty"` @@ -3536,6 +3643,8 @@ type ModelBillingTokenPrices struct { } // Long context tier pricing (available for models with extended context windows) +// Experimental: ModelBillingTokenPricesLongContext is part of an experimental API and may +// change or be removed. type ModelBillingTokenPricesLongContext struct { // Use cacheReadPrice instead. AI Credits cost per billing batch of cached tokens // Deprecated: CachePrice is deprecated. @@ -3558,6 +3667,8 @@ type ModelBillingTokenPricesLongContext struct { } // Model capabilities and limits +// Experimental: ModelCapabilities is part of an experimental API and may change or be +// removed. type ModelCapabilities struct { // Token limits for prompts, outputs, and context window Limits *ModelCapabilitiesLimits `json:"limits,omitempty"` @@ -3566,6 +3677,8 @@ type ModelCapabilities struct { } // Token limits for prompts, outputs, and context window +// Experimental: ModelCapabilitiesLimits is part of an experimental API and may change or be +// removed. type ModelCapabilitiesLimits struct { // Maximum total context window size in tokens MaxContextWindowTokens *int64 `json:"max_context_window_tokens,omitempty"` @@ -3578,6 +3691,8 @@ type ModelCapabilitiesLimits struct { } // Vision-specific limits +// Experimental: ModelCapabilitiesLimitsVision is part of an experimental API and may change +// or be removed. type ModelCapabilitiesLimitsVision struct { // Maximum number of images per prompt MaxPromptImages int64 `json:"max_prompt_images"` @@ -3634,6 +3749,8 @@ type ModelCapabilitiesOverrideSupports struct { } // Feature flags indicating what the model supports +// Experimental: ModelCapabilitiesSupports is part of an experimental API and may change or +// be removed. type ModelCapabilitiesSupports struct { // Whether this model supports reasoning effort configuration ReasoningEffort *bool `json:"reasoningEffort,omitempty"` @@ -3643,6 +3760,7 @@ type ModelCapabilitiesSupports struct { // List of Copilot models available to the resolved user, including capabilities and billing // metadata. +// Experimental: ModelList is part of an experimental API and may change or be removed. type ModelList struct { // List of available models with full metadata Models []Model `json:"models"` @@ -3657,6 +3775,7 @@ type ModelListRequest struct { } // Policy state (if applicable) +// Experimental: ModelPolicy is part of an experimental API and may change or be removed. type ModelPolicy struct { // Current policy state for this model State ModelPolicyState `json:"state"` @@ -3683,6 +3802,8 @@ type ModelSetReasoningEffortResult struct { ReasoningEffort string `json:"reasoningEffort"` } +// Experimental: ModelsListRequest is part of an experimental API and may change or be +// removed. type ModelsListRequest struct { // GitHub token for per-user model listing. When provided, resolves this token to determine // the user's Copilot plan and available models instead of using the global auth. @@ -4932,6 +5053,7 @@ type PermissionURLsSetUnrestrictedModeParams struct { } // Optional message to echo back to the caller. +// Experimental: PingRequest is part of an experimental API and may change or be removed. type PingRequest struct { // Optional message to echo back Message *string `json:"message,omitempty"` @@ -4939,6 +5061,7 @@ type PingRequest struct { // Server liveness response, including the echoed message, current server timestamp, and // protocol version. +// Experimental: PingResult is part of an experimental API and may change or be removed. type PingResult struct { // Echoed message (or default greeting) Message string `json:"message"` @@ -6114,16 +6237,16 @@ type RemoteSessionRepository struct { Owner string `json:"owner"` } -// Optional response budget limits. -// Experimental: ResponseBudgetConfig is part of an experimental API and may change or be +// Optional response limits. +// Experimental: ResponseLimitsConfig is part of an experimental API and may change or be // removed. -type ResponseBudgetConfig struct { +type ResponseLimitsConfig struct { // Maximum AI Credits allowed while responding to one top-level user message. MaxAiCredits *float64 `json:"maxAiCredits,omitempty"` - // Maximum model-call iterations allowed while responding to one top-level user message. - MaxModelIterations *int64 `json:"maxModelIterations,omitempty"` } +// Experimental: RuntimeShutdownResult is part of an experimental API and may change or be +// removed. type RuntimeShutdownResult struct { } @@ -6257,12 +6380,16 @@ type ScheduleStopResult struct { } // Secret values to add to the redaction filter. +// Experimental: SecretsAddFilterValuesRequest is part of an experimental API and may change +// or be removed. type SecretsAddFilterValuesRequest struct { // Raw secret values to register for redaction Values []string `json:"values"` } // Confirmation that the secret values were registered. +// Experimental: SecretsAddFilterValuesResult is part of an experimental API and may change +// or be removed. type SecretsAddFilterValuesResult struct { // Whether the values were successfully registered Ok bool `json:"ok"` @@ -6350,6 +6477,7 @@ type ServerInstructionSourceList struct { } // Schema for the `ServerSkill` type. +// Experimental: ServerSkill is part of an experimental API and may change or be removed. type ServerSkill struct { // Optional freeform hint describing the skill's expected arguments, from the // `argument-hint` frontmatter field @@ -6371,6 +6499,7 @@ type ServerSkill struct { } // Skills discovered across global and project sources. +// Experimental: ServerSkillList is part of an experimental API and may change or be removed. type ServerSkillList struct { // All discovered skills across all sources Skills []ServerSkill `json:"skills"` @@ -6654,6 +6783,8 @@ type SessionFSRmRequest struct { } // Optional capabilities declared by the provider +// Experimental: SessionFSSetProviderCapabilities is part of an experimental API and may +// change or be removed. type SessionFSSetProviderCapabilities struct { // Whether the provider supports SQLite query/exists operations Sqlite *bool `json:"sqlite,omitempty"` @@ -6661,6 +6792,8 @@ type SessionFSSetProviderCapabilities struct { // Initial working directory, session-state path layout, and path conventions used to // register the calling SDK client as the session filesystem provider. +// Experimental: SessionFSSetProviderRequest is part of an experimental API and may change +// or be removed. type SessionFSSetProviderRequest struct { // Optional capabilities declared by the provider Capabilities *SessionFSSetProviderCapabilities `json:"capabilities,omitempty"` @@ -6673,6 +6806,8 @@ type SessionFSSetProviderRequest struct { } // Indicates whether the calling client was registered as the session filesystem provider. +// Experimental: SessionFSSetProviderResult is part of an experimental API and may change or +// be removed. type SessionFSSetProviderResult struct { // Whether the provider was set successfully Success bool `json:"success"` @@ -6996,6 +7131,8 @@ type SessionMetadataSnapshot struct { // Remote-session-specific metadata. Populated only when `isRemote` is true. Fields are // immutable for the lifetime of the session. RemoteMetadata *MetadataSnapshotRemoteMetadata `json:"remoteMetadata,omitempty"` + // Current response limits for the session, or null when no limits are active + ResponseLimits *ResponseLimitsConfig `json:"responseLimits"` // Currently selected model identifier, if any SelectedModel *string `json:"selectedModel,omitempty"` // The unique identifier of the session @@ -7148,8 +7285,8 @@ type SessionOpenOptions struct { RemoteExporting *bool `json:"remoteExporting,omitempty"` // Whether this session supports remote steering. RemoteSteerable *bool `json:"remoteSteerable,omitempty"` - // Initial response budget limits for the session. - ResponseBudget *ResponseBudgetConfig `json:"responseBudget,omitempty"` + // Initial response limits for the session. + ResponseLimits *ResponseLimitsConfig `json:"responseLimits,omitempty"` // Whether the host is an interactive UI. RunningInInteractiveMode *bool `json:"runningInInteractiveMode,omitempty"` // Resolved sandbox configuration. @@ -7975,8 +8112,8 @@ type SessionUpdateOptionsParams struct { ReasoningEffort *string `json:"reasoningEffort,omitempty"` // Reasoning summary mode for supported model clients. ReasoningSummary *OptionsUpdateReasoningSummary `json:"reasoningSummary,omitempty"` - // Optional response budget limits. Pass null to clear the response budget. - ResponseBudget *ResponseBudgetConfig `json:"responseBudget,omitempty"` + // Optional response limits. Pass null to clear the response limits. + ResponseLimits *ResponseLimitsConfig `json:"responseLimits,omitempty"` // Whether the session is running in an interactive UI. RunningInInteractiveMode *bool `json:"runningInInteractiveMode,omitempty"` // Resolved sandbox configuration. @@ -8165,11 +8302,15 @@ type SkillList struct { } // Skill names to mark as disabled in global configuration, replacing any previous list. +// Experimental: SkillsConfigSetDisabledSkillsRequest is part of an experimental API and may +// change or be removed. type SkillsConfigSetDisabledSkillsRequest struct { // List of skill names to disable DisabledSkills []string `json:"disabledSkills"` } +// Experimental: SkillsConfigSetDisabledSkillsResult is part of an experimental API and may +// change or be removed. type SkillsConfigSetDisabledSkillsResult struct { } @@ -8182,6 +8323,8 @@ type SkillsDisableRequest struct { } // Optional project paths and additional skill directories to include in discovery. +// Experimental: SkillsDiscoverRequest is part of an experimental API and may change or be +// removed. type SkillsDiscoverRequest struct { // When true, omit skills from the host's global sources (personal, custom, plugin, and // built-in), returning only project-scoped skills. For multitenant deployments. @@ -8744,6 +8887,7 @@ type TelemetrySetFeatureOverridesRequest struct { } // Schema for the `Tool` type. +// Experimental: Tool is part of an experimental API and may change or be removed. type Tool struct { // Description of what the tool does Description string `json:"description"` @@ -8759,6 +8903,7 @@ type Tool struct { } // Built-in tools available for the requested model, with their parameters and instructions. +// Experimental: ToolList is part of an experimental API and may change or be removed. type ToolList struct { // List of available built-in tools with metadata Tools []Tool `json:"tools"` @@ -8781,6 +8926,8 @@ type ToolsInitializeAndValidateResult struct { } // Optional model identifier whose tool overrides should be applied to the listing. +// Experimental: ToolsListRequest is part of an experimental API and may change or be +// removed. type ToolsListRequest struct { // Optional model ID — when provided, the returned tool list reflects model-specific // overrides @@ -9350,9 +9497,57 @@ type UserRequestedShellCommandResult struct { ToolCallID string `json:"toolCallId"` } +// A single user setting's effective value alongside its default, so consumers can render +// settings left at their default. +// Experimental: UserSettingMetadata is part of an experimental API and may change or be +// removed. +type UserSettingMetadata struct { + // The centrally-known default for this setting (null when no default is registered). + Default any `json:"default"` + // True when the user has not set an explicit value for this setting (i.e. it is left at its + // default). Reflects whether the user has overridden the key, not whether the effective + // value happens to equal the default — a key explicitly set to a value identical to the + // default still reports false. + IsDefault bool `json:"isDefault"` + // The effective value: the user's value if set, otherwise the default. + Value any `json:"value"` +} + +// Per-key metadata for every known user setting (settings.json overlaid with the legacy +// config.json, config.json wins), including settings left at their default. Excludes +// repository- and enterprise-managed overrides. +// Experimental: UserSettingsGetResult is part of an experimental API and may change or be +// removed. +type UserSettingsGetResult struct { + // Every known user setting keyed by setting name, each with its effective value, default, + // and whether it is at the default. + Settings map[string]UserSettingMetadata `json:"settings"` +} + +// Experimental: UserSettingsReloadResult is part of an experimental API and may change or +// be removed. type UserSettingsReloadResult struct { } +// Partial user settings to write to settings.json. Each top-level key is written +// individually, replacing the existing value; a key whose value is null is removed. +// Experimental: UserSettingsSetRequest is part of an experimental API and may change or be +// removed. +type UserSettingsSetRequest struct { + // Partial user settings to write, as a free-form object keyed by setting name + Settings any `json:"settings"` +} + +// Outcome of writing user settings. +// Experimental: UserSettingsSetResult is part of an experimental API and may change or be +// removed. +type UserSettingsSetResult struct { + // Top-level keys whose write landed in settings.json but is shadowed by a value still + // present in the legacy config.json (config.json wins on read). The write does not take + // effect until the legacy value is removed. + ShadowedKeys []string `json:"shadowedKeys"` +} + // The approval to add as a session-scoped rule // Experimental: UserToolSessionApproval is part of an experimental API and may change or be // removed. @@ -9471,6 +9666,46 @@ func (UserToolSessionApprovalWrite) Kind() UserToolSessionApprovalKind { return UserToolSessionApprovalKindWrite } +// Current sharing status and shareable GitHub URL for a session. +// Experimental: VisibilityGetResult is part of an experimental API and may change or be +// removed. +type VisibilityGetResult struct { + // Shareable GitHub URL for the session. Present when the session is synced and the URL can + // be resolved. + ShareURL *string `json:"shareUrl,omitempty"` + // Current sharing status. Absent when the session is not synced or the status could not be + // retrieved (e.g. the user is not authenticated). + Status *SessionVisibilityStatus `json:"status,omitempty"` + // Whether the session has been synced to Mission Control (i.e. has a GitHub task). When + // false, the session cannot be shared and `status`/`shareUrl` are absent. + Synced bool `json:"synced"` +} + +// Desired sharing status for the session. +// Experimental: VisibilitySetRequest is part of an experimental API and may change or be +// removed. +type VisibilitySetRequest struct { + // Sharing status to apply. "repo" makes the session visible to repository readers; + // "unshared" restricts it to the creator and collaborators. + Status SessionVisibilityStatus `json:"status"` +} + +// Effective sharing status and shareable GitHub URL after updating session visibility. +// Experimental: VisibilitySetResult is part of an experimental API and may change or be +// removed. +type VisibilitySetResult struct { + // Shareable GitHub URL for the session. Present when the session is synced and the URL can + // be resolved. + ShareURL *string `json:"shareUrl,omitempty"` + // Effective sharing status after the update. May differ from the requested status for task + // types that are already visible to repository readers by default. Absent when the update + // could not be applied (e.g. the session is not synced or the user is not authenticated). + Status *SessionVisibilityStatus `json:"status,omitempty"` + // Whether the session has been synced to Mission Control (i.e. has a GitHub task). When + // false, the visibility change could not be applied and `status`/`shareUrl` are absent. + Synced bool `json:"synced"` +} + // A single changed file and its unified diff. // Experimental: WorkspaceDiffFileChange is part of an experimental API and may change or be // removed. @@ -9913,6 +10148,8 @@ const ( // Controls how MCP tool result content is filtered: none leaves content unchanged, markdown // sanitizes HTML while preserving Markdown-friendly output, and hidden_characters removes // characters that can hide directives. +// Experimental: ContentFilterMode is part of an experimental API and may change or be +// removed. type ContentFilterMode string const ( @@ -9943,6 +10180,8 @@ const ( ) // Server transport type: stdio, http, sse (deprecated), or memory +// Experimental: DiscoveredMCPServerType is part of an experimental API and may change or be +// removed. type DiscoveredMCPServerType string const ( @@ -10056,6 +10295,7 @@ const ( ExternalToolTextResultForLlmContentTypeImage ExternalToolTextResultForLlmContentType = "image" ExternalToolTextResultForLlmContentTypeResource ExternalToolTextResultForLlmContentType = "resource" ExternalToolTextResultForLlmContentTypeResourceLink ExternalToolTextResultForLlmContentType = "resource_link" + ExternalToolTextResultForLlmContentTypeShellExit ExternalToolTextResultForLlmContentType = "shell_exit" ExternalToolTextResultForLlmContentTypeTerminal ExternalToolTextResultForLlmContentType = "terminal" ExternalToolTextResultForLlmContentTypeText ExternalToolTextResultForLlmContentType = "text" ) @@ -10327,6 +10567,8 @@ const ( // Controls if tools provided by this server can be loaded on demand via tool search (auto) // or always included in the initial tool list (never) +// Experimental: MCPServerConfigDeferTools is part of an experimental API and may change or +// be removed. type MCPServerConfigDeferTools string const ( @@ -10337,6 +10579,8 @@ const ( ) // OAuth grant type to use when authenticating to the remote MCP server. +// Experimental: MCPServerConfigHTTPOauthGrantType is part of an experimental API and may +// change or be removed. type MCPServerConfigHTTPOauthGrantType string const ( @@ -10347,6 +10591,8 @@ const ( ) // Remote transport type. Defaults to "http" when omitted. +// Experimental: MCPServerConfigHTTPType is part of an experimental API and may change or be +// removed. type MCPServerConfigHTTPType string const ( @@ -10357,6 +10603,7 @@ const ( ) // Configuration source: user, workspace, plugin, or builtin +// Experimental: MCPServerSource is part of an experimental API and may change or be removed. type MCPServerSource string const ( @@ -10433,6 +10680,8 @@ const ( ) // Model capability category for grouping in the model picker +// Experimental: ModelPickerCategory is part of an experimental API and may change or be +// removed. type ModelPickerCategory string const ( @@ -10445,6 +10694,8 @@ const ( ) // Relative cost tier for token-based billing users +// Experimental: ModelPickerPriceCategory is part of an experimental API and may change or +// be removed. type ModelPickerPriceCategory string const ( @@ -10459,6 +10710,8 @@ const ( ) // Current policy state for this model +// Experimental: ModelPolicyState is part of an experimental API and may change or be +// removed. type ModelPolicyState string const ( @@ -10958,6 +11211,8 @@ const ( ) // Path conventions used by this filesystem +// Experimental: SessionFSSetProviderConventions is part of an experimental API and may +// change or be removed. type SessionFSSetProviderConventions string const ( @@ -11158,6 +11413,19 @@ const ( SessionSourceRemote SessionSource = "remote" ) +// Sharing status for a synced session. "repo" makes the session visible to anyone with read +// access to the repository; "unshared" restricts it to the creator and collaborators. +// Experimental: SessionVisibilityStatus is part of an experimental API and may change or be +// removed. +type SessionVisibilityStatus string + +const ( + // The session is visible to repository readers. + SessionVisibilityStatusRepo SessionVisibilityStatus = "repo" + // The session is restricted to its creator and collaborators. + SessionVisibilityStatusUnshared SessionVisibilityStatus = "unshared" +) + // Hosting platform type of the repository // Experimental: SessionWorkingDirectoryContextHostType is part of an experimental API and // may change or be removed. @@ -11211,6 +11479,7 @@ const ( ) // Source location type (e.g., project, personal-copilot, plugin, builtin) +// Experimental: SkillSource is part of an experimental API and may change or be removed. type SkillSource string const ( @@ -11509,6 +11778,7 @@ type serverAPI struct { client *jsonrpc2.Client } +// Experimental: ServerAccountAPI contains experimental APIs that may change or be removed. type ServerAccountAPI serverAPI // GetAllUsers gets all authenticated users available for account switching. @@ -11786,6 +12056,7 @@ func (a *ServerLlmInferenceAPI) SetProvider(ctx context.Context) (*LlmInferenceS return &result, nil } +// Experimental: ServerMCPAPI contains experimental APIs that may change or be removed. type ServerMCPAPI serverAPI // Discovers MCP servers from user, workspace, plugin, and builtin sources. @@ -11807,6 +12078,7 @@ func (a *ServerMCPAPI) Discover(ctx context.Context, params *MCPDiscoverRequest) return &result, nil } +// Experimental: ServerMCPConfigAPI contains experimental APIs that may change or be removed. type ServerMCPConfigAPI serverAPI // Adds an MCP server to user configuration. @@ -11927,10 +12199,12 @@ func (a *ServerMCPConfigAPI) Update(ctx context.Context, params *MCPConfigUpdate return &result, nil } +// Experimental: Config returns experimental APIs that may change or be removed. func (s *ServerMCPAPI) Config() *ServerMCPConfigAPI { return (*ServerMCPConfigAPI)(s) } +// Experimental: ServerModelsAPI contains experimental APIs that may change or be removed. type ServerModelsAPI serverAPI // Lists Copilot models available to the authenticated user. @@ -12184,6 +12458,7 @@ func (s *ServerPluginsAPI) Marketplaces() *ServerPluginsMarketplacesAPI { return (*ServerPluginsMarketplacesAPI)(s) } +// Experimental: ServerRuntimeAPI contains experimental APIs that may change or be removed. type ServerRuntimeAPI serverAPI // Shutdown gracefully shuts down an SDK-owned runtime. The response is sent only after @@ -12202,6 +12477,7 @@ func (a *ServerRuntimeAPI) Shutdown(ctx context.Context) (*RuntimeShutdownResult return &result, nil } +// Experimental: ServerSecretsAPI contains experimental APIs that may change or be removed. type ServerSecretsAPI serverAPI // AddFilterValues registers secret values for redaction in session logs and exports. The @@ -12224,6 +12500,7 @@ func (a *ServerSecretsAPI) AddFilterValues(ctx context.Context, params *SecretsA return &result, nil } +// Experimental: ServerSessionFSAPI contains experimental APIs that may change or be removed. type ServerSessionFSAPI serverAPI // SetProvider registers an SDK client as the session filesystem provider. @@ -12726,6 +13003,7 @@ func (a *ServerSessionsAPI) TransferRemoteControl(ctx context.Context, params *S return &result, nil } +// Experimental: ServerSkillsAPI contains experimental APIs that may change or be removed. type ServerSkillsAPI serverAPI // Discovers skills across global and project sources. @@ -12758,8 +13036,6 @@ func (a *ServerSkillsAPI) Discover(ctx context.Context, params *SkillsDiscoverRe // // Returns: Canonical locations where skills can be created so the runtime will recognize // them. -// Experimental: GetDiscoveryPaths is an experimental API and may change or be removed in -// future versions. func (a *ServerSkillsAPI) GetDiscoveryPaths(ctx context.Context, params *SkillsGetDiscoveryPathsRequest) (*SkillDiscoveryPathList, error) { raw, err := a.client.Request(ctx, "skills.getDiscoveryPaths", params) if err != nil { @@ -12772,6 +13048,8 @@ func (a *ServerSkillsAPI) GetDiscoveryPaths(ctx context.Context, params *SkillsG return &result, nil } +// Experimental: ServerSkillsConfigAPI contains experimental APIs that may change or be +// removed. type ServerSkillsConfigAPI serverAPI // SetDisabledSkills replaces the global list of disabled skills. @@ -12792,10 +13070,12 @@ func (a *ServerSkillsConfigAPI) SetDisabledSkills(ctx context.Context, params *S return &result, nil } +// Experimental: Config returns experimental APIs that may change or be removed. func (s *ServerSkillsAPI) Config() *ServerSkillsConfigAPI { return (*ServerSkillsConfigAPI)(s) } +// Experimental: ServerToolsAPI contains experimental APIs that may change or be removed. type ServerToolsAPI serverAPI // Lists built-in tools available for a model. @@ -12819,10 +13099,36 @@ func (a *ServerToolsAPI) List(ctx context.Context, params *ToolsListRequest) (*T return &result, nil } +// Experimental: ServerUserAPI contains experimental APIs that may change or be removed. type ServerUserAPI serverAPI +// Experimental: ServerUserSettingsAPI contains experimental APIs that may change or be +// removed. type ServerUserSettingsAPI serverAPI +// Get lists every known user setting (settings.json overlaid with the legacy config.json, +// config.json wins), each with its effective value, its default, and whether it is at the +// default — so settings the user has never set still appear with their default value. Does +// not include repository- or enterprise-managed overrides that the runtime layers on top at +// session time. +// +// RPC method: user.settings.get. +// +// Returns: Per-key metadata for every known user setting (settings.json overlaid with the +// legacy config.json, config.json wins), including settings left at their default. Excludes +// repository- and enterprise-managed overrides. +func (a *ServerUserSettingsAPI) Get(ctx context.Context) (*UserSettingsGetResult, error) { + raw, err := a.client.Request(ctx, "user.settings.get", nil) + if err != nil { + return nil, err + } + var result UserSettingsGetResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // Reload drops this runtime process's in-memory user settings cache so the next settings // read observes disk. // @@ -12839,6 +13145,30 @@ func (a *ServerUserSettingsAPI) Reload(ctx context.Context) (*UserSettingsReload return &result, nil } +// Set writes one or more user settings to settings.json, replacing each provided top-level +// key. A key whose value is null is removed. Returns the keys whose new value is shadowed +// by a legacy config.json entry (config.json wins on read), which the runtime leaves in +// place — such writes do not take effect until the legacy value is removed. +// +// RPC method: user.settings.set. +// +// Parameters: Partial user settings to write to settings.json. Each top-level key is +// written individually, replacing the existing value; a key whose value is null is removed. +// +// Returns: Outcome of writing user settings. +func (a *ServerUserSettingsAPI) Set(ctx context.Context, params *UserSettingsSetRequest) (*UserSettingsSetResult, error) { + raw, err := a.client.Request(ctx, "user.settings.set", params) + if err != nil { + return nil, err + } + var result UserSettingsSetResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Experimental: Settings returns experimental APIs that may change or be removed. func (s *ServerUserAPI) Settings() *ServerUserSettingsAPI { return (*ServerUserSettingsAPI)(s) } @@ -12873,6 +13203,7 @@ type ServerRPC struct { // // Returns: Server liveness response, including the echoed message, current server // timestamp, and protocol version. +// Experimental: Ping is an experimental API and may change or be removed in future versions. func (a *ServerRPC) Ping(ctx context.Context, params *PingRequest) (*PingResult, error) { raw, err := a.common.client.Request(ctx, "ping", params) if err != nil { @@ -13092,6 +13423,8 @@ type InternalServerRPC struct { // // Returns: Handshake result reporting the server's protocol version and package version on // success. +// Experimental: Connect is an experimental API and may change or be removed in future +// versions. // Internal: Connect is part of the SDK's internal handshake/plumbing; external callers // should not use it. func (a *InternalServerRPC) Connect(ctx context.Context, params *ConnectRequest) (*ConnectResult, error) { @@ -15000,8 +15333,8 @@ func (a *OptionsAPI) Update(ctx context.Context, params *SessionUpdateOptionsPar if params.ReasoningSummary != nil { req["reasoningSummary"] = *params.ReasoningSummary } - if params.ResponseBudget != nil { - req["responseBudget"] = *params.ResponseBudget + if params.ResponseLimits != nil { + req["responseLimits"] = *params.ResponseLimits } if params.RunningInInteractiveMode != nil { req["runningInInteractiveMode"] = *params.RunningInInteractiveMode @@ -16898,6 +17231,55 @@ func (a *UsageAPI) GetMetrics(ctx context.Context) (*UsageGetMetricsResult, erro return &result, nil } +// Experimental: VisibilityAPI contains experimental APIs that may change or be removed. +type VisibilityAPI sessionAPI + +// Get returns the session's current Mission Control sharing status and shareable GitHub +// URL. Reflects whether the synced session is visible to repository readers ("repo") or +// restricted to its creator and collaborators ("unshared"). +// +// RPC method: session.visibility.get. +// +// Returns: Current sharing status and shareable GitHub URL for a session. +func (a *VisibilityAPI) Get(ctx context.Context) (*VisibilityGetResult, error) { + req := map[string]any{"sessionId": a.sessionID} + raw, err := a.client.Request(ctx, "session.visibility.get", req) + if err != nil { + return nil, err + } + var result VisibilityGetResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Sets the session's Mission Control sharing status, controlling whether the synced session +// is visible to repository readers. Returns the effective status and shareable GitHub URL +// after the change. +// +// RPC method: session.visibility.set. +// +// Parameters: Desired sharing status for the session. +// +// Returns: Effective sharing status and shareable GitHub URL after updating session +// visibility. +func (a *VisibilityAPI) Set(ctx context.Context, params *VisibilitySetRequest) (*VisibilitySetResult, error) { + req := map[string]any{"sessionId": a.sessionID} + if params != nil { + req["status"] = params.Status + } + raw, err := a.client.Request(ctx, "session.visibility.set", req) + if err != nil { + return nil, err + } + var result VisibilitySetResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // Experimental: WorkspacesAPI contains experimental APIs that may change or be removed. type WorkspacesAPI sessionAPI @@ -17110,6 +17492,7 @@ type SessionRPC struct { Tools *ToolsAPI UI *UIAPI Usage *UsageAPI + Visibility *VisibilityAPI Workspaces *WorkspacesAPI } @@ -17321,6 +17704,7 @@ func NewSessionRPC(client *jsonrpc2.Client, sessionID string) *SessionRPC { r.Tools = (*ToolsAPI)(&r.common) r.UI = (*UIAPI)(&r.common) r.Usage = (*UsageAPI)(&r.common) + r.Visibility = (*VisibilityAPI)(&r.common) r.Workspaces = (*WorkspacesAPI)(&r.common) return r } diff --git a/go/rpc/zrpc_encoding.go b/go/rpc/zrpc_encoding.go index 8b9e492702..954b288459 100644 --- a/go/rpc/zrpc_encoding.go +++ b/go/rpc/zrpc_encoding.go @@ -738,6 +738,12 @@ func unmarshalExternalToolTextResultForLlmContent(data []byte) (ExternalToolText return nil, err } return &d, nil + case ExternalToolTextResultForLlmContentTypeShellExit: + var d ExternalToolTextResultForLlmContentShellExit + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil case ExternalToolTextResultForLlmContentTypeTerminal: var d ExternalToolTextResultForLlmContentTerminal if err := json.Unmarshal(data, &d); err != nil { @@ -884,6 +890,17 @@ func (r ExternalToolTextResultForLlmContentResourceLink) MarshalJSON() ([]byte, }) } +func (r ExternalToolTextResultForLlmContentShellExit) MarshalJSON() ([]byte, error) { + type alias ExternalToolTextResultForLlmContentShellExit + return json.Marshal(struct { + Type ExternalToolTextResultForLlmContentType `json:"type"` + alias + }{ + Type: r.Type(), + alias: alias(r), + }) +} + func (r ExternalToolTextResultForLlmContentTerminal) MarshalJSON() ([]byte, error) { type alias ExternalToolTextResultForLlmContentTerminal return json.Marshal(struct { @@ -3251,7 +3268,7 @@ func (r *SessionOpenOptions) UnmarshalJSON(data []byte) error { RemoteDefaultedOn *bool `json:"remoteDefaultedOn,omitempty"` RemoteExporting *bool `json:"remoteExporting,omitempty"` RemoteSteerable *bool `json:"remoteSteerable,omitempty"` - ResponseBudget *ResponseBudgetConfig `json:"responseBudget,omitempty"` + ResponseLimits *ResponseLimitsConfig `json:"responseLimits,omitempty"` RunningInInteractiveMode *bool `json:"runningInInteractiveMode,omitempty"` SandboxConfig *SandboxConfig `json:"sandboxConfig,omitempty"` SessionCapabilities []SessionCapability `json:"sessionCapabilities,omitzero"` @@ -3319,7 +3336,7 @@ func (r *SessionOpenOptions) UnmarshalJSON(data []byte) error { r.RemoteDefaultedOn = raw.RemoteDefaultedOn r.RemoteExporting = raw.RemoteExporting r.RemoteSteerable = raw.RemoteSteerable - r.ResponseBudget = raw.ResponseBudget + r.ResponseLimits = raw.ResponseLimits r.RunningInInteractiveMode = raw.RunningInInteractiveMode r.SandboxConfig = raw.SandboxConfig r.SessionCapabilities = raw.SessionCapabilities diff --git a/go/rpc/zsession_encoding.go b/go/rpc/zsession_encoding.go index f26dabc269..2768730ac3 100644 --- a/go/rpc/zsession_encoding.go +++ b/go/rpc/zsession_encoding.go @@ -431,6 +431,12 @@ func (e *SessionEvent) UnmarshalJSON(data []byte) error { return err } e.Data = &d + case SessionEventTypeSessionResponseLimitsChanged: + var d SessionResponseLimitsChangedData + if err := json.Unmarshal(raw.Data, &d); err != nil { + return err + } + e.Data = &d case SessionEventTypeSessionResume: var d SessionResumeData if err := json.Unmarshal(raw.Data, &d); err != nil { @@ -1014,6 +1020,12 @@ func unmarshalToolExecutionCompleteContent(data []byte) (ToolExecutionCompleteCo return nil, err } return &d, nil + case ToolExecutionCompleteContentTypeShellExit: + var d ToolExecutionCompleteContentShellExit + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil case ToolExecutionCompleteContentTypeTerminal: var d ToolExecutionCompleteContentTerminal if err := json.Unmarshal(data, &d); err != nil { @@ -1148,6 +1160,17 @@ func (r ToolExecutionCompleteContentResourceLink) MarshalJSON() ([]byte, error) }) } +func (r ToolExecutionCompleteContentShellExit) MarshalJSON() ([]byte, error) { + type alias ToolExecutionCompleteContentShellExit + return json.Marshal(struct { + Type ToolExecutionCompleteContentType `json:"type"` + alias + }{ + Type: r.Type(), + alias: alias(r), + }) +} + func (r ToolExecutionCompleteContentTerminal) MarshalJSON() ([]byte, error) { type alias ToolExecutionCompleteContentTerminal return json.Marshal(struct { diff --git a/go/rpc/zsession_events.go b/go/rpc/zsession_events.go index df0aa5beea..05b1846405 100644 --- a/go/rpc/zsession_events.go +++ b/go/rpc/zsession_events.go @@ -133,6 +133,7 @@ const ( SessionEventTypeSessionPermissionsChanged SessionEventType = "session.permissions_changed" SessionEventTypeSessionPlanChanged SessionEventType = "session.plan_changed" SessionEventTypeSessionRemoteSteerableChanged SessionEventType = "session.remote_steerable_changed" + SessionEventTypeSessionResponseLimitsChanged SessionEventType = "session.response_limits_changed" SessionEventTypeSessionResume SessionEventType = "session.resume" SessionEventTypeSessionScheduleCancelled SessionEventType = "session.schedule_cancelled" SessionEventTypeSessionScheduleCreated SessionEventType = "session.schedule_created" @@ -992,6 +993,17 @@ type CommandExecuteData struct { func (*CommandExecuteData) sessionEventData() {} func (*CommandExecuteData) Type() SessionEventType { return SessionEventTypeCommandExecute } +// Response limits update details. Null clears the limits. +type SessionResponseLimitsChangedData struct { + // Current response limits for the session, or null when no limits are active + ResponseLimits *ResponseLimitsConfig `json:"responseLimits"` +} + +func (*SessionResponseLimitsChangedData) sessionEventData() {} +func (*SessionResponseLimitsChangedData) Type() SessionEventType { + return SessionEventTypeSessionResponseLimitsChanged +} + // SDK command registration change notification type CommandsChangedData struct { // Current list of registered SDK commands @@ -1293,8 +1305,8 @@ type SessionStartData struct { ReasoningSummary *ReasoningSummary `json:"reasoningSummary,omitempty"` // Whether this session supports remote steering via GitHub RemoteSteerable *bool `json:"remoteSteerable,omitempty"` - // Response budget limits configured at session creation time, if any - ResponseBudget *ResponseBudgetConfig `json:"responseBudget,omitempty"` + // Response limits configured at session creation time, if any + ResponseLimits *ResponseLimitsConfig `json:"responseLimits,omitempty"` // Model selected at session creation time, if any SelectedModel *string `json:"selectedModel,omitempty"` // Unique identifier for the session @@ -1328,8 +1340,8 @@ type SessionResumeData struct { ReasoningSummary *ReasoningSummary `json:"reasoningSummary,omitempty"` // Whether this session supports remote steering via GitHub RemoteSteerable *bool `json:"remoteSteerable,omitempty"` - // Response budget limits currently configured at resume time; null when no budget is active - ResponseBudget *ResponseBudgetConfig `json:"responseBudget,omitempty"` + // Response limits currently configured at resume time; null when no limits are active + ResponseLimits *ResponseLimitsConfig `json:"responseLimits,omitempty"` // ISO 8601 timestamp when the session was resumed ResumeTime time.Time `json:"resumeTime"` // Model currently selected at resume time @@ -3156,7 +3168,26 @@ func (ToolExecutionCompleteContentResourceLink) Type() ToolExecutionCompleteCont return ToolExecutionCompleteContentTypeResourceLink } -// Terminal/shell output content block with optional exit code and working directory +// Shell command exit metadata with optional output preview +type ToolExecutionCompleteContentShellExit struct { + // Working directory where the shell command was executed + Cwd *string `json:"cwd,omitempty"` + // Exit code from the completed shell command + ExitCode int64 `json:"exitCode"` + // Output associated with this shell command, if available. May be partial, truncated, or a preview; not guaranteed to be full output. + OutputPreview *string `json:"outputPreview,omitempty"` + // Whether outputPreview is known to be incomplete or truncated + OutputTruncated *bool `json:"outputTruncated,omitempty"` + // Shell id, as assigned by Copilot runtime + ShellID string `json:"shellId"` +} + +func (ToolExecutionCompleteContentShellExit) toolExecutionCompleteContent() {} +func (ToolExecutionCompleteContentShellExit) Type() ToolExecutionCompleteContentType { + return ToolExecutionCompleteContentTypeShellExit +} + +// Deprecated for shell command exit metadata. Use ToolExecutionCompleteContentShellExit instead. type ToolExecutionCompleteContentTerminal struct { // Working directory where the command was executed Cwd *string `json:"cwd,omitempty"` @@ -3830,6 +3861,7 @@ const ( ToolExecutionCompleteContentTypeImage ToolExecutionCompleteContentType = "image" ToolExecutionCompleteContentTypeResource ToolExecutionCompleteContentType = "resource" ToolExecutionCompleteContentTypeResourceLink ToolExecutionCompleteContentType = "resource_link" + ToolExecutionCompleteContentTypeShellExit ToolExecutionCompleteContentType = "shell_exit" ToolExecutionCompleteContentTypeTerminal ToolExecutionCompleteContentType = "terminal" ToolExecutionCompleteContentTypeText ToolExecutionCompleteContentType = "text" ) diff --git a/go/zsession_events.go b/go/zsession_events.go index 6b9f17aa63..86aeab5bab 100644 --- a/go/zsession_events.go +++ b/go/zsession_events.go @@ -192,7 +192,7 @@ type ( RawSystemNotification = rpc.RawSystemNotification RawToolExecutionCompleteContent = rpc.RawToolExecutionCompleteContent ReasoningSummary = rpc.ReasoningSummary - ResponseBudgetConfig = rpc.ResponseBudgetConfig + ResponseLimitsConfig = rpc.ResponseLimitsConfig SamplingCompletedData = rpc.SamplingCompletedData SamplingRequestedData = rpc.SamplingRequestedData SessionAutopilotObjectiveChangedData = rpc.SessionAutopilotObjectiveChangedData @@ -226,6 +226,7 @@ type ( SessionPermissionsChangedData = rpc.SessionPermissionsChangedData SessionPlanChangedData = rpc.SessionPlanChangedData SessionRemoteSteerableChangedData = rpc.SessionRemoteSteerableChangedData + SessionResponseLimitsChangedData = rpc.SessionResponseLimitsChangedData SessionResumeData = rpc.SessionResumeData SessionScheduleCancelledData = rpc.SessionScheduleCancelledData SessionScheduleCreatedData = rpc.SessionScheduleCreatedData @@ -279,6 +280,7 @@ type ( ToolExecutionCompleteContentResourceLink = rpc.ToolExecutionCompleteContentResourceLink ToolExecutionCompleteContentResourceLinkIcon = rpc.ToolExecutionCompleteContentResourceLinkIcon ToolExecutionCompleteContentResourceLinkIconTheme = rpc.ToolExecutionCompleteContentResourceLinkIconTheme + ToolExecutionCompleteContentShellExit = rpc.ToolExecutionCompleteContentShellExit ToolExecutionCompleteContentTerminal = rpc.ToolExecutionCompleteContentTerminal ToolExecutionCompleteContentText = rpc.ToolExecutionCompleteContentText ToolExecutionCompleteContentType = rpc.ToolExecutionCompleteContentType @@ -547,6 +549,7 @@ const ( SessionEventTypeSessionPermissionsChanged = rpc.SessionEventTypeSessionPermissionsChanged SessionEventTypeSessionPlanChanged = rpc.SessionEventTypeSessionPlanChanged SessionEventTypeSessionRemoteSteerableChanged = rpc.SessionEventTypeSessionRemoteSteerableChanged + SessionEventTypeSessionResponseLimitsChanged = rpc.SessionEventTypeSessionResponseLimitsChanged SessionEventTypeSessionResume = rpc.SessionEventTypeSessionResume SessionEventTypeSessionScheduleCancelled = rpc.SessionEventTypeSessionScheduleCancelled SessionEventTypeSessionScheduleCreated = rpc.SessionEventTypeSessionScheduleCreated @@ -610,6 +613,7 @@ const ( ToolExecutionCompleteContentTypeImage = rpc.ToolExecutionCompleteContentTypeImage ToolExecutionCompleteContentTypeResource = rpc.ToolExecutionCompleteContentTypeResource ToolExecutionCompleteContentTypeResourceLink = rpc.ToolExecutionCompleteContentTypeResourceLink + ToolExecutionCompleteContentTypeShellExit = rpc.ToolExecutionCompleteContentTypeShellExit ToolExecutionCompleteContentTypeTerminal = rpc.ToolExecutionCompleteContentTypeTerminal ToolExecutionCompleteContentTypeText = rpc.ToolExecutionCompleteContentTypeText ToolExecutionCompleteToolDescriptionMetaUIVisibilityApp = rpc.ToolExecutionCompleteToolDescriptionMetaUIVisibilityApp diff --git a/java/pom.xml b/java/pom.xml index dfb779acf3..da3591d18d 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -86,7 +86,7 @@ DO NOT EDIT MANUALLY. Updated by the update-copilot-dependency workflow. --> - ^1.0.66-1 + ^1.0.66-2 diff --git a/java/scripts/codegen/package-lock.json b/java/scripts/codegen/package-lock.json index 3befb50acf..a604992bbb 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.66-1", + "@github/copilot": "^1.0.66-2", "json-schema": "^0.4.0", "tsx": "^4.22.4" } @@ -428,9 +428,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.66-1", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.66-1.tgz", - "integrity": "sha512-Cf0rTsG1wfdRzGmD9PC0TPYxQojItwo6Hv/Jp6GwakrBswLn4PlxW/pCQA7n3o2DahTQDX2y6Z9olAdx0dHFQA==", + "version": "1.0.66-2", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.66-2.tgz", + "integrity": "sha512-nAhhtfjpryklyombieuu18NK2g+BmEk4/8qvXVj8k+w/63tiVpLxFh865Vf6NQiVh/S7hbjMghTbrptsspYg2w==", "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.66-1", - "@github/copilot-darwin-x64": "1.0.66-1", - "@github/copilot-linux-arm64": "1.0.66-1", - "@github/copilot-linux-x64": "1.0.66-1", - "@github/copilot-linuxmusl-arm64": "1.0.66-1", - "@github/copilot-linuxmusl-x64": "1.0.66-1", - "@github/copilot-win32-arm64": "1.0.66-1", - "@github/copilot-win32-x64": "1.0.66-1" + "@github/copilot-darwin-arm64": "1.0.66-2", + "@github/copilot-darwin-x64": "1.0.66-2", + "@github/copilot-linux-arm64": "1.0.66-2", + "@github/copilot-linux-x64": "1.0.66-2", + "@github/copilot-linuxmusl-arm64": "1.0.66-2", + "@github/copilot-linuxmusl-x64": "1.0.66-2", + "@github/copilot-win32-arm64": "1.0.66-2", + "@github/copilot-win32-x64": "1.0.66-2" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.66-1", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.66-1.tgz", - "integrity": "sha512-HTum+52pVBlrUrUjn/r/Q6kd2c0pvGsi6NyfuaGLRKStSQj00Iz5urYlo0hcq5JKF9eGB7ow+aeYc7BDIUVnhw==", + "version": "1.0.66-2", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.66-2.tgz", + "integrity": "sha512-gjLRtAQOdFQUOTm7nYi+zufkGxMlQlTzUyncQ3W4u1+WdGQbx5fWqMg/yd+j1yMN9PEETyF/ZHZqAaFWkEpQww==", "cpu": [ "arm64" ], @@ -466,9 +466,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.66-1", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.66-1.tgz", - "integrity": "sha512-gniq5/n2nX8cBQncjwvU7nAGYj21ALSknNUqhPWIQYwx+IM6KnGeBgSpldubJCMDjkZkbPYqskVcxTGvw0GGHA==", + "version": "1.0.66-2", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.66-2.tgz", + "integrity": "sha512-wWWBsVwJtRTXqCK8lVpzwbJd3Tm1F23avf942K+PmsGYiZZYNcS5pt4umQRRj0sHKgO/muuA4eg/tMfGNi5fgA==", "cpu": [ "x64" ], @@ -482,9 +482,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.66-1", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.66-1.tgz", - "integrity": "sha512-PG/xIIndXo0NpKYXR8GYPXAA3p/kuf4lsA898Pq+9UH5wU9ybqo5P/n5HBLXNOQnpP8+u9pjL9rPbvtwxMkzaQ==", + "version": "1.0.66-2", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.66-2.tgz", + "integrity": "sha512-j0hjx77JNFR3ZS8z3flY2j5SfGZMfKigYVFpDlTJM8FhfkMCUJ5IUhsZwSTimhHlxrsXuI31S6g0WsZLmBUe6A==", "cpu": [ "arm64" ], @@ -498,9 +498,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.66-1", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.66-1.tgz", - "integrity": "sha512-Tb11uVan2f8YjFLiTvPUC8yLSYdmoMru9J8axZRuiSgOtRfmaJGxHoM/axPYW+874YAn4gSygs7OPUt1C+67Xw==", + "version": "1.0.66-2", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.66-2.tgz", + "integrity": "sha512-vWaNbh4WdwkiI40Thcfbwi8tZFKo06r+Dm9Zfb8uY4wAz3X5PaGeSq+8XrNoV3uaRWltI0ncSIrq5tSOyDtRPg==", "cpu": [ "x64" ], @@ -514,9 +514,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.66-1", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.66-1.tgz", - "integrity": "sha512-GJEVj60B5MeJ8kfnf/dRmyX4EwU4HWL7yUZkrAG6xznSyHHPoTWtZ/tudQX/mf69emXtO7Nt9cLOcNIEdYRPMg==", + "version": "1.0.66-2", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.66-2.tgz", + "integrity": "sha512-LbWy5NlWasBeV/i+Xol+8dW7kbAQr6MF46apbseRNHYkhwyF/417WtLfirP8O2hPuqyU72q/HAQziFXkz14pIQ==", "cpu": [ "arm64" ], @@ -530,9 +530,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.66-1", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.66-1.tgz", - "integrity": "sha512-I5k9mMRuIO+pmPGDiblFXd+HOBJo92XEIBwbZMaAW3qRuyF5UcEFuWlczOCYzcTreXfBqNkG1P9qsBeDDNXfnQ==", + "version": "1.0.66-2", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.66-2.tgz", + "integrity": "sha512-djOu52fGIU7eUhQdUS0K5xB2eFdi8LTTbxvphHWlrN1AD1BdZ+VX9Pk2avt6yCfW+Hh0loh2pNsCbTfNyxvULA==", "cpu": [ "x64" ], @@ -546,9 +546,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.66-1", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.66-1.tgz", - "integrity": "sha512-tUkNUkx5F2TIefY3KDORon3THo256hr/ZVUMEph5fr6xSib4d8gGgNjzok/4kEfIR3a7L/45g0Qi+CzQNtjSdA==", + "version": "1.0.66-2", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.66-2.tgz", + "integrity": "sha512-YQu01atiwoz8XfrHKqvI1xNjnc2IIIxgJDkQ6PxwrWPZ4IO320izwlXbW2ZaOz9yDgjWNis6EJ4Ryz8K+mM6kg==", "cpu": [ "arm64" ], @@ -562,9 +562,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.66-1", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.66-1.tgz", - "integrity": "sha512-ktTbksWav2WSVi8BbTYxD4CJ+OrPximk5zPWff3stsU1MrG0XjZtlML1KUY3d/rrq2lpfZqh0ooF+A4bt8IFsQ==", + "version": "1.0.66-2", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.66-2.tgz", + "integrity": "sha512-4/kTs+lKc67f7KEAQ+Gt3sEBFDSEGoUxJujddV/+fS8EAg9uF2g6e3NzS1I4+htyRM4Oq/Z6xfWjGUgQsi9rfw==", "cpu": [ "x64" ], diff --git a/java/scripts/codegen/package.json b/java/scripts/codegen/package.json index 91442b63c6..a83a2d46c0 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.66-1", + "@github/copilot": "^1.0.66-2", "json-schema": "^0.4.0", "tsx": "^4.22.4" } diff --git a/java/src/generated/java/com/github/copilot/generated/ResponseBudgetConfig.java b/java/src/generated/java/com/github/copilot/generated/ResponseLimitsConfig.java similarity index 79% rename from java/src/generated/java/com/github/copilot/generated/ResponseBudgetConfig.java rename to java/src/generated/java/com/github/copilot/generated/ResponseLimitsConfig.java index 0acfdbdb3d..0b12b8ba47 100644 --- a/java/src/generated/java/com/github/copilot/generated/ResponseBudgetConfig.java +++ b/java/src/generated/java/com/github/copilot/generated/ResponseLimitsConfig.java @@ -13,16 +13,14 @@ import javax.annotation.processing.Generated; /** - * Optional response budget limits. + * Optional response limits. * * @since 1.0.0 */ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record ResponseBudgetConfig( - /** Maximum model-call iterations allowed while responding to one top-level user message. */ - @JsonProperty("maxModelIterations") Long maxModelIterations, +public record ResponseLimitsConfig( /** Maximum AI Credits allowed while responding to one top-level user message. */ @JsonProperty("maxAiCredits") Double maxAiCredits ) { diff --git a/java/src/generated/java/com/github/copilot/generated/SessionEvent.java b/java/src/generated/java/com/github/copilot/generated/SessionEvent.java index bcbb09831c..f648828327 100644 --- a/java/src/generated/java/com/github/copilot/generated/SessionEvent.java +++ b/java/src/generated/java/com/github/copilot/generated/SessionEvent.java @@ -39,6 +39,7 @@ @JsonSubTypes.Type(value = SessionWarningEvent.class, name = "session.warning"), @JsonSubTypes.Type(value = SessionModelChangeEvent.class, name = "session.model_change"), @JsonSubTypes.Type(value = SessionModeChangedEvent.class, name = "session.mode_changed"), + @JsonSubTypes.Type(value = SessionResponseLimitsChangedEvent.class, name = "session.response_limits_changed"), @JsonSubTypes.Type(value = SessionPermissionsChangedEvent.class, name = "session.permissions_changed"), @JsonSubTypes.Type(value = SessionPlanChangedEvent.class, name = "session.plan_changed"), @JsonSubTypes.Type(value = SessionTodosChangedEvent.class, name = "session.todos_changed"), @@ -140,6 +141,7 @@ public abstract sealed class SessionEvent permits SessionWarningEvent, SessionModelChangeEvent, SessionModeChangedEvent, + SessionResponseLimitsChangedEvent, SessionPermissionsChangedEvent, SessionPlanChangedEvent, SessionTodosChangedEvent, diff --git a/java/src/generated/java/com/github/copilot/generated/SessionResponseLimitsChangedEvent.java b/java/src/generated/java/com/github/copilot/generated/SessionResponseLimitsChangedEvent.java new file mode 100644 index 0000000000..170e26d4be --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/SessionResponseLimitsChangedEvent.java @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Session event "session.response_limits_changed". Response limits update details. Null clears the limits. + * @since 1.0.0 + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class SessionResponseLimitsChangedEvent extends SessionEvent { + + @Override + public String getType() { return "session.response_limits_changed"; } + + @JsonProperty("data") + private SessionResponseLimitsChangedEventData data; + + public SessionResponseLimitsChangedEventData getData() { return data; } + public void setData(SessionResponseLimitsChangedEventData data) { this.data = data; } + + /** Data payload for {@link SessionResponseLimitsChangedEvent}. */ + @JsonIgnoreProperties(ignoreUnknown = true) + @JsonInclude(JsonInclude.Include.NON_NULL) + public record SessionResponseLimitsChangedEventData( + /** Current response limits for the session, or null when no limits are active */ + @JsonProperty("responseLimits") ResponseLimitsConfig responseLimits + ) { + } +} diff --git a/java/src/generated/java/com/github/copilot/generated/SessionResumeEvent.java b/java/src/generated/java/com/github/copilot/generated/SessionResumeEvent.java index 47e8b3a997..a4282d16ee 100644 --- a/java/src/generated/java/com/github/copilot/generated/SessionResumeEvent.java +++ b/java/src/generated/java/com/github/copilot/generated/SessionResumeEvent.java @@ -49,8 +49,8 @@ public record SessionResumeEventData( @JsonProperty("reasoningSummary") ReasoningSummary reasoningSummary, /** Context tier currently selected at resume time; null when no tier is active */ @JsonProperty("contextTier") ContextTier contextTier, - /** Response budget limits currently configured at resume time; null when no budget is active */ - @JsonProperty("responseBudget") ResponseBudgetConfig responseBudget, + /** Response limits currently configured at resume time; null when no limits are active */ + @JsonProperty("responseLimits") ResponseLimitsConfig responseLimits, /** Updated working directory and git context at resume time */ @JsonProperty("context") WorkingDirectoryContext context, /** Whether the session was already in use by another client at resume time */ diff --git a/java/src/generated/java/com/github/copilot/generated/SessionStartEvent.java b/java/src/generated/java/com/github/copilot/generated/SessionStartEvent.java index 3cd6fb9664..4823fcff52 100644 --- a/java/src/generated/java/com/github/copilot/generated/SessionStartEvent.java +++ b/java/src/generated/java/com/github/copilot/generated/SessionStartEvent.java @@ -53,8 +53,8 @@ public record SessionStartEventData( @JsonProperty("reasoningSummary") ReasoningSummary reasoningSummary, /** Context tier selected at session creation time for models with tiered context pricing; null when no tier is selected (e.g., non-tiered model) */ @JsonProperty("contextTier") ContextTier contextTier, - /** Response budget limits configured at session creation time, if any */ - @JsonProperty("responseBudget") ResponseBudgetConfig responseBudget, + /** Response limits configured at session creation time, if any */ + @JsonProperty("responseLimits") ResponseLimitsConfig responseLimits, /** Working directory and git context at session start */ @JsonProperty("context") WorkingDirectoryContext context, /** Whether the session was already in use by another client at start time */ diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AccountGetCurrentAuthResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/AccountGetCurrentAuthResult.java index 9cbcd4906d..eb577fcc25 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/AccountGetCurrentAuthResult.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/AccountGetCurrentAuthResult.java @@ -10,13 +10,17 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; import java.util.List; import javax.annotation.processing.Generated; /** * Current authentication state + * + * @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) diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AccountGetQuotaResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/AccountGetQuotaResult.java index 3685fbb8ad..6e5929dfd5 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/AccountGetQuotaResult.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/AccountGetQuotaResult.java @@ -10,13 +10,17 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; import java.util.Map; import javax.annotation.processing.Generated; /** * Quota usage snapshots for the resolved user, keyed by quota type. + * + * @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) diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AccountLoginParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/AccountLoginParams.java index 6dd4266b29..bd8e697341 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/AccountLoginParams.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/AccountLoginParams.java @@ -10,12 +10,16 @@ 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; /** * Credentials to store after successful authentication + * + * @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) diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AccountLoginResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/AccountLoginResult.java index 33d65f7dc0..1119835575 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/AccountLoginResult.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/AccountLoginResult.java @@ -10,12 +10,16 @@ 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; /** * Result of a successful login; throws on failure + * + * @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) diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AccountLogoutParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/AccountLogoutParams.java index 0d15fd1231..b5e93e1859 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/AccountLogoutParams.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/AccountLogoutParams.java @@ -10,12 +10,16 @@ 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; /** * User to log out + * + * @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) diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AccountLogoutResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/AccountLogoutResult.java index c26df8524b..296227e5b6 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/AccountLogoutResult.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/AccountLogoutResult.java @@ -10,12 +10,16 @@ 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; /** * Logout result indicating if more users remain + * + * @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) diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ConnectParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/ConnectParams.java index aae84cad39..5fa87b3af6 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/ConnectParams.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ConnectParams.java @@ -10,12 +10,16 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; import javax.annotation.processing.Generated; /** * Optional connection token presented by the SDK client during the handshake. + * + * @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) diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ConnectResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/ConnectResult.java index d7184004c0..8c12b57a80 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/ConnectResult.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ConnectResult.java @@ -10,12 +10,16 @@ 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; /** * Handshake result reporting the server's protocol version and package version on success. + * + * @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) diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigAddParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigAddParams.java index 47ce176796..4c8baff2c4 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigAddParams.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigAddParams.java @@ -10,12 +10,16 @@ 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; /** * MCP server name and configuration to add to user configuration. + * + * @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) diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigDisableParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigDisableParams.java index c5f743a8eb..81fab3e4c3 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigDisableParams.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigDisableParams.java @@ -10,13 +10,17 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; import java.util.List; import javax.annotation.processing.Generated; /** * MCP server names to disable for new sessions. + * + * @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) diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigEnableParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigEnableParams.java index ae845ecf23..57e882acb2 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigEnableParams.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigEnableParams.java @@ -10,13 +10,17 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; import java.util.List; import javax.annotation.processing.Generated; /** * MCP server names to enable for new sessions. + * + * @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) diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigListResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigListResult.java index 048496806f..8100818615 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigListResult.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigListResult.java @@ -10,13 +10,17 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; import java.util.Map; import javax.annotation.processing.Generated; /** * User-configured MCP servers, keyed by server name. + * + * @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) diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigRemoveParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigRemoveParams.java index bd646cacaa..81a0aa0e21 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigRemoveParams.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigRemoveParams.java @@ -10,12 +10,16 @@ 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; /** * MCP server name to remove from user configuration. + * + * @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) diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigUpdateParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigUpdateParams.java index b3134cfbf4..082d98318e 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigUpdateParams.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigUpdateParams.java @@ -10,12 +10,16 @@ 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; /** * MCP server name and replacement configuration to write to user configuration. + * + * @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) diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpDiscoverParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/McpDiscoverParams.java index 8c44ab7d29..a9e029da35 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/McpDiscoverParams.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/McpDiscoverParams.java @@ -10,12 +10,16 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; import javax.annotation.processing.Generated; /** * Optional working directory used as context for MCP server discovery. + * + * @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) diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpDiscoverResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/McpDiscoverResult.java index ed75dd92d3..e5131e36d6 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/McpDiscoverResult.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/McpDiscoverResult.java @@ -10,13 +10,17 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; import java.util.List; import javax.annotation.processing.Generated; /** * MCP servers discovered from user, workspace, plugin, and built-in sources. + * + * @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) diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ModelsListResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/ModelsListResult.java index 6c10eeeba4..5a88a01db2 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/ModelsListResult.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ModelsListResult.java @@ -10,13 +10,17 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; import java.util.List; import javax.annotation.processing.Generated; /** * List of Copilot models available to the resolved user, including capabilities and billing metadata. + * + * @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) diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PingParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/PingParams.java index 7eb9e06232..841688e1bd 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/PingParams.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/PingParams.java @@ -10,12 +10,16 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; import javax.annotation.processing.Generated; /** * Optional message to echo back to the caller. + * + * @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) diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PingResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/PingResult.java index 021ebed853..3199f706d9 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/PingResult.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/PingResult.java @@ -10,13 +10,17 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; import java.time.OffsetDateTime; import javax.annotation.processing.Generated; /** * Server liveness response, including the echoed message, current server timestamp, and protocol version. + * + * @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) diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ResponseBudgetConfig.java b/java/src/generated/java/com/github/copilot/generated/rpc/ResponseLimitsConfig.java similarity index 79% rename from java/src/generated/java/com/github/copilot/generated/rpc/ResponseBudgetConfig.java rename to java/src/generated/java/com/github/copilot/generated/rpc/ResponseLimitsConfig.java index b83639c09a..ddb6a68a38 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/ResponseBudgetConfig.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ResponseLimitsConfig.java @@ -13,16 +13,14 @@ import javax.annotation.processing.Generated; /** - * Optional response budget limits. + * Optional response limits. * * @since 1.0.0 */ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record ResponseBudgetConfig( - /** Maximum model-call iterations allowed while responding to one top-level user message. */ - @JsonProperty("maxModelIterations") Long maxModelIterations, +public record ResponseLimitsConfig( /** Maximum AI Credits allowed while responding to one top-level user message. */ @JsonProperty("maxAiCredits") Double maxAiCredits ) { diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SecretsAddFilterValuesParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/SecretsAddFilterValuesParams.java index d0f5003f9c..6616a3d6df 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SecretsAddFilterValuesParams.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SecretsAddFilterValuesParams.java @@ -10,13 +10,17 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; import java.util.List; import javax.annotation.processing.Generated; /** * Secret values to add to the redaction filter. + * + * @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) diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SecretsAddFilterValuesResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/SecretsAddFilterValuesResult.java index 61eb3fb63c..b2e3251c74 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SecretsAddFilterValuesResult.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SecretsAddFilterValuesResult.java @@ -10,12 +10,16 @@ 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; /** * Confirmation that the secret values were registered. + * + * @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) diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerAccountApi.java b/java/src/generated/java/com/github/copilot/generated/rpc/ServerAccountApi.java index b750596b55..4b7cf718ee 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/ServerAccountApi.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ServerAccountApi.java @@ -7,6 +7,7 @@ package com.github.copilot.generated.rpc; +import com.github.copilot.CopilotExperimental; import java.util.List; import java.util.concurrent.CompletableFuture; import javax.annotation.processing.Generated; @@ -28,40 +29,55 @@ public final class ServerAccountApi { /** * Optional GitHub token used to look up quota for a specific user instead of the global auth context. + * + * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ + @CopilotExperimental public CompletableFuture getQuota() { return caller.invoke("account.getQuota", java.util.Map.of(), AccountGetQuotaResult.class); } /** * Current authentication state + * + * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ + @CopilotExperimental public CompletableFuture getCurrentAuth() { return caller.invoke("account.getCurrentAuth", java.util.Map.of(), AccountGetCurrentAuthResult.class); } /** * List of all authenticated users + * + * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ + @CopilotExperimental public CompletableFuture> getAllUsers() { return caller.invoke("account.getAllUsers", java.util.Map.of(), RpcMapper.INSTANCE.getTypeFactory().constructCollectionType(List.class, AccountAllUsers.class)); } /** * Credentials to store after successful authentication + * + * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ + @CopilotExperimental public CompletableFuture login(AccountLoginParams params) { return caller.invoke("account.login", params, AccountLoginResult.class); } /** * User to log out + * + * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ + @CopilotExperimental public CompletableFuture logout(AccountLogoutParams params) { return caller.invoke("account.logout", params, AccountLogoutResult.class); } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerMcpApi.java b/java/src/generated/java/com/github/copilot/generated/rpc/ServerMcpApi.java index 6ff26e80dd..b29c27fa4c 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/ServerMcpApi.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ServerMcpApi.java @@ -7,6 +7,7 @@ package com.github.copilot.generated.rpc; +import com.github.copilot.CopilotExperimental; import java.util.concurrent.CompletableFuture; import javax.annotation.processing.Generated; @@ -31,8 +32,11 @@ public final class ServerMcpApi { /** * Optional working directory used as context for MCP server discovery. + * + * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ + @CopilotExperimental public CompletableFuture discover(McpDiscoverParams params) { return caller.invoke("mcp.discover", params, McpDiscoverResult.class); } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerMcpConfigApi.java b/java/src/generated/java/com/github/copilot/generated/rpc/ServerMcpConfigApi.java index ce5974f5b5..6d3510f515 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/ServerMcpConfigApi.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ServerMcpConfigApi.java @@ -7,6 +7,7 @@ package com.github.copilot.generated.rpc; +import com.github.copilot.CopilotExperimental; import java.util.concurrent.CompletableFuture; import javax.annotation.processing.Generated; @@ -27,56 +28,77 @@ public final class ServerMcpConfigApi { /** * User-configured MCP servers, keyed by server name. + * + * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ + @CopilotExperimental public CompletableFuture list() { return caller.invoke("mcp.config.list", java.util.Map.of(), McpConfigListResult.class); } /** * MCP server name and configuration to add to user configuration. + * + * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ + @CopilotExperimental public CompletableFuture add(McpConfigAddParams params) { return caller.invoke("mcp.config.add", params, Void.class); } /** * MCP server name and replacement configuration to write to user configuration. + * + * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ + @CopilotExperimental public CompletableFuture update(McpConfigUpdateParams params) { return caller.invoke("mcp.config.update", params, Void.class); } /** * MCP server name to remove from user configuration. + * + * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ + @CopilotExperimental public CompletableFuture remove(McpConfigRemoveParams params) { return caller.invoke("mcp.config.remove", params, Void.class); } /** * MCP server names to enable for new sessions. + * + * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ + @CopilotExperimental public CompletableFuture enable(McpConfigEnableParams params) { return caller.invoke("mcp.config.enable", params, Void.class); } /** * MCP server names to disable for new sessions. + * + * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ + @CopilotExperimental public CompletableFuture disable(McpConfigDisableParams params) { return caller.invoke("mcp.config.disable", params, Void.class); } /** * Invokes {@code mcp.config.reload}. + * + * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ + @CopilotExperimental public CompletableFuture reload() { return caller.invoke("mcp.config.reload", java.util.Map.of(), Void.class); } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerModelsApi.java b/java/src/generated/java/com/github/copilot/generated/rpc/ServerModelsApi.java index c0515a06f4..64ccc62cde 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/ServerModelsApi.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ServerModelsApi.java @@ -7,6 +7,7 @@ package com.github.copilot.generated.rpc; +import com.github.copilot.CopilotExperimental; import java.util.concurrent.CompletableFuture; import javax.annotation.processing.Generated; @@ -27,8 +28,11 @@ public final class ServerModelsApi { /** * Optional GitHub token used to list models for a specific user instead of the global auth context. + * + * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ + @CopilotExperimental public CompletableFuture list() { return caller.invoke("models.list", java.util.Map.of(), ModelsListResult.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 9b90cea4a9..ce8c6c34f4 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 @@ -7,6 +7,7 @@ package com.github.copilot.generated.rpc; +import com.github.copilot.CopilotExperimental; import java.util.concurrent.CompletableFuture; import javax.annotation.processing.Generated; @@ -81,16 +82,22 @@ public ServerRpc(RpcCaller caller) { /** * Optional message to echo back to the caller. + * + * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ + @CopilotExperimental public CompletableFuture ping(PingParams params) { return caller.invoke("ping", params, PingResult.class); } /** * Optional connection token presented by the SDK client during the handshake. + * + * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ + @CopilotExperimental public CompletableFuture connect(ConnectParams params) { return caller.invoke("connect", params, ConnectResult.class); } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerRuntimeApi.java b/java/src/generated/java/com/github/copilot/generated/rpc/ServerRuntimeApi.java index 9c98df9682..e57db70946 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/ServerRuntimeApi.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ServerRuntimeApi.java @@ -7,6 +7,7 @@ package com.github.copilot.generated.rpc; +import com.github.copilot.CopilotExperimental; import java.util.concurrent.CompletableFuture; import javax.annotation.processing.Generated; @@ -27,8 +28,11 @@ public final class ServerRuntimeApi { /** * Invokes {@code runtime.shutdown}. + * + * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ + @CopilotExperimental public CompletableFuture shutdown() { return caller.invoke("runtime.shutdown", java.util.Map.of(), Void.class); } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerSecretsApi.java b/java/src/generated/java/com/github/copilot/generated/rpc/ServerSecretsApi.java index 800722c858..7f17687818 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/ServerSecretsApi.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ServerSecretsApi.java @@ -7,6 +7,7 @@ package com.github.copilot.generated.rpc; +import com.github.copilot.CopilotExperimental; import java.util.concurrent.CompletableFuture; import javax.annotation.processing.Generated; @@ -27,8 +28,11 @@ public final class ServerSecretsApi { /** * Secret values to add to the redaction filter. + * + * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ + @CopilotExperimental public CompletableFuture addFilterValues(SecretsAddFilterValuesParams params) { return caller.invoke("secrets.addFilterValues", params, SecretsAddFilterValuesResult.class); } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerSessionFsApi.java b/java/src/generated/java/com/github/copilot/generated/rpc/ServerSessionFsApi.java index 93022becf2..5f540897eb 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/ServerSessionFsApi.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ServerSessionFsApi.java @@ -7,6 +7,7 @@ package com.github.copilot.generated.rpc; +import com.github.copilot.CopilotExperimental; import java.util.concurrent.CompletableFuture; import javax.annotation.processing.Generated; @@ -27,8 +28,11 @@ public final class ServerSessionFsApi { /** * Initial working directory, session-state path layout, and path conventions used to register the calling SDK client as the session filesystem provider. + * + * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ + @CopilotExperimental public CompletableFuture setProvider(SessionFsSetProviderParams params) { return caller.invoke("sessionFs.setProvider", params, SessionFsSetProviderResult.class); } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsApi.java b/java/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsApi.java index 8dc4b19112..a7328dd567 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsApi.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsApi.java @@ -32,8 +32,11 @@ public final class ServerSkillsApi { /** * Optional project paths and additional skill directories to include in discovery. + * + * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ + @CopilotExperimental public CompletableFuture discover(SkillsDiscoverParams params) { return caller.invoke("skills.discover", params, SkillsDiscoverResult.class); } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsConfigApi.java b/java/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsConfigApi.java index e552227ccb..688288a118 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsConfigApi.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsConfigApi.java @@ -7,6 +7,7 @@ package com.github.copilot.generated.rpc; +import com.github.copilot.CopilotExperimental; import java.util.concurrent.CompletableFuture; import javax.annotation.processing.Generated; @@ -27,8 +28,11 @@ public final class ServerSkillsConfigApi { /** * Skill names to mark as disabled in global configuration, replacing any previous list. + * + * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ + @CopilotExperimental public CompletableFuture setDisabledSkills(SkillsConfigSetDisabledSkillsParams params) { return caller.invoke("skills.config.setDisabledSkills", params, Void.class); } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerToolsApi.java b/java/src/generated/java/com/github/copilot/generated/rpc/ServerToolsApi.java index 10e64747ef..2938010010 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/ServerToolsApi.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ServerToolsApi.java @@ -7,6 +7,7 @@ package com.github.copilot.generated.rpc; +import com.github.copilot.CopilotExperimental; import java.util.concurrent.CompletableFuture; import javax.annotation.processing.Generated; @@ -27,8 +28,11 @@ public final class ServerToolsApi { /** * Optional model identifier whose tool overrides should be applied to the listing. + * + * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ + @CopilotExperimental public CompletableFuture list(ToolsListParams params) { return caller.invoke("tools.list", params, ToolsListResult.class); } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerUserSettingsApi.java b/java/src/generated/java/com/github/copilot/generated/rpc/ServerUserSettingsApi.java index 0f11436e32..665cfb107a 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/ServerUserSettingsApi.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ServerUserSettingsApi.java @@ -7,6 +7,7 @@ package com.github.copilot.generated.rpc; +import com.github.copilot.CopilotExperimental; import java.util.concurrent.CompletableFuture; import javax.annotation.processing.Generated; @@ -27,10 +28,35 @@ public final class ServerUserSettingsApi { /** * Invokes {@code user.settings.reload}. + * + * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ + @CopilotExperimental public CompletableFuture reload() { return caller.invoke("user.settings.reload", java.util.Map.of(), Void.class); } + /** + * Per-key metadata for every known user setting (settings.json overlaid with the legacy config.json, config.json wins), including settings left at their default. Excludes repository- and enterprise-managed overrides. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture get() { + return caller.invoke("user.settings.get", java.util.Map.of(), UserSettingsGetResult.class); + } + + /** + * Partial user settings to write to settings.json. Each top-level key is written individually, replacing the existing value; a key whose value is null is removed. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture set(UserSettingsSetParams params) { + return caller.invoke("user.settings.set", params, UserSettingsSetResult.class); + } + } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderParams.java index 8003bdbb18..bcc1d964ef 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderParams.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderParams.java @@ -10,12 +10,16 @@ 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; /** * Initial working directory, session-state path layout, and path conventions used to register the calling SDK client as the session filesystem provider. + * + * @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) diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderResult.java index 222e160560..4809f53028 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderResult.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderResult.java @@ -10,12 +10,16 @@ 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; /** * Indicates whether the calling client was registered as the session filesystem provider. + * + * @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) diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataSnapshotResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataSnapshotResult.java index 908409dec1..774bcad981 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataSnapshotResult.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataSnapshotResult.java @@ -51,6 +51,8 @@ public record SessionMetadataSnapshotResult( @JsonProperty("currentMode") MetadataSnapshotCurrentMode currentMode, /** Currently selected model identifier, if any */ @JsonProperty("selectedModel") String selectedModel, + /** Current response limits for the session, or null when no limits are active */ + @JsonProperty("responseLimits") ResponseLimitsConfig responseLimits, /** Public-facing workspace metadata for this session, or null if the session has no associated workspace. Excludes runtime-internal fields (GitHub IDs, summary count, internal flags). */ @JsonProperty("workspace") SessionMetadataSnapshotResultWorkspace workspace ) { diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsUpdateParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsUpdateParams.java index 0ac8db564d..930b6ec636 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsUpdateParams.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsUpdateParams.java @@ -130,7 +130,7 @@ public record SessionOptionsUpdateParams( @JsonProperty("enableSkills") Boolean enableSkills, /** Context tier for models with tiered pricing. The session uses this to derive effective `modelCapabilitiesOverrides` so compaction, truncation, token display, and request limits honor the selected tier. */ @JsonProperty("contextTier") OptionsUpdateContextTier contextTier, - /** Optional response budget limits. Pass null to clear the response budget. */ - @JsonProperty("responseBudget") ResponseBudgetConfig responseBudget + /** Optional response limits. Pass null to clear the response limits. */ + @JsonProperty("responseLimits") ResponseLimitsConfig responseLimits ) { } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionRpc.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionRpc.java index 57ca767129..bcac24e699 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SessionRpc.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionRpc.java @@ -89,6 +89,8 @@ public final class SessionRpc { public final SessionUsageApi usage; /** API methods for the {@code remote} namespace. */ public final SessionRemoteApi remote; + /** API methods for the {@code visibility} namespace. */ + public final SessionVisibilityApi visibility; /** API methods for the {@code schedule} namespace. */ public final SessionScheduleApi schedule; @@ -131,6 +133,7 @@ public SessionRpc(RpcCaller caller, String sessionId) { this.eventLog = new SessionEventLogApi(caller, sessionId); this.usage = new SessionUsageApi(caller, sessionId); this.remote = new SessionRemoteApi(caller, sessionId); + this.visibility = new SessionVisibilityApi(caller, sessionId); this.schedule = new SessionScheduleApi(caller, sessionId); } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityApi.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityApi.java new file mode 100644 index 0000000000..54f38c2614 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityApi.java @@ -0,0 +1,60 @@ +/*--------------------------------------------------------------------------------------------- + * 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 visibility} namespace. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class SessionVisibilityApi { + + private static final com.fasterxml.jackson.databind.ObjectMapper MAPPER = RpcMapper.INSTANCE; + + private final RpcCaller caller; + private final String sessionId; + + /** @param caller the RPC transport function */ + SessionVisibilityApi(RpcCaller caller, String sessionId) { + this.caller = caller; + this.sessionId = sessionId; + } + + /** + * Identifies the target session. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture get() { + return caller.invoke("session.visibility.get", java.util.Map.of("sessionId", this.sessionId), SessionVisibilityGetResult.class); + } + + /** + * Desired sharing status for the session. + *

+ * Note: the {@code sessionId} field in the params record is overridden + * by the session-scoped wrapper; any value provided is ignored. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture set(SessionVisibilitySetParams params) { + com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); + _p.put("sessionId", this.sessionId); + return caller.invoke("session.visibility.set", _p, SessionVisibilitySetResult.class); + } + +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityGetParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityGetParams.java new file mode 100644 index 0000000000..e3c4a23709 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityGetParams.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; + +/** + * Identifies the target session. + * + * @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 SessionVisibilityGetParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityGetResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityGetResult.java new file mode 100644 index 0000000000..86c37cf6f6 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityGetResult.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 com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Current sharing status and shareable GitHub URL for a session. + * + * @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 SessionVisibilityGetResult( + /** Whether the session has been synced to Mission Control (i.e. has a GitHub task). When false, the session cannot be shared and `status`/`shareUrl` are absent. */ + @JsonProperty("synced") Boolean synced, + /** Current sharing status. Absent when the session is not synced or the status could not be retrieved (e.g. the user is not authenticated). */ + @JsonProperty("status") SessionVisibilityStatus status, + /** Shareable GitHub URL for the session. Present when the session is synced and the URL can be resolved. */ + @JsonProperty("shareUrl") String shareUrl +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilitySetParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilitySetParams.java new file mode 100644 index 0000000000..c5287ac7c4 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilitySetParams.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; + +/** + * Desired sharing status for the session. + * + * @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 SessionVisibilitySetParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId, + /** Sharing status to apply. "repo" makes the session visible to repository readers; "unshared" restricts it to the creator and collaborators. */ + @JsonProperty("status") SessionVisibilityStatus status +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilitySetResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilitySetResult.java new file mode 100644 index 0000000000..dda88be426 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilitySetResult.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 com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Effective sharing status and shareable GitHub URL after updating session visibility. + * + * @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 SessionVisibilitySetResult( + /** Whether the session has been synced to Mission Control (i.e. has a GitHub task). When false, the visibility change could not be applied and `status`/`shareUrl` are absent. */ + @JsonProperty("synced") Boolean synced, + /** Effective sharing status after the update. May differ from the requested status for task types that are already visible to repository readers by default. Absent when the update could not be applied (e.g. the session is not synced or the user is not authenticated). */ + @JsonProperty("status") SessionVisibilityStatus status, + /** Shareable GitHub URL for the session. Present when the session is synced and the URL can be resolved. */ + @JsonProperty("shareUrl") String shareUrl +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityStatus.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityStatus.java new file mode 100644 index 0000000000..46ba78511d --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityStatus.java @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * Sharing status for a synced session. "repo" makes the session visible to anyone with read access to the repository; "unshared" restricts it to the creator and collaborators. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum SessionVisibilityStatus { + /** The {@code repo} variant. */ + REPO("repo"), + /** The {@code unshared} variant. */ + UNSHARED("unshared"); + + private final String value; + SessionVisibilityStatus(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static SessionVisibilityStatus fromValue(String value) { + for (SessionVisibilityStatus v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown SessionVisibilityStatus value: " + value); + } +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SkillsConfigSetDisabledSkillsParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/SkillsConfigSetDisabledSkillsParams.java index 0711192d1a..1ba81d7b74 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SkillsConfigSetDisabledSkillsParams.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SkillsConfigSetDisabledSkillsParams.java @@ -10,13 +10,17 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; import java.util.List; import javax.annotation.processing.Generated; /** * Skill names to mark as disabled in global configuration, replacing any previous list. + * + * @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) diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SkillsDiscoverParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/SkillsDiscoverParams.java index 787e9278be..85cf09fadb 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SkillsDiscoverParams.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SkillsDiscoverParams.java @@ -10,13 +10,17 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; import java.util.List; import javax.annotation.processing.Generated; /** * Optional project paths and additional skill directories to include in discovery. + * + * @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) diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SkillsDiscoverResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/SkillsDiscoverResult.java index d0544b1c35..588e9760c6 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SkillsDiscoverResult.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SkillsDiscoverResult.java @@ -10,13 +10,17 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; import java.util.List; import javax.annotation.processing.Generated; /** * Skills discovered across global and project sources. + * + * @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) diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ToolsListParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/ToolsListParams.java index 384aeb5cbc..caee391eaf 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/ToolsListParams.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ToolsListParams.java @@ -10,12 +10,16 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; import javax.annotation.processing.Generated; /** * Optional model identifier whose tool overrides should be applied to the listing. + * + * @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) diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ToolsListResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/ToolsListResult.java index 41e933377a..099628aed7 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/ToolsListResult.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ToolsListResult.java @@ -10,13 +10,17 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; import java.util.List; import javax.annotation.processing.Generated; /** * Built-in tools available for the requested model, with their parameters and instructions. + * + * @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) diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/UserSettingMetadata.java b/java/src/generated/java/com/github/copilot/generated/rpc/UserSettingMetadata.java new file mode 100644 index 0000000000..fb6412f20e --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/UserSettingMetadata.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; + +/** + * A single user setting's effective value alongside its default, so consumers can render settings left at their default. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record UserSettingMetadata( + /** The effective value: the user's value if set, otherwise the default. */ + @JsonProperty("value") Object value, + /** The centrally-known default for this setting (null when no default is registered). */ + @JsonProperty("default") Object default_, + /** True when the user has not set an explicit value for this setting (i.e. it is left at its default). Reflects whether the user has overridden the key, not whether the effective value happens to equal the default — a key explicitly set to a value identical to the default still reports false. */ + @JsonProperty("isDefault") Boolean isDefault +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/UserSettingsGetResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/UserSettingsGetResult.java new file mode 100644 index 0000000000..c94e90fcc6 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/UserSettingsGetResult.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 com.github.copilot.CopilotExperimental; +import java.util.Map; +import javax.annotation.processing.Generated; + +/** + * Per-key metadata for every known user setting (settings.json overlaid with the legacy config.json, config.json wins), including settings left at their default. Excludes repository- and enterprise-managed overrides. + * + * @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 UserSettingsGetResult( + /** Every known user setting keyed by setting name, each with its effective value, default, and whether it is at the default. */ + @JsonProperty("settings") Map settings +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetParams.java new file mode 100644 index 0000000000..ba19886c24 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetParams.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; + +/** + * Partial user settings to write to settings.json. Each top-level key is written individually, replacing the existing value; a key whose value is null is removed. + * + * @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 UserSettingsSetParams( + /** Partial user settings to write, as a free-form object keyed by setting name */ + @JsonProperty("settings") Object settings +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetResult.java new file mode 100644 index 0000000000..c5ab98621a --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetResult.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 com.github.copilot.CopilotExperimental; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Outcome of writing user settings. + * + * @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 UserSettingsSetResult( + /** Top-level keys whose write landed in settings.json but is shadowed by a value still present in the legacy config.json (config.json wins on read). The write does not take effect until the legacy value is removed. */ + @JsonProperty("shadowedKeys") List shadowedKeys +) { +} diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index bfdf31c99e..3d8adc8534 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.66-1", + "@github/copilot": "^1.0.66-2", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" }, @@ -699,9 +699,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.66-1", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.66-1.tgz", - "integrity": "sha512-Cf0rTsG1wfdRzGmD9PC0TPYxQojItwo6Hv/Jp6GwakrBswLn4PlxW/pCQA7n3o2DahTQDX2y6Z9olAdx0dHFQA==", + "version": "1.0.66-2", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.66-2.tgz", + "integrity": "sha512-nAhhtfjpryklyombieuu18NK2g+BmEk4/8qvXVj8k+w/63tiVpLxFh865Vf6NQiVh/S7hbjMghTbrptsspYg2w==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "detect-libc": "^2.1.2" @@ -710,20 +710,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.66-1", - "@github/copilot-darwin-x64": "1.0.66-1", - "@github/copilot-linux-arm64": "1.0.66-1", - "@github/copilot-linux-x64": "1.0.66-1", - "@github/copilot-linuxmusl-arm64": "1.0.66-1", - "@github/copilot-linuxmusl-x64": "1.0.66-1", - "@github/copilot-win32-arm64": "1.0.66-1", - "@github/copilot-win32-x64": "1.0.66-1" + "@github/copilot-darwin-arm64": "1.0.66-2", + "@github/copilot-darwin-x64": "1.0.66-2", + "@github/copilot-linux-arm64": "1.0.66-2", + "@github/copilot-linux-x64": "1.0.66-2", + "@github/copilot-linuxmusl-arm64": "1.0.66-2", + "@github/copilot-linuxmusl-x64": "1.0.66-2", + "@github/copilot-win32-arm64": "1.0.66-2", + "@github/copilot-win32-x64": "1.0.66-2" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.66-1", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.66-1.tgz", - "integrity": "sha512-HTum+52pVBlrUrUjn/r/Q6kd2c0pvGsi6NyfuaGLRKStSQj00Iz5urYlo0hcq5JKF9eGB7ow+aeYc7BDIUVnhw==", + "version": "1.0.66-2", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.66-2.tgz", + "integrity": "sha512-gjLRtAQOdFQUOTm7nYi+zufkGxMlQlTzUyncQ3W4u1+WdGQbx5fWqMg/yd+j1yMN9PEETyF/ZHZqAaFWkEpQww==", "cpu": [ "arm64" ], @@ -737,9 +737,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.66-1", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.66-1.tgz", - "integrity": "sha512-gniq5/n2nX8cBQncjwvU7nAGYj21ALSknNUqhPWIQYwx+IM6KnGeBgSpldubJCMDjkZkbPYqskVcxTGvw0GGHA==", + "version": "1.0.66-2", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.66-2.tgz", + "integrity": "sha512-wWWBsVwJtRTXqCK8lVpzwbJd3Tm1F23avf942K+PmsGYiZZYNcS5pt4umQRRj0sHKgO/muuA4eg/tMfGNi5fgA==", "cpu": [ "x64" ], @@ -753,9 +753,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.66-1", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.66-1.tgz", - "integrity": "sha512-PG/xIIndXo0NpKYXR8GYPXAA3p/kuf4lsA898Pq+9UH5wU9ybqo5P/n5HBLXNOQnpP8+u9pjL9rPbvtwxMkzaQ==", + "version": "1.0.66-2", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.66-2.tgz", + "integrity": "sha512-j0hjx77JNFR3ZS8z3flY2j5SfGZMfKigYVFpDlTJM8FhfkMCUJ5IUhsZwSTimhHlxrsXuI31S6g0WsZLmBUe6A==", "cpu": [ "arm64" ], @@ -769,9 +769,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.66-1", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.66-1.tgz", - "integrity": "sha512-Tb11uVan2f8YjFLiTvPUC8yLSYdmoMru9J8axZRuiSgOtRfmaJGxHoM/axPYW+874YAn4gSygs7OPUt1C+67Xw==", + "version": "1.0.66-2", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.66-2.tgz", + "integrity": "sha512-vWaNbh4WdwkiI40Thcfbwi8tZFKo06r+Dm9Zfb8uY4wAz3X5PaGeSq+8XrNoV3uaRWltI0ncSIrq5tSOyDtRPg==", "cpu": [ "x64" ], @@ -785,9 +785,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.66-1", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.66-1.tgz", - "integrity": "sha512-GJEVj60B5MeJ8kfnf/dRmyX4EwU4HWL7yUZkrAG6xznSyHHPoTWtZ/tudQX/mf69emXtO7Nt9cLOcNIEdYRPMg==", + "version": "1.0.66-2", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.66-2.tgz", + "integrity": "sha512-LbWy5NlWasBeV/i+Xol+8dW7kbAQr6MF46apbseRNHYkhwyF/417WtLfirP8O2hPuqyU72q/HAQziFXkz14pIQ==", "cpu": [ "arm64" ], @@ -801,9 +801,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.66-1", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.66-1.tgz", - "integrity": "sha512-I5k9mMRuIO+pmPGDiblFXd+HOBJo92XEIBwbZMaAW3qRuyF5UcEFuWlczOCYzcTreXfBqNkG1P9qsBeDDNXfnQ==", + "version": "1.0.66-2", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.66-2.tgz", + "integrity": "sha512-djOu52fGIU7eUhQdUS0K5xB2eFdi8LTTbxvphHWlrN1AD1BdZ+VX9Pk2avt6yCfW+Hh0loh2pNsCbTfNyxvULA==", "cpu": [ "x64" ], @@ -817,9 +817,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.66-1", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.66-1.tgz", - "integrity": "sha512-tUkNUkx5F2TIefY3KDORon3THo256hr/ZVUMEph5fr6xSib4d8gGgNjzok/4kEfIR3a7L/45g0Qi+CzQNtjSdA==", + "version": "1.0.66-2", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.66-2.tgz", + "integrity": "sha512-YQu01atiwoz8XfrHKqvI1xNjnc2IIIxgJDkQ6PxwrWPZ4IO320izwlXbW2ZaOz9yDgjWNis6EJ4Ryz8K+mM6kg==", "cpu": [ "arm64" ], @@ -833,9 +833,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.66-1", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.66-1.tgz", - "integrity": "sha512-ktTbksWav2WSVi8BbTYxD4CJ+OrPximk5zPWff3stsU1MrG0XjZtlML1KUY3d/rrq2lpfZqh0ooF+A4bt8IFsQ==", + "version": "1.0.66-2", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.66-2.tgz", + "integrity": "sha512-4/kTs+lKc67f7KEAQ+Gt3sEBFDSEGoUxJujddV/+fS8EAg9uF2g6e3NzS1I4+htyRM4Oq/Z6xfWjGUgQsi9rfw==", "cpu": [ "x64" ], diff --git a/nodejs/package.json b/nodejs/package.json index 6d45a75dcd..6777caae15 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -56,7 +56,7 @@ "author": "GitHub", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.66-1", + "@github/copilot": "^1.0.66-2", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" }, diff --git a/nodejs/samples/package-lock.json b/nodejs/samples/package-lock.json index 2823d3db4a..aeb70b1c5d 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.66-1", + "@github/copilot": "^1.0.66-2", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" }, diff --git a/nodejs/src/generated/rpc.ts b/nodejs/src/generated/rpc.ts index 3ceadb45a0..65df346401 100644 --- a/nodejs/src/generated/rpc.ts +++ b/nodejs/src/generated/rpc.ts @@ -5,7 +5,7 @@ import type { MessageConnection } from "vscode-jsonrpc/node.js"; -import type { AbortReason, Attachment, ContextTier, EmbeddedBlobResourceContents, EmbeddedTextResourceContents, McpServerSource, McpServerStatus, PermissionPromptRequest, PermissionRule, ReasoningSummary, ResponseBudgetConfig, SessionEvent, SessionMode, ShutdownType, SkillSource, UserToolSessionApproval } from "./session-events.js"; +import type { AbortReason, Attachment, ContextTier, EmbeddedBlobResourceContents, EmbeddedTextResourceContents, McpServerSource, McpServerStatus, PermissionPromptRequest, PermissionRule, ReasoningSummary, ResponseLimitsConfig, SessionEvent, SessionMode, ShutdownType, SkillSource, UserToolSessionApproval } from "./session-events.js"; /** * Initial authentication info for the session. @@ -13,6 +13,7 @@ import type { AbortReason, Attachment, ContextTier, EmbeddedBlobResourceContents * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "AuthInfo". */ +/** @experimental */ export type AuthInfo = | HMACAuthInfo | EnvAuthInfo @@ -257,6 +258,7 @@ export type ConnectedRemoteSessionMetadataKind = * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "ContentFilterMode". */ +/** @experimental */ export type ContentFilterMode = /** Leave MCP tool result content unchanged. */ | "none" @@ -270,6 +272,7 @@ export type ContentFilterMode = * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "DiscoveredMcpServerType". */ +/** @experimental */ export type DiscoveredMcpServerType = /** Server communicates over stdio with a local child process. */ | "stdio" @@ -373,6 +376,7 @@ export type ExternalToolTextResultForLlmBinaryResultsForLlmType = export type ExternalToolTextResultForLlmContent = | ExternalToolTextResultForLlmContentText | ExternalToolTextResultForLlmContentTerminal + | ExternalToolTextResultForLlmContentShellExit | ExternalToolTextResultForLlmContentImage | ExternalToolTextResultForLlmContentAudio | ExternalToolTextResultForLlmContentResourceLink @@ -405,6 +409,7 @@ export type ExternalToolTextResultForLlmContentResourceDetails = * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "FilterMapping". */ +/** @experimental */ export type FilterMapping = | { [k: string]: ContentFilterMode; @@ -640,6 +645,7 @@ export type McpAppsSetHostContextDetailsPlatform = * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "McpServerConfig". */ +/** @experimental */ export type McpServerConfig = McpServerConfigStdio | McpServerConfigHttp; /** * Set to `true` to use defaults, or provide an object with additional auth or OIDC settings. @@ -647,6 +653,7 @@ export type McpServerConfig = McpServerConfigStdio | McpServerConfigHttp; * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "McpServerAuthConfig". */ +/** @experimental */ export type McpServerAuthConfig = boolean | McpServerAuthConfigRedirectPort; /** * Controls if tools provided by this server can be loaded on demand via tool search (auto) or always included in the initial tool list (never) @@ -654,6 +661,7 @@ export type McpServerAuthConfig = boolean | McpServerAuthConfigRedirectPort; * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "McpServerConfigDeferTools". */ +/** @experimental */ export type McpServerConfigDeferTools = /** Tools may be deferred under certain conditions */ | "auto" @@ -665,6 +673,7 @@ export type McpServerConfigDeferTools = * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "McpServerConfigHttpType". */ +/** @experimental */ export type McpServerConfigHttpType = /** Streamable HTTP transport. */ | "http" @@ -676,6 +685,7 @@ export type McpServerConfigHttpType = * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "McpServerConfigHttpOauthGrantType". */ +/** @experimental */ export type McpServerConfigHttpOauthGrantType = /** Interactive browser-based authorization code flow with PKCE. */ | "authorization_code" @@ -858,6 +868,7 @@ export type MetadataSnapshotRemoteMetadataTaskType = * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "ModelPolicyState". */ +/** @experimental */ export type ModelPolicyState = /** The model is enabled by policy. */ | "enabled" @@ -871,6 +882,7 @@ export type ModelPolicyState = * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "ModelPickerCategory". */ +/** @experimental */ export type ModelPickerCategory = /** Lightweight model category optimized for faster, lower-cost interactions. */ | "lightweight" @@ -884,6 +896,7 @@ export type ModelPickerCategory = * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "ModelPickerPriceCategory". */ +/** @experimental */ export type ModelPickerPriceCategory = /** Lowest relative token cost tier. */ | "low" @@ -1348,6 +1361,7 @@ export type SessionFsReaddirWithTypesEntryType = * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "SessionFsSetProviderConventions". */ +/** @experimental */ export type SessionFsSetProviderConventions = /** Paths use Windows path conventions. */ | "windows" @@ -1574,6 +1588,18 @@ export type SessionSource = | "remote" /** Return both local and remote sessions. */ | "all"; +/** + * Sharing status for a synced session. "repo" makes the session visible to anyone with read access to the repository; "unshared" restricts it to the creator and collaborators. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SessionVisibilityStatus". + */ +/** @experimental */ +export type SessionVisibilityStatus = + /** The session is visible to repository readers. */ + | "repo" + /** The session is restricted to its creator and collaborators. */ + | "unshared"; /** * Signal to send (default: SIGTERM) * @@ -1861,6 +1887,7 @@ export type WorkspacesWorkspaceDetailsHostType = * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "AccountGetAllUsersResult". */ +/** @experimental */ export type AccountGetAllUsersResult = AccountAllUsers[]; /** @@ -1896,6 +1923,7 @@ export interface AbortResult { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "AccountAllUsers". */ +/** @experimental */ export interface AccountAllUsers { authInfo: AuthInfo; /** @@ -1909,6 +1937,7 @@ export interface AccountAllUsers { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "HMACAuthInfo". */ +/** @experimental */ export interface HMACAuthInfo { /** * HMAC-based authentication used by GitHub-internal services. @@ -1930,6 +1959,7 @@ export interface HMACAuthInfo { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "CopilotUserResponse". */ +/** @experimental */ export interface CopilotUserResponse { login?: string; access_type_sku?: string; @@ -2005,6 +2035,7 @@ export interface CopilotUserResponse { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "CopilotUserResponseEndpoints". */ +/** @experimental */ export interface CopilotUserResponseEndpoints { api?: string; "origin-tracker"?: string; @@ -2017,6 +2048,7 @@ export interface CopilotUserResponseEndpoints { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "CopilotUserResponseQuotaSnapshots". */ +/** @experimental */ export interface CopilotUserResponseQuotaSnapshots { chat?: CopilotUserResponseQuotaSnapshotsChat; completions?: CopilotUserResponseQuotaSnapshotsCompletions; @@ -2044,6 +2076,7 @@ export interface CopilotUserResponseQuotaSnapshots { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "CopilotUserResponseQuotaSnapshotsChat". */ +/** @experimental */ export interface CopilotUserResponseQuotaSnapshotsChat { entitlement?: number; overage_count?: number; @@ -2064,6 +2097,7 @@ export interface CopilotUserResponseQuotaSnapshotsChat { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "CopilotUserResponseQuotaSnapshotsCompletions". */ +/** @experimental */ export interface CopilotUserResponseQuotaSnapshotsCompletions { entitlement?: number; overage_count?: number; @@ -2084,6 +2118,7 @@ export interface CopilotUserResponseQuotaSnapshotsCompletions { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "CopilotUserResponseQuotaSnapshotsPremiumInteractions". */ +/** @experimental */ export interface CopilotUserResponseQuotaSnapshotsPremiumInteractions { entitlement?: number; overage_count?: number; @@ -2104,6 +2139,7 @@ export interface CopilotUserResponseQuotaSnapshotsPremiumInteractions { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "EnvAuthInfo". */ +/** @experimental */ export interface EnvAuthInfo { /** * Personal access token (PAT) or server-to-server token sourced from an environment variable. @@ -2133,6 +2169,7 @@ export interface EnvAuthInfo { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "TokenAuthInfo". */ +/** @experimental */ export interface TokenAuthInfo { /** * SDK-side token authentication; the host configured the token directly via the SDK. @@ -2154,6 +2191,7 @@ export interface TokenAuthInfo { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "CopilotApiTokenAuthInfo". */ +/** @experimental */ export interface CopilotApiTokenAuthInfo { /** * Direct Copilot API authentication via the `GITHUB_COPILOT_API_TOKEN` + `COPILOT_API_URL` environment-variable pair. The token itself is read from the environment by the runtime, not carried in this struct. @@ -2171,6 +2209,7 @@ export interface CopilotApiTokenAuthInfo { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "UserAuthInfo". */ +/** @experimental */ export interface UserAuthInfo { /** * OAuth user authentication. The token itself is held in the runtime's secret token store (keyed by host+login) and is NOT carried in this struct. @@ -2192,6 +2231,7 @@ export interface UserAuthInfo { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "GhCliAuthInfo". */ +/** @experimental */ export interface GhCliAuthInfo { /** * Authentication via the `gh` CLI's saved credentials. @@ -2217,6 +2257,7 @@ export interface GhCliAuthInfo { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "ApiKeyAuthInfo". */ +/** @experimental */ export interface ApiKeyAuthInfo { /** * API-key authentication for non-GitHub LLM providers (e.g. when running BYOM-style). @@ -2238,6 +2279,7 @@ export interface ApiKeyAuthInfo { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "AccountGetCurrentAuthResult". */ +/** @experimental */ export interface AccountGetCurrentAuthResult { authInfo?: AuthInfo; /** @@ -2246,6 +2288,7 @@ export interface AccountGetCurrentAuthResult { authErrors?: string[]; } +/** @experimental */ export interface AccountGetQuotaRequest { /** * GitHub token for per-user quota lookup. When provided, resolves this token to determine the user's quota instead of using the global auth. @@ -2258,6 +2301,7 @@ export interface AccountGetQuotaRequest { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "AccountGetQuotaResult". */ +/** @experimental */ export interface AccountGetQuotaResult { /** * Quota snapshots keyed by type (e.g., chat, completions, premium_interactions) @@ -2272,6 +2316,7 @@ export interface AccountGetQuotaResult { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "AccountQuotaSnapshot". */ +/** @experimental */ export interface AccountQuotaSnapshot { /** * Whether the user has an unlimited usage entitlement @@ -2312,6 +2357,7 @@ export interface AccountQuotaSnapshot { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "AccountLoginRequest". */ +/** @experimental */ export interface AccountLoginRequest { /** * GitHub host URL @@ -2332,6 +2378,7 @@ export interface AccountLoginRequest { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "AccountLoginResult". */ +/** @experimental */ export interface AccountLoginResult { /** * Whether the credential was persisted to a secure store (system keychain, or the config file when plaintext storage is enabled). False when no secure store was available and the token was not saved, so the consumer can decide how to proceed. @@ -2344,6 +2391,7 @@ export interface AccountLoginResult { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "AccountLogoutRequest". */ +/** @experimental */ export interface AccountLogoutRequest { authInfo: AuthInfo; } @@ -2353,6 +2401,7 @@ export interface AccountLogoutRequest { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "AccountLogoutResult". */ +/** @experimental */ export interface AccountLogoutResult { /** * Whether other authenticated users remain after logout @@ -3468,6 +3517,7 @@ export interface ConnectRemoteSessionParams { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "ConnectRequest". */ +/** @experimental */ /** @internal */ export interface ConnectRequest { /** @@ -3481,6 +3531,7 @@ export interface ConnectRequest { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "ConnectResult". */ +/** @experimental */ /** @internal */ export interface ConnectResult { /** @@ -3559,6 +3610,7 @@ export interface CurrentToolMetadata { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "DiscoveredMcpServer". */ +/** @experimental */ export interface DiscoveredMcpServer { /** * Server name (config key) @@ -3900,6 +3952,39 @@ export interface ExternalToolTextResultForLlmContentTerminal { */ cwd?: string; } +/** + * Shell command exit metadata with optional output preview + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ExternalToolTextResultForLlmContentShellExit". + */ +/** @experimental */ +export interface ExternalToolTextResultForLlmContentShellExit { + /** + * Content block type discriminator + */ + type: "shell_exit"; + /** + * Shell id, as assigned by Copilot runtime + */ + shellId: string; + /** + * Exit code from the completed shell command + */ + exitCode: number; + /** + * Working directory where the shell command was executed + */ + cwd?: string; + /** + * Output associated with this shell command, if available. May be partial, truncated, or a preview; not guaranteed to be full output. + */ + outputPreview?: string; + /** + * Whether outputPreview is known to be incomplete or truncated + */ + outputTruncated?: boolean; +} /** * Image content block with base64-encoded data * @@ -5258,6 +5343,7 @@ export interface McpCancelSamplingExecutionResult { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "McpConfigAddRequest". */ +/** @experimental */ export interface McpConfigAddRequest { /** * Unique name for the MCP server @@ -5271,6 +5357,7 @@ export interface McpConfigAddRequest { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "McpServerConfigStdio". */ +/** @experimental */ export interface McpServerConfigStdio { /** * Tools to include. Defaults to all tools if not specified. @@ -5313,6 +5400,7 @@ export interface McpServerConfigStdio { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "McpServerAuthConfigRedirectPort". */ +/** @experimental */ export interface McpServerAuthConfigRedirectPort { /** * Fixed port for the OAuth redirect callback server. @@ -5325,6 +5413,7 @@ export interface McpServerAuthConfigRedirectPort { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "McpServerConfigHttp". */ +/** @experimental */ export interface McpServerConfigHttp { /** * Tools to include. Defaults to all tools if not specified. @@ -5369,6 +5458,7 @@ export interface McpServerConfigHttp { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "McpConfigDisableRequest". */ +/** @experimental */ export interface McpConfigDisableRequest { /** * Names of MCP servers to disable. Each server is added to the persisted disabled list so new sessions skip it. Already-disabled names are ignored. Active sessions keep their current connections until they end. @@ -5381,6 +5471,7 @@ export interface McpConfigDisableRequest { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "McpConfigEnableRequest". */ +/** @experimental */ export interface McpConfigEnableRequest { /** * Names of MCP servers to enable. Each server is removed from the persisted disabled list so new sessions spawn it. Unknown or already-enabled names are ignored. @@ -5393,6 +5484,7 @@ export interface McpConfigEnableRequest { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "McpConfigList". */ +/** @experimental */ export interface McpConfigList { /** * All MCP servers from user config, keyed by name @@ -5407,6 +5499,7 @@ export interface McpConfigList { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "McpConfigRemoveRequest". */ +/** @experimental */ export interface McpConfigRemoveRequest { /** * Name of the MCP server to remove @@ -5419,6 +5512,7 @@ export interface McpConfigRemoveRequest { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "McpConfigUpdateRequest". */ +/** @experimental */ export interface McpConfigUpdateRequest { /** * Name of the MCP server to update @@ -5476,6 +5570,7 @@ export interface McpDisableRequest { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "McpDiscoverRequest". */ +/** @experimental */ export interface McpDiscoverRequest { /** * Working directory used as context for discovery (e.g., plugin resolution) @@ -5488,6 +5583,7 @@ export interface McpDiscoverRequest { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "McpDiscoverResult". */ +/** @experimental */ export interface McpDiscoverResult { /** * MCP servers discovered from all sources @@ -6306,6 +6402,7 @@ export interface MetadataSnapshotRemoteMetadataRepository { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "Model". */ +/** @experimental */ export interface Model { /** * Model identifier (e.g., "claude-sonnet-4.5") @@ -6335,6 +6432,7 @@ export interface Model { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "ModelCapabilities". */ +/** @experimental */ export interface ModelCapabilities { supports?: ModelCapabilitiesSupports; limits?: ModelCapabilitiesLimits; @@ -6345,6 +6443,7 @@ export interface ModelCapabilities { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "ModelCapabilitiesSupports". */ +/** @experimental */ export interface ModelCapabilitiesSupports { /** * Whether this model supports vision/image input @@ -6361,6 +6460,7 @@ export interface ModelCapabilitiesSupports { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "ModelCapabilitiesLimits". */ +/** @experimental */ export interface ModelCapabilitiesLimits { /** * Maximum number of prompt/input tokens @@ -6382,6 +6482,7 @@ export interface ModelCapabilitiesLimits { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "ModelCapabilitiesLimitsVision". */ +/** @experimental */ export interface ModelCapabilitiesLimitsVision { /** * MIME types the model accepts @@ -6402,6 +6503,7 @@ export interface ModelCapabilitiesLimitsVision { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "ModelPolicy". */ +/** @experimental */ export interface ModelPolicy { state: ModelPolicyState; /** @@ -6415,6 +6517,7 @@ export interface ModelPolicy { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "ModelBilling". */ +/** @experimental */ export interface ModelBilling { /** * Billing cost multiplier relative to the base rate @@ -6432,6 +6535,7 @@ export interface ModelBilling { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "ModelBillingTokenPrices". */ +/** @experimental */ export interface ModelBillingTokenPrices { /** * AI Credits cost per billing batch of input tokens @@ -6475,6 +6579,7 @@ export interface ModelBillingTokenPrices { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "ModelBillingTokenPricesLongContext". */ +/** @experimental */ export interface ModelBillingTokenPricesLongContext { /** * AI Credits cost per billing batch of input tokens @@ -6584,6 +6689,7 @@ export interface ModelCapabilitiesOverrideLimitsVision { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "ModelList". */ +/** @experimental */ export interface ModelList { /** * List of available models with full metadata @@ -6630,6 +6736,7 @@ export interface ModelSetReasoningEffortResult { reasoningEffort: string; } +/** @experimental */ export interface ModelsListRequest { /** * GitHub token for per-user model listing. When provided, resolves this token to determine the user's Copilot plan and available models instead of using the global auth. @@ -7902,6 +8009,7 @@ export interface PermissionsFolderTrustAddTrustedResult { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "PermissionsGetAllowAllRequest". */ +/** @experimental */ export interface PermissionsGetAllowAllRequest {} /** * Indicates whether the operation succeeded. @@ -7983,6 +8091,7 @@ export interface PermissionsPathsAddResult { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "PermissionsPathsListRequest". */ +/** @experimental */ export interface PermissionsPathsListRequest {} /** * Indicates whether the operation succeeded. @@ -8003,6 +8112,7 @@ export interface PermissionsPathsUpdatePrimaryResult { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "PermissionsPendingRequestsRequest". */ +/** @experimental */ export interface PermissionsPendingRequestsRequest {} /** * No parameters; clears all session-scoped tool permission approvals. @@ -8010,6 +8120,7 @@ export interface PermissionsPendingRequestsRequest {} * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "PermissionsResetSessionApprovalsRequest". */ +/** @experimental */ export interface PermissionsResetSessionApprovalsRequest {} /** * Indicates whether the operation succeeded. @@ -8123,6 +8234,7 @@ export interface PermissionUrlsSetUnrestrictedModeParams { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "PingRequest". */ +/** @experimental */ export interface PingRequest { /** * Optional message to echo back @@ -8135,6 +8247,7 @@ export interface PingRequest { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "PingResult". */ +/** @experimental */ export interface PingResult { /** * Echoed message (or default greeting) @@ -9941,6 +10054,7 @@ export interface ScheduleStopResult { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "SecretsAddFilterValuesRequest". */ +/** @experimental */ export interface SecretsAddFilterValuesRequest { /** * Raw secret values to register for redaction @@ -9953,6 +10067,7 @@ export interface SecretsAddFilterValuesRequest { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "SecretsAddFilterValuesResult". */ +/** @experimental */ export interface SecretsAddFilterValuesResult { /** * Whether the values were successfully registered @@ -10080,6 +10195,7 @@ export interface ServerInstructionSourceList { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "ServerSkill". */ +/** @experimental */ export interface ServerSkill { /** * Unique identifier for the skill @@ -10117,6 +10233,7 @@ export interface ServerSkill { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "ServerSkillList". */ +/** @experimental */ export interface ServerSkillList { /** * All discovered skills across all sources @@ -10451,6 +10568,7 @@ export interface SessionFsRmRequest { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "SessionFsSetProviderCapabilities". */ +/** @experimental */ export interface SessionFsSetProviderCapabilities { /** * Whether the provider supports SQLite query/exists operations @@ -10463,6 +10581,7 @@ export interface SessionFsSetProviderCapabilities { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "SessionFsSetProviderRequest". */ +/** @experimental */ export interface SessionFsSetProviderRequest { /** * Initial working directory for sessions @@ -10481,6 +10600,7 @@ export interface SessionFsSetProviderRequest { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "SessionFsSetProviderResult". */ +/** @experimental */ export interface SessionFsSetProviderResult { /** * Whether the provider was set successfully @@ -10813,6 +10933,10 @@ export interface SessionMetadataSnapshot { * Currently selected model identifier, if any */ selectedModel?: string; + /** + * Current response limits for the session, or null when no limits are active + */ + responseLimits: ResponseLimitsConfig | null; /** * Public-facing workspace metadata for this session, or null if the session has no associated workspace. Excludes runtime-internal fields (GitHub IDs, summary count, internal flags). */ @@ -11033,7 +11157,7 @@ export interface SessionOpenOptions { */ maxInlineBinaryBytes?: number; modelCapabilitiesOverrides?: ModelCapabilitiesOverride; - responseBudget?: ResponseBudgetConfig; + responseLimits?: ResponseLimitsConfig; /** * Runtime context discriminator for agent filtering. */ @@ -12054,9 +12178,9 @@ export interface SessionUpdateOptionsParams { enableSkills?: boolean; contextTier?: OptionsUpdateContextTier; /** - * Optional response budget limits. Pass null to clear the response budget. + * Optional response limits. Pass null to clear the response limits. */ - responseBudget?: ResponseBudgetConfig | null; + responseLimits?: ResponseLimitsConfig | null; } /** * Indicates whether the session options patch was applied successfully. @@ -12268,6 +12392,7 @@ export interface SkillList { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "SkillsConfigSetDisabledSkillsRequest". */ +/** @experimental */ export interface SkillsConfigSetDisabledSkillsRequest { /** * List of skill names to disable @@ -12293,6 +12418,7 @@ export interface SkillsDisableRequest { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "SkillsDiscoverRequest". */ +/** @experimental */ export interface SkillsDiscoverRequest { /** * Optional list of project directory paths to scan for project-scoped skills @@ -12980,6 +13106,7 @@ export interface TelemetrySetFeatureOverridesRequest { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "Tool". */ +/** @experimental */ export interface Tool { /** * Tool identifier (e.g., "bash", "grep", "str_replace_editor") @@ -13010,6 +13137,7 @@ export interface Tool { * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "ToolList". */ +/** @experimental */ export interface ToolList { /** * List of available built-in tools with metadata @@ -13043,6 +13171,7 @@ export interface ToolsInitializeAndValidateResult {} * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "ToolsListRequest". */ +/** @experimental */ export interface ToolsListRequest { /** * Optional model ID — when provided, the returned tool list reflects model-specific overrides @@ -13838,6 +13967,120 @@ export interface UserRequestedShellCommandResult { */ error?: string; } +/** + * A single user setting's effective value alongside its default, so consumers can render settings left at their default. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingMetadata". + */ +/** @experimental */ +export interface UserSettingMetadata { + /** + * The effective value: the user's value if set, otherwise the default. + */ + value: { + [k: string]: unknown | undefined; + }; + /** + * The centrally-known default for this setting (null when no default is registered). + */ + default: { + [k: string]: unknown | undefined; + }; + /** + * True when the user has not set an explicit value for this setting (i.e. it is left at its default). Reflects whether the user has overridden the key, not whether the effective value happens to equal the default — a key explicitly set to a value identical to the default still reports false. + */ + isDefault: boolean; +} +/** + * Per-key metadata for every known user setting (settings.json overlaid with the legacy config.json, config.json wins), including settings left at their default. Excludes repository- and enterprise-managed overrides. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsGetResult". + */ +/** @experimental */ +export interface UserSettingsGetResult { + /** + * Every known user setting keyed by setting name, each with its effective value, default, and whether it is at the default. + */ + settings: { + [k: string]: UserSettingMetadata; + }; +} +/** + * Partial user settings to write to settings.json. Each top-level key is written individually, replacing the existing value; a key whose value is null is removed. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsSetRequest". + */ +/** @experimental */ +export interface UserSettingsSetRequest { + /** + * Partial user settings to write, as a free-form object keyed by setting name + */ + settings: { + [k: string]: unknown | undefined; + }; +} +/** + * Outcome of writing user settings. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UserSettingsSetResult". + */ +/** @experimental */ +export interface UserSettingsSetResult { + /** + * Top-level keys whose write landed in settings.json but is shadowed by a value still present in the legacy config.json (config.json wins on read). The write does not take effect until the legacy value is removed. + */ + shadowedKeys: string[]; +} +/** + * Current sharing status and shareable GitHub URL for a session. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "VisibilityGetResult". + */ +/** @experimental */ +export interface VisibilityGetResult { + /** + * Whether the session has been synced to Mission Control (i.e. has a GitHub task). When false, the session cannot be shared and `status`/`shareUrl` are absent. + */ + synced: boolean; + status?: SessionVisibilityStatus; + /** + * Shareable GitHub URL for the session. Present when the session is synced and the URL can be resolved. + */ + shareUrl?: string; +} +/** + * Desired sharing status for the session. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "VisibilitySetRequest". + */ +/** @experimental */ +export interface VisibilitySetRequest { + status: SessionVisibilityStatus; +} +/** + * Effective sharing status and shareable GitHub URL after updating session visibility. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "VisibilitySetResult". + */ +/** @experimental */ +export interface VisibilitySetResult { + /** + * Whether the session has been synced to Mission Control (i.e. has a GitHub task). When false, the visibility change could not be applied and `status`/`shareUrl` are absent. + */ + synced: boolean; + status?: SessionVisibilityStatus; + /** + * Shareable GitHub URL for the session. Present when the session is synced and the URL can be resolved. + */ + shareUrl?: string; +} /** * A single changed file and its unified diff. * @@ -14124,9 +14367,12 @@ export function createServerRpc(connection: MessageConnection) { * @param params Optional message to echo back to the caller. * * @returns Server liveness response, including the echoed message, current server timestamp, and protocol version. + * + * @experimental */ ping: async (params: PingRequest): Promise => connection.sendRequest("ping", params), + /** @experimental */ models: { /** * Lists Copilot models available to the authenticated user. @@ -14138,6 +14384,7 @@ export function createServerRpc(connection: MessageConnection) { list: async (params: ModelsListRequest): Promise => connection.sendRequest("models.list", params), }, + /** @experimental */ tools: { /** * Lists built-in tools available for a model. @@ -14149,6 +14396,7 @@ export function createServerRpc(connection: MessageConnection) { list: async (params: ToolsListRequest): Promise => connection.sendRequest("tools.list", params), }, + /** @experimental */ account: { /** * Gets Copilot quota usage for the authenticated user or supplied GitHub token. @@ -14192,6 +14440,7 @@ export function createServerRpc(connection: MessageConnection) { logout: async (params: AccountLogoutRequest): Promise => connection.sendRequest("account.logout", params), }, + /** @experimental */ secrets: { /** * Registers secret values for redaction in session logs and exports. The SDK calls this to inject dynamically generated secret values (e.g., OIDC tokens). @@ -14203,7 +14452,9 @@ export function createServerRpc(connection: MessageConnection) { addFilterValues: async (params: SecretsAddFilterValuesRequest): Promise => connection.sendRequest("secrets.addFilterValues", params), }, + /** @experimental */ mcp: { + /** @experimental */ config: { /** * Lists MCP servers from user configuration. @@ -14365,7 +14616,9 @@ export function createServerRpc(connection: MessageConnection) { connection.sendRequest("plugins.marketplaces.refresh", params), }, }, + /** @experimental */ skills: { + /** @experimental */ config: { /** * Replaces the global list of disabled skills. @@ -14390,8 +14643,6 @@ export function createServerRpc(connection: MessageConnection) { * @param params Optional project paths to enumerate. * * @returns Canonical locations where skills can be created so the runtime will recognize them. - * - * @experimental */ getDiscoveryPaths: async (params: SkillsGetDiscoveryPathsRequest): Promise => connection.sendRequest("skills.getDiscoveryPaths", params), @@ -14438,15 +14689,34 @@ export function createServerRpc(connection: MessageConnection) { getDiscoveryPaths: async (params: InstructionsGetDiscoveryPathsRequest): Promise => connection.sendRequest("instructions.getDiscoveryPaths", params), }, + /** @experimental */ user: { + /** @experimental */ settings: { /** * Drops this runtime process's in-memory user settings cache so the next settings read observes disk. */ reload: async (): Promise => connection.sendRequest("user.settings.reload", {}), + /** + * Lists every known user setting (settings.json overlaid with the legacy config.json, config.json wins), each with its effective value, its default, and whether it is at the default — so settings the user has never set still appear with their default value. Does not include repository- or enterprise-managed overrides that the runtime layers on top at session time. + * + * @returns Per-key metadata for every known user setting (settings.json overlaid with the legacy config.json, config.json wins), including settings left at their default. Excludes repository- and enterprise-managed overrides. + */ + get: async (): Promise => + connection.sendRequest("user.settings.get", {}), + /** + * Writes one or more user settings to settings.json, replacing each provided top-level key. A key whose value is null is removed. Returns the keys whose new value is shadowed by a legacy config.json entry (config.json wins on read), which the runtime leaves in place — such writes do not take effect until the legacy value is removed. + * + * @param params Partial user settings to write to settings.json. Each top-level key is written individually, replacing the existing value; a key whose value is null is removed. + * + * @returns Outcome of writing user settings. + */ + set: async (params: UserSettingsSetRequest): Promise => + connection.sendRequest("user.settings.set", params), }, }, + /** @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. @@ -14454,6 +14724,7 @@ export function createServerRpc(connection: MessageConnection) { shutdown: async (): Promise => connection.sendRequest("runtime.shutdown", {}), }, + /** @experimental */ sessionFs: { /** * Registers an SDK client as the session filesystem provider. @@ -14727,6 +14998,8 @@ export function createInternalServerRpc(connection: MessageConnection) { * @param params Optional connection token presented by the SDK client during the handshake. * * @returns Handshake result reporting the server's protocol version and package version on success. + * + * @experimental */ connect: async (params: ConnectRequest): Promise => connection.sendRequest("connect", params), @@ -16176,6 +16449,25 @@ export function createSessionRpc(connection: MessageConnection, sessionId: strin connection.sendRequest("session.remote.notifySteerableChanged", { sessionId, ...params }), }, /** @experimental */ + visibility: { + /** + * Returns the session's current Mission Control sharing status and shareable GitHub URL. Reflects whether the synced session is visible to repository readers ("repo") or restricted to its creator and collaborators ("unshared"). + * + * @returns Current sharing status and shareable GitHub URL for a session. + */ + get: async (): Promise => + connection.sendRequest("session.visibility.get", { sessionId }), + /** + * Sets the session's Mission Control sharing status, controlling whether the synced session is visible to repository readers. Returns the effective status and shareable GitHub URL after the change. + * + * @param params Desired sharing status for the session. + * + * @returns Effective sharing status and shareable GitHub URL after updating session visibility. + */ + set: async (params: VisibilitySetRequest): Promise => + connection.sendRequest("session.visibility.set", { sessionId, ...params }), + }, + /** @experimental */ schedule: { /** * Lists the session's currently active scheduled prompts. diff --git a/nodejs/src/generated/session-events.ts b/nodejs/src/generated/session-events.ts index 5ecc550de3..68462f20b1 100644 --- a/nodejs/src/generated/session-events.ts +++ b/nodejs/src/generated/session-events.ts @@ -21,6 +21,7 @@ export type SessionEvent = | WarningEvent | ModelChangeEvent | ModeChangedEvent + | ResponseLimitsChangedEvent | PermissionsChangedEvent | PlanChangedEvent | TodosChangedEvent @@ -363,6 +364,7 @@ export type BinaryAssetReferenceType = export type ToolExecutionCompleteContent = | ToolExecutionCompleteContentText | ToolExecutionCompleteContentTerminal + | ToolExecutionCompleteContentShellExit | ToolExecutionCompleteContentImage | ToolExecutionCompleteContentAudio | ToolExecutionCompleteContentResourceLink @@ -738,7 +740,7 @@ export interface StartData { * Whether this session supports remote steering via GitHub */ remoteSteerable?: boolean; - responseBudget?: ResponseBudgetConfig; + responseLimits?: ResponseLimitsConfig; /** * Model selected at session creation time, if any */ @@ -791,17 +793,13 @@ export interface WorkingDirectoryContext { repositoryHost?: string; } /** - * Optional response budget limits. + * Optional response limits. */ -export interface ResponseBudgetConfig { +export interface ResponseLimitsConfig { /** * Maximum AI Credits allowed while responding to one top-level user message. */ maxAiCredits?: number; - /** - * Maximum model-call iterations allowed while responding to one top-level user message. - */ - maxModelIterations?: number; } /** * Session event "session.resume". Session resume metadata including current context and event count @@ -868,9 +866,9 @@ export interface ResumeData { */ remoteSteerable?: boolean; /** - * Response budget limits currently configured at resume time; null when no budget is active + * Response limits currently configured at resume time; null when no limits are active */ - responseBudget?: ResponseBudgetConfig | null; + responseLimits?: ResponseLimitsConfig | null; /** * ISO 8601 timestamp when the session was resumed */ @@ -1462,6 +1460,45 @@ export interface ModeChangedData { newMode: SessionMode; previousMode: SessionMode; } +/** + * Session event "session.response_limits_changed". Response limits update details. Null clears the limits. + */ +export interface ResponseLimitsChangedEvent { + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; + data: ResponseLimitsChangedData; + /** + * When true, the event is transient and not persisted to the session event log on disk + */ + ephemeral?: boolean; + /** + * Unique event identifier (UUID v4), generated when the event is emitted + */ + id: string; + /** + * ID of the chronologically preceding event in the session, forming a linked chain. Null for the first event. + */ + parentId: string | null; + /** + * ISO 8601 timestamp when the event was created + */ + timestamp: string; + /** + * Type discriminator. Always "session.response_limits_changed". + */ + type: "session.response_limits_changed"; +} +/** + * Response limits update details. Null clears the limits. + */ +export interface ResponseLimitsChangedData { + /** + * Current response limits for the session, or null when no limits are active + */ + responseLimits: ResponseLimitsConfig | null; +} /** * Session event "session.permissions_changed". Permissions change details carrying the aggregate allow-all boolean transition. */ @@ -4441,7 +4478,8 @@ export interface ToolExecutionCompleteContentText { type: "text"; } /** - * Terminal/shell output content block with optional exit code and working directory + * @deprecated + * Deprecated for shell command exit metadata. Use ToolExecutionCompleteContentShellExit instead. */ export interface ToolExecutionCompleteContentTerminal { /** @@ -4461,6 +4499,35 @@ export interface ToolExecutionCompleteContentTerminal { */ type: "terminal"; } +/** + * Shell command exit metadata with optional output preview + */ +export interface ToolExecutionCompleteContentShellExit { + /** + * Working directory where the shell command was executed + */ + cwd?: string; + /** + * Exit code from the completed shell command + */ + exitCode: number; + /** + * Output associated with this shell command, if available. May be partial, truncated, or a preview; not guaranteed to be full output. + */ + outputPreview?: string; + /** + * Whether outputPreview is known to be incomplete or truncated + */ + outputTruncated?: boolean; + /** + * Shell id, as assigned by Copilot runtime + */ + shellId: string; + /** + * Content block type discriminator + */ + type: "shell_exit"; +} /** * Image content block with base64-encoded data */ diff --git a/python/copilot/generated/rpc.py b/python/copilot/generated/rpc.py index 212865e17e..1d3a7b2467 100644 --- a/python/copilot/generated/rpc.py +++ b/python/copilot/generated/rpc.py @@ -6,7 +6,7 @@ from typing import ClassVar, TYPE_CHECKING -from .session_events import AbortReason, Attachment, ContextTier, EmbeddedBlobResourceContents, EmbeddedTextResourceContents, McpServerSource, McpServerStatus, PermissionPromptRequest, PermissionRule, ReasoningSummary, ResponseBudgetConfig, SessionEvent, SessionMode, ShutdownType, SkillSource, UserToolSessionApproval +from .session_events import AbortReason, Attachment, ContextTier, EmbeddedBlobResourceContents, EmbeddedTextResourceContents, McpServerSource, McpServerStatus, PermissionPromptRequest, PermissionRule, ReasoningSummary, ResponseLimitsConfig, SessionEvent, SessionMode, ShutdownType, SkillSource, UserToolSessionApproval if TYPE_CHECKING: from .._jsonrpc import JsonRpcClient @@ -120,6 +120,7 @@ def to_dict(self) -> dict: result["error"] = from_union([from_str, from_none], self.error) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class CopilotUserResponseEndpoints: """Schema for the `CopilotUserResponseEndpoints` type.""" @@ -162,6 +163,7 @@ class AuthInfoType(Enum): TOKEN = "token" USER = "user" +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class AccountAllUsers: """Schema for the `AccountAllUsers` type. @@ -188,6 +190,7 @@ def to_dict(self) -> dict: result["token"] = from_union([from_str, from_none], self.token) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class AccountGetCurrentAuthResult: """Current authentication state""" @@ -213,6 +216,7 @@ def to_dict(self) -> dict: result["authInfo"] = from_union([lambda x: (x).to_dict(), from_none], self.auth_info) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class AccountGetQuotaRequest: git_hub_token: str | None = None @@ -232,6 +236,7 @@ def to_dict(self) -> dict: result["gitHubToken"] = from_union([from_str, from_none], self.git_hub_token) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class AccountQuotaSnapshot: """Schema for the `AccountQuotaSnapshot` type.""" @@ -286,6 +291,7 @@ def to_dict(self) -> dict: result["resetDate"] = from_union([from_str, from_none], self.reset_date) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class AccountLoginRequest: """Credentials to store after successful authentication""" @@ -314,6 +320,7 @@ def to_dict(self) -> dict: result["token"] = from_str(self.token) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class AccountLoginResult: """Result of a successful login; throws on failure""" @@ -335,6 +342,7 @@ def to_dict(self) -> dict: result["storedInVault"] = from_bool(self.stored_in_vault) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class AccountLogoutRequest: """User to log out""" @@ -353,6 +361,7 @@ def to_dict(self) -> dict: result["authInfo"] = (self.auth_info).to_dict() return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class AccountLogoutResult: """Logout result indicating if more users remain""" @@ -1086,6 +1095,7 @@ def to_dict(self) -> dict: result["sessionId"] = from_str(self.session_id) return result +# Experimental: this type is part of an experimental API and may change or be removed. # Internal: this type is an internal SDK API and is not part of the public surface. @dataclass class _ConnectRequest: @@ -1106,6 +1116,7 @@ def to_dict(self) -> dict: result["token"] = from_union([from_str, from_none], self.token) return result +# Experimental: this type is part of an experimental API and may change or be removed. # Internal: this type is an internal SDK API and is not part of the public surface. @dataclass class _ConnectResult: @@ -1171,6 +1182,7 @@ def to_dict(self) -> dict: result["owner"] = from_str(self.owner) return result +# Experimental: this type is part of an experimental API and may change or be removed. class ContentFilterMode(Enum): """Controls how MCP tool result content is filtered: none leaves content unchanged, markdown sanitizes HTML while preserving Markdown-friendly output, and hidden_characters removes @@ -1186,6 +1198,7 @@ class Host(Enum): class CopilotAPITokenAuthInfoType(Enum): COPILOT_API_TOKEN = "copilot-api-token" +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class CopilotUserResponseQuotaSnapshotsChat: """Schema for the `CopilotUserResponseQuotaSnapshotsChat` type.""" @@ -1248,6 +1261,7 @@ def to_dict(self) -> dict: result["unlimited"] = from_union([from_bool, from_none], self.unlimited) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class CopilotUserResponseQuotaSnapshotsCompletions: """Schema for the `CopilotUserResponseQuotaSnapshotsCompletions` type.""" @@ -1310,6 +1324,7 @@ def to_dict(self) -> dict: result["unlimited"] = from_union([from_bool, from_none], self.unlimited) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class CopilotUserResponseQuotaSnapshotsPremiumInteractions: """Schema for the `CopilotUserResponseQuotaSnapshotsPremiumInteractions` type.""" @@ -1409,6 +1424,7 @@ def to_dict(self) -> dict: result["reasoningEffort"] = from_union([from_str, from_none], self.reasoning_effort) return result +# Experimental: this type is part of an experimental API and may change or be removed. class DiscoveredMCPServerType(Enum): """Server transport type: stdio, http, sse (deprecated), or memory""" @@ -1658,6 +1674,7 @@ class ExternalToolTextResultForLlmContentType(Enum): IMAGE = "image" RESOURCE = "resource" RESOURCE_LINK = "resource_link" + SHELL_EXIT = "shell_exit" TERMINAL = "terminal" TEXT = "text" @@ -1673,6 +1690,9 @@ class ExternalToolTextResultForLlmContentResourceType(Enum): class ExternalToolTextResultForLlmContentResourceLinkType(Enum): RESOURCE_LINK = "resource_link" +class ExternalToolTextResultForLlmContentShellExitType(Enum): + SHELL_EXIT = "shell_exit" + class ExternalToolTextResultForLlmContentTerminalType(Enum): TERMINAL = "terminal" @@ -2745,6 +2765,7 @@ def to_dict(self) -> dict: result["cancelled"] = from_bool(self.cancelled) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class MCPServerAuthConfigRedirectPort: """Authentication settings with optional redirect port configuration.""" @@ -2764,6 +2785,7 @@ def to_dict(self) -> dict: result["redirectPort"] = from_union([from_int, from_none], self.redirect_port) return result +# Experimental: this type is part of an experimental API and may change or be removed. class MCPServerConfigDeferTools(Enum): """Controls if tools provided by this server can be loaded on demand via tool search (auto) or always included in the initial tool list (never) @@ -2783,12 +2805,14 @@ class MCPGrantType(Enum): AUTHORIZATION_CODE = "authorization_code" CLIENT_CREDENTIALS = "client_credentials" +# Experimental: this type is part of an experimental API and may change or be removed. class MCPServerConfigHTTPType(Enum): """Remote transport type. Defaults to "http" when omitted.""" HTTP = "http" SSE = "sse" +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class MCPConfigDisableRequest: """MCP server names to disable for new sessions.""" @@ -2810,6 +2834,7 @@ def to_dict(self) -> dict: result["names"] = from_list(from_str, self.names) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class MCPConfigEnableRequest: """MCP server names to enable for new sessions.""" @@ -2830,6 +2855,7 @@ def to_dict(self) -> dict: result["names"] = from_list(from_str, self.names) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class MCPConfigRemoveRequest: """MCP server name to remove from user configuration.""" @@ -2908,6 +2934,7 @@ def to_dict(self) -> dict: result["serverName"] = from_str(self.server_name) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class MCPDiscoverRequest: """Optional working directory used as context for MCP server discovery.""" @@ -3555,6 +3582,7 @@ def to_dict(self) -> dict: result["mode"] = to_enum(SessionMode, self.mode) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ModelBillingTokenPricesLongContext: """Long context tier pricing (available for models with extended context windows)""" @@ -3612,6 +3640,7 @@ def to_dict(self) -> dict: result["outputPrice"] = from_union([to_float, from_none], self.output_price) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ModelCapabilitiesLimitsVision: """Vision-specific limits""" @@ -3640,6 +3669,7 @@ def to_dict(self) -> dict: result["supported_media_types"] = from_list(from_str, self.supported_media_types) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ModelCapabilitiesSupports: """Feature flags indicating what the model supports""" @@ -3665,6 +3695,7 @@ def to_dict(self) -> dict: result["vision"] = from_union([from_bool, from_none], self.vision) return result +# Experimental: this type is part of an experimental API and may change or be removed. class ModelPickerPriceCategory(Enum): """Relative cost tier for token-based billing users""" @@ -3673,6 +3704,7 @@ class ModelPickerPriceCategory(Enum): MEDIUM = "medium" VERY_HIGH = "very_high" +# Experimental: this type is part of an experimental API and may change or be removed. class ModelPolicyState(Enum): """Current policy state for this model""" @@ -3820,6 +3852,7 @@ def to_dict(self) -> dict: result["modelId"] = from_union([from_str, from_none], self.model_id) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ModelsListRequest: git_hub_token: str | None = None @@ -4786,6 +4819,7 @@ def to_dict(self) -> dict: result["success"] = from_bool(self.success) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class PingRequest: """Optional message to echo back to the caller.""" @@ -4805,6 +4839,7 @@ def to_dict(self) -> dict: result["message"] = from_union([from_str, from_none], self.message) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class PingResult: """Server liveness response, including the echoed message, current server timestamp, and @@ -6049,6 +6084,7 @@ def to_dict(self) -> dict: result["id"] = from_int(self.id) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class SecretsAddFilterValuesRequest: """Secret values to add to the redaction filter.""" @@ -6067,6 +6103,7 @@ def to_dict(self) -> dict: result["values"] = from_list(from_str, self.values) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class SecretsAddFilterValuesResult: """Confirmation that the secret values were registered.""" @@ -6152,6 +6189,7 @@ def to_dict(self) -> dict: result["messageId"] = from_str(self.message_id) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ServerSkill: """Schema for the `ServerSkill` type.""" @@ -6536,6 +6574,7 @@ def to_dict(self) -> dict: result["recursive"] = from_union([from_bool, from_none], self.recursive) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class SessionFSSetProviderCapabilities: """Optional capabilities declared by the provider""" @@ -6555,12 +6594,14 @@ def to_dict(self) -> dict: result["sqlite"] = from_union([from_bool, from_none], self.sqlite) return result +# Experimental: this type is part of an experimental API and may change or be removed. class SessionFSSetProviderConventions(Enum): """Path conventions used by this filesystem""" POSIX = "posix" WINDOWS = "windows" +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class SessionFSSetProviderResult: """Indicates whether the calling client was registered as the session filesystem provider.""" @@ -7022,6 +7063,24 @@ def to_dict(self) -> dict: result["success"] = from_bool(self.success) return result +# Experimental: this type is part of an experimental API and may change or be removed. +class SessionVisibilityStatus(Enum): + """Sharing status for a synced session. "repo" makes the session visible to anyone with read + access to the repository; "unshared" restricts it to the creator and collaborators. + + Current sharing status. Absent when the session is not synced or the status could not be + retrieved (e.g. the user is not authenticated). + + Sharing status to apply. "repo" makes the session visible to repository readers; + "unshared" restricts it to the creator and collaborators. + + Effective sharing status after the update. May differ from the requested status for task + types that are already visible to repository readers by default. Absent when the update + could not be applied (e.g. the session is not synced or the user is not authenticated). + """ + REPO = "repo" + UNSHARED = "unshared" + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class SessionsBulkDeleteRequest: @@ -7936,6 +7995,7 @@ def to_dict(self) -> dict: result["name"] = from_str(self.name) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class SkillsDiscoverRequest: """Optional project paths and additional skill directories to include in discovery.""" @@ -8566,6 +8626,7 @@ def to_dict(self) -> dict: class TokenAuthInfoType(Enum): TOKEN = "token" +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class Tool: """Schema for the `Tool` type.""" @@ -8624,6 +8685,7 @@ def to_dict(self) -> dict: result: dict = {} return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ToolsListRequest: """Optional model identifier whose tool overrides should be applied to the listing.""" @@ -9092,6 +9154,81 @@ def to_dict(self) -> dict: class UserAuthInfoType(Enum): USER = "user" +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class UserSettingMetadata: + """A single user setting's effective value alongside its default, so consumers can render + settings left at their default. + """ + default: Any + """The centrally-known default for this setting (null when no default is registered).""" + + is_default: bool + """True when the user has not set an explicit value for this setting (i.e. it is left at its + default). Reflects whether the user has overridden the key, not whether the effective + value happens to equal the default — a key explicitly set to a value identical to the + default still reports false. + """ + value: Any + """The effective value: the user's value if set, otherwise the default.""" + + @staticmethod + def from_dict(obj: Any) -> 'UserSettingMetadata': + assert isinstance(obj, dict) + default = obj.get("default") + is_default = from_bool(obj.get("isDefault")) + value = obj.get("value") + return UserSettingMetadata(default, is_default, value) + + def to_dict(self) -> dict: + result: dict = {} + result["default"] = self.default + result["isDefault"] = from_bool(self.is_default) + result["value"] = self.value + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class UserSettingsSetRequest: + """Partial user settings to write to settings.json. Each top-level key is written + individually, replacing the existing value; a key whose value is null is removed. + """ + settings: Any + """Partial user settings to write, as a free-form object keyed by setting name""" + + @staticmethod + def from_dict(obj: Any) -> 'UserSettingsSetRequest': + assert isinstance(obj, dict) + settings = obj.get("settings") + return UserSettingsSetRequest(settings) + + def to_dict(self) -> dict: + result: dict = {} + result["settings"] = self.settings + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class UserSettingsSetResult: + """Outcome of writing user settings.""" + + shadowed_keys: list[str] + """Top-level keys whose write landed in settings.json but is shadowed by a value still + present in the legacy config.json (config.json wins on read). The write does not take + effect until the legacy value is removed. + """ + + @staticmethod + def from_dict(obj: Any) -> 'UserSettingsSetResult': + assert isinstance(obj, dict) + shadowed_keys = from_list(from_str, obj.get("shadowedKeys")) + return UserSettingsSetResult(shadowed_keys) + + def to_dict(self) -> dict: + result: dict = {} + result["shadowedKeys"] = from_list(from_str, self.shadowed_keys) + return result + # Experimental: this type is part of an experimental API and may change or be removed. class WorkspaceDiffFileChangeType(Enum): """Type of change represented by this file diff.""" @@ -9353,6 +9490,7 @@ def to_dict(self) -> dict: result["statusMessage"] = from_union([from_str, from_none], self.status_message) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class AccountGetQuotaResult: """Quota usage snapshots for the resolved user, keyed by quota type.""" @@ -9744,6 +9882,7 @@ def to_dict(self) -> dict: result["canvases"] = from_union([from_bool, from_none], self.canvases) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class CopilotUserResponseQuotaSnapshots: """Schema for the `CopilotUserResponseQuotaSnapshotsChat` type. @@ -10136,6 +10275,53 @@ 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 ExternalToolTextResultForLlmContentShellExit: + """Shell command exit metadata with optional output preview""" + + exit_code: int + """Exit code from the completed shell command""" + + shell_id: str + """Shell id, as assigned by Copilot runtime""" + + type: ClassVar[str] = "shell_exit" + """Content block type discriminator""" + + cwd: str | None = None + """Working directory where the shell command was executed""" + + output_preview: str | None = None + """Output associated with this shell command, if available. May be partial, truncated, or a + preview; not guaranteed to be full output. + """ + output_truncated: bool | None = None + """Whether outputPreview is known to be incomplete or truncated""" + + @staticmethod + def from_dict(obj: Any) -> 'ExternalToolTextResultForLlmContentShellExit': + assert isinstance(obj, dict) + exit_code = from_int(obj.get("exitCode")) + shell_id = from_str(obj.get("shellId")) + cwd = from_union([from_str, from_none], obj.get("cwd")) + output_preview = from_union([from_str, from_none], obj.get("outputPreview")) + output_truncated = from_union([from_bool, from_none], obj.get("outputTruncated")) + return ExternalToolTextResultForLlmContentShellExit(exit_code, shell_id, cwd, output_preview, output_truncated) + + def to_dict(self) -> dict: + result: dict = {} + result["exitCode"] = from_int(self.exit_code) + result["shellId"] = from_str(self.shell_id) + result["type"] = self.type + if self.cwd is not None: + result["cwd"] = from_union([from_str, from_none], self.cwd) + if self.output_preview is not None: + result["outputPreview"] = from_union([from_str, from_none], self.output_preview) + if self.output_truncated is not None: + result["outputTruncated"] = from_union([from_bool, from_none], self.output_truncated) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ExternalToolTextResultForLlmContentTerminal: @@ -11127,6 +11313,7 @@ def to_dict(self) -> dict: result["contents"] = from_list(lambda x: to_class(MCPAppsResourceContent, x), self.contents) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class MCPServerConfigStdio: """Stdio MCP server configuration launched as a child process.""" @@ -11285,6 +11472,7 @@ def to_dict(self) -> dict: result["publicClient"] = from_union([from_bool, from_none], self.public_client) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class MCPServerConfig: """MCP server configuration (stdio process or remote HTTP/SSE) @@ -11407,6 +11595,7 @@ def to_dict(self) -> dict: result["url"] = from_union([from_str, from_none], self.url) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class MCPServerConfigHTTP: """Remote MCP server configuration accessed over HTTP or SSE.""" @@ -11776,6 +11965,7 @@ def to_dict(self) -> dict: result["taskType"] = from_union([lambda x: to_enum(TaskType, x), from_none], self.task_type) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ModelBillingTokenPrices: """Token-level pricing information for this model""" @@ -11845,6 +12035,7 @@ def to_dict(self) -> dict: result["outputPrice"] = from_union([to_float, from_none], self.output_price) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ModelCapabilitiesLimits: """Token limits for prompts, outputs, and context window""" @@ -11882,6 +12073,7 @@ def to_dict(self) -> dict: result["vision"] = from_union([lambda x: to_class(ModelCapabilitiesLimitsVision, x), from_none], self.vision) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ModelPolicy: """Policy state (if applicable)""" @@ -13271,6 +13463,7 @@ def to_dict(self) -> dict: result["rows"] = from_list(lambda x: to_class(PlanSQLTodosRow, x), self.rows) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class DiscoveredMCPServer: """Schema for the `DiscoveredMcpServer` type.""" @@ -14635,6 +14828,7 @@ def to_dict(self) -> dict: result["wait"] = from_union([from_bool, from_none], self.wait) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ServerSkillList: """Skills discovered across global and project sources.""" @@ -14678,6 +14872,7 @@ def to_dict(self) -> dict: result["message"] = from_union([from_str, from_none], self.message) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class SessionFSSetProviderRequest: """Initial working directory, session-state path layout, and path conventions used to @@ -14862,6 +15057,98 @@ def to_dict(self) -> dict: result["throwOnError"] = from_union([from_bool, from_none], self.throw_on_error) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class VisibilityGetResult: + """Current sharing status and shareable GitHub URL for a session.""" + + synced: bool + """Whether the session has been synced to Mission Control (i.e. has a GitHub task). When + false, the session cannot be shared and `status`/`shareUrl` are absent. + """ + share_url: str | None = None + """Shareable GitHub URL for the session. Present when the session is synced and the URL can + be resolved. + """ + status: SessionVisibilityStatus | None = None + """Current sharing status. Absent when the session is not synced or the status could not be + retrieved (e.g. the user is not authenticated). + """ + + @staticmethod + def from_dict(obj: Any) -> 'VisibilityGetResult': + assert isinstance(obj, dict) + synced = from_bool(obj.get("synced")) + share_url = from_union([from_str, from_none], obj.get("shareUrl")) + status = from_union([SessionVisibilityStatus, from_none], obj.get("status")) + return VisibilityGetResult(synced, share_url, status) + + def to_dict(self) -> dict: + result: dict = {} + result["synced"] = from_bool(self.synced) + if self.share_url is not None: + result["shareUrl"] = from_union([from_str, from_none], self.share_url) + if self.status is not None: + result["status"] = from_union([lambda x: to_enum(SessionVisibilityStatus, x), from_none], self.status) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class VisibilitySetRequest: + """Desired sharing status for the session.""" + + status: SessionVisibilityStatus + """Sharing status to apply. "repo" makes the session visible to repository readers; + "unshared" restricts it to the creator and collaborators. + """ + + @staticmethod + def from_dict(obj: Any) -> 'VisibilitySetRequest': + assert isinstance(obj, dict) + status = SessionVisibilityStatus(obj.get("status")) + return VisibilitySetRequest(status) + + def to_dict(self) -> dict: + result: dict = {} + result["status"] = to_enum(SessionVisibilityStatus, self.status) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class VisibilitySetResult: + """Effective sharing status and shareable GitHub URL after updating session visibility.""" + + synced: bool + """Whether the session has been synced to Mission Control (i.e. has a GitHub task). When + false, the visibility change could not be applied and `status`/`shareUrl` are absent. + """ + share_url: str | None = None + """Shareable GitHub URL for the session. Present when the session is synced and the URL can + be resolved. + """ + status: SessionVisibilityStatus | None = None + """Effective sharing status after the update. May differ from the requested status for task + types that are already visible to repository readers by default. Absent when the update + could not be applied (e.g. the session is not synced or the user is not authenticated). + """ + + @staticmethod + def from_dict(obj: Any) -> 'VisibilitySetResult': + assert isinstance(obj, dict) + synced = from_bool(obj.get("synced")) + share_url = from_union([from_str, from_none], obj.get("shareUrl")) + status = from_union([SessionVisibilityStatus, from_none], obj.get("status")) + return VisibilitySetResult(synced, share_url, status) + + def to_dict(self) -> dict: + result: dict = {} + result["synced"] = from_bool(self.synced) + if self.share_url is not None: + result["shareUrl"] = from_union([from_str, from_none], self.share_url) + if self.status is not None: + result["status"] = from_union([lambda x: to_enum(SessionVisibilityStatus, x), from_none], self.status) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class SessionsOpenAttach: @@ -15017,6 +15304,7 @@ def to_dict(self) -> dict: result["skills"] = from_list(lambda x: to_class(Skill, x), self.skills) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class SkillsConfigSetDisabledSkillsRequest: """Skill names to mark as disabled in global configuration, replacing any previous list.""" @@ -15553,6 +15841,7 @@ def to_dict(self) -> dict: result["result"] = from_union([from_str, from_none], self.result) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ToolList: """Built-in tools available for the requested model, with their parameters and instructions.""" @@ -16067,6 +16356,29 @@ def to_dict(self) -> dict: result["totalNanoAiu"] = from_union([to_float, from_none], self.total_nano_aiu) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class UserSettingsGetResult: + """Per-key metadata for every known user setting (settings.json overlaid with the legacy + config.json, config.json wins), including settings left at their default. Excludes + repository- and enterprise-managed overrides. + """ + settings: dict[str, UserSettingMetadata] + """Every known user setting keyed by setting name, each with its effective value, default, + and whether it is at the default. + """ + + @staticmethod + def from_dict(obj: Any) -> 'UserSettingsGetResult': + assert isinstance(obj, dict) + settings = from_dict(UserSettingMetadata.from_dict, obj.get("settings")) + return UserSettingsGetResult(settings) + + def to_dict(self) -> dict: + result: dict = {} + result["settings"] = from_dict(lambda x: to_class(UserSettingMetadata, x), self.settings) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class WorkspaceDiffFileChange: @@ -16328,6 +16640,7 @@ def to_dict(self) -> dict: result["capabilities"] = from_union([lambda x: to_class(CanvasHostContextCapabilities, x), from_none], self.capabilities) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class CopilotUserResponse: """Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the @@ -17214,6 +17527,7 @@ def to_dict(self) -> dict: result["context"] = to_class(MCPAppsSetHostContextDetails, self.context) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class MCPConfigAddRequest: """MCP server name and configuration to add to user configuration.""" @@ -17237,6 +17551,7 @@ def to_dict(self) -> dict: result["name"] = from_str(self.name) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class MCPConfigList: """User-configured MCP servers, keyed by server name.""" @@ -17255,6 +17570,7 @@ def to_dict(self) -> dict: result["servers"] = from_dict(lambda x: to_class(MCPServerConfig, x), self.servers) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class MCPConfigUpdateRequest: """MCP server name and replacement configuration to write to user configuration.""" @@ -17378,6 +17694,7 @@ def to_dict(self) -> dict: result["result"] = to_class(MCPOauthPendingRequestResponse, self.result) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ModelBilling: """Billing information""" @@ -17522,6 +17839,7 @@ 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 MCPDiscoverResult: """MCP servers discovered from user, workspace, plugin, and built-in sources.""" @@ -18796,6 +19114,7 @@ def to_dict(self) -> dict: result["session"] = from_union([lambda x: to_class(CanvasSessionContext, x), from_none], self.session) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class APIKeyAuthInfo: """Schema for the `ApiKeyAuthInfo` type.""" @@ -18832,6 +19151,7 @@ def to_dict(self) -> dict: result["copilotUser"] = from_union([lambda x: to_class(CopilotUserResponse, x), from_none], self.copilot_user) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class CopilotAPITokenAuthInfo: """Schema for the `CopilotApiTokenAuthInfo` type.""" @@ -18865,6 +19185,7 @@ def to_dict(self) -> dict: result["copilotUser"] = from_union([lambda x: to_class(CopilotUserResponse, x), from_none], self.copilot_user) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class EnvAuthInfo: """Schema for the `EnvAuthInfo` type.""" @@ -18914,6 +19235,7 @@ def to_dict(self) -> dict: result["login"] = from_union([from_str, from_none], self.login) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class GhCLIAuthInfo: """Schema for the `GhCliAuthInfo` type.""" @@ -18955,6 +19277,7 @@ def to_dict(self) -> dict: result["copilotUser"] = from_union([lambda x: to_class(CopilotUserResponse, x), from_none], self.copilot_user) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class HMACAuthInfo: """Schema for the `HMACAuthInfo` type.""" @@ -18991,6 +19314,7 @@ def to_dict(self) -> dict: result["copilotUser"] = from_union([lambda x: to_class(CopilotUserResponse, x), from_none], self.copilot_user) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class TokenAuthInfo: """Schema for the `TokenAuthInfo` type.""" @@ -19027,6 +19351,7 @@ def to_dict(self) -> dict: result["copilotUser"] = from_union([lambda x: to_class(CopilotUserResponse, x), from_none], self.copilot_user) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class UserAuthInfo: """Schema for the `UserAuthInfo` type.""" @@ -19462,6 +19787,9 @@ class SessionMetadataSnapshot: """Remote-session-specific metadata. Populated only when `isRemote` is true. Fields are immutable for the lifetime of the session. """ + response_limits: ResponseLimitsConfig | None = None + """Current response limits for the session, or null when no limits are active""" + selected_model: str | None = None """Currently selected model identifier, if any""" @@ -19492,11 +19820,12 @@ def from_dict(obj: Any) -> 'SessionMetadataSnapshot': client_name = from_union([from_str, from_none], obj.get("clientName")) initial_name = from_union([from_str, from_none], obj.get("initialName")) remote_metadata = from_union([MetadataSnapshotRemoteMetadata.from_dict, from_none], obj.get("remoteMetadata")) + response_limits = from_union([ResponseLimitsConfig.from_dict, from_none], obj.get("responseLimits")) selected_model = from_union([from_str, from_none], obj.get("selectedModel")) summary = from_union([from_str, from_none], obj.get("summary")) workspace = from_union([WorkspaceSummary.from_dict, from_none], obj.get("workspace")) workspace_path = from_union([from_none, from_str], obj.get("workspacePath")) - return SessionMetadataSnapshot(already_in_use, current_mode, is_remote, modified_time, session_id, start_time, working_directory, client_name, initial_name, remote_metadata, selected_model, summary, workspace, workspace_path) + return SessionMetadataSnapshot(already_in_use, current_mode, is_remote, modified_time, session_id, start_time, working_directory, client_name, initial_name, remote_metadata, response_limits, selected_model, summary, workspace, workspace_path) def to_dict(self) -> dict: result: dict = {} @@ -19513,6 +19842,7 @@ def to_dict(self) -> dict: result["initialName"] = from_union([from_str, from_none], self.initial_name) if self.remote_metadata is not None: result["remoteMetadata"] = from_union([lambda x: to_class(MetadataSnapshotRemoteMetadata, x), from_none], self.remote_metadata) + result["responseLimits"] = from_union([lambda x: to_class(ResponseLimitsConfig, x), from_none], self.response_limits) if self.selected_model is not None: result["selectedModel"] = from_union([from_str, from_none], self.selected_model) if self.summary is not None: @@ -19943,8 +20273,8 @@ class SessionOpenOptions: remote_steerable: bool | None = None """Whether this session supports remote steering.""" - response_budget: ResponseBudgetConfig | None = None - """Initial response budget limits for the session.""" + response_limits: ResponseLimitsConfig | None = None + """Initial response limits for the session.""" running_in_interactive_mode: bool | None = None """Whether the host is an interactive UI.""" @@ -20027,7 +20357,7 @@ def from_dict(obj: Any) -> 'SessionOpenOptions': remote_defaulted_on = from_union([from_bool, from_none], obj.get("remoteDefaultedOn")) remote_exporting = from_union([from_bool, from_none], obj.get("remoteExporting")) remote_steerable = from_union([from_bool, from_none], obj.get("remoteSteerable")) - response_budget = from_union([ResponseBudgetConfig.from_dict, from_none], obj.get("responseBudget")) + response_limits = from_union([ResponseLimitsConfig.from_dict, from_none], obj.get("responseLimits")) running_in_interactive_mode = from_union([from_bool, from_none], obj.get("runningInInteractiveMode")) sandbox_config = from_union([SandboxConfig.from_dict, from_none], obj.get("sandboxConfig")) session_capabilities = from_union([lambda x: from_list(SessionCapability, x), from_none], obj.get("sessionCapabilities")) @@ -20039,7 +20369,7 @@ def from_dict(obj: Any) -> 'SessionOpenOptions': trajectory_file = from_union([from_str, from_none], obj.get("trajectoryFile")) 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, 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_on_demand_instruction_discovery, enable_script_safety, enable_streaming, env_value_mode, events_log_directory, excluded_tools, exp_assignments, feature_flags, 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, response_budget, running_in_interactive_mode, sandbox_config, session_capabilities, session_id, shell_init_profile, shell_process_flags, skill_directories, skip_custom_instructions, trajectory_file, working_directory, working_directory_context) + return SessionOpenOptions(additional_content_exclusion_policies, 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_on_demand_instruction_discovery, enable_script_safety, enable_streaming, env_value_mode, events_log_directory, excluded_tools, exp_assignments, feature_flags, 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, response_limits, running_in_interactive_mode, sandbox_config, session_capabilities, session_id, shell_init_profile, shell_process_flags, skill_directories, skip_custom_instructions, trajectory_file, working_directory, working_directory_context) def to_dict(self) -> dict: result: dict = {} @@ -20133,8 +20463,8 @@ def to_dict(self) -> dict: result["remoteExporting"] = from_union([from_bool, from_none], self.remote_exporting) if self.remote_steerable is not None: result["remoteSteerable"] = from_union([from_bool, from_none], self.remote_steerable) - if self.response_budget is not None: - result["responseBudget"] = from_union([lambda x: to_class(ResponseBudgetConfig, x), from_none], self.response_budget) + if self.response_limits is not None: + result["responseLimits"] = from_union([lambda x: to_class(ResponseLimitsConfig, x), from_none], self.response_limits) if self.running_in_interactive_mode is not None: result["runningInInteractiveMode"] = from_union([from_bool, from_none], self.running_in_interactive_mode) if self.sandbox_config is not None: @@ -20297,8 +20627,8 @@ class SessionUpdateOptionsParams: reasoning_summary: ReasoningSummary | None = None """Reasoning summary mode for supported model clients.""" - response_budget: ResponseBudgetConfig | None = None - """Optional response budget limits. Pass null to clear the response budget.""" + response_limits: ResponseLimitsConfig | None = None + """Optional response limits. Pass null to clear the response limits.""" running_in_interactive_mode: bool | None = None """Whether the session is running in an interactive UI.""" @@ -20383,7 +20713,7 @@ def from_dict(obj: Any) -> 'SessionUpdateOptionsParams': provider = from_union([ProviderConfig.from_dict, from_none], obj.get("provider")) reasoning_effort = from_union([from_str, from_none], obj.get("reasoningEffort")) reasoning_summary = from_union([ReasoningSummary, from_none], obj.get("reasoningSummary")) - response_budget = from_union([ResponseBudgetConfig.from_dict, from_none], obj.get("responseBudget")) + response_limits = from_union([ResponseLimitsConfig.from_dict, from_none], obj.get("responseLimits")) running_in_interactive_mode = from_union([from_bool, from_none], obj.get("runningInInteractiveMode")) sandbox_config = from_union([SandboxConfig.from_dict, from_none], obj.get("sandboxConfig")) session_capabilities = from_union([lambda x: from_list(SessionCapability, x), from_none], obj.get("sessionCapabilities")) @@ -20396,7 +20726,7 @@ def from_dict(obj: Any) -> 'SessionUpdateOptionsParams': tool_filter_precedence = from_union([OptionsUpdateToolFilterPrecedence, from_none], obj.get("toolFilterPrecedence")) trajectory_file = from_union([from_str, from_none], obj.get("trajectoryFile")) working_directory = from_union([from_str, from_none], obj.get("workingDirectory")) - return SessionUpdateOptionsParams(additional_content_exclusion_policies, agent_context, allow_all_mcp_server_instructions, ask_user_disabled, available_tools, capi, client_name, coauthor_enabled, context_tier, continue_on_auto_mode, copilot_url, custom_agents_local_only, disabled_instruction_sources, disabled_skills, enable_file_hooks, enable_host_git_operations, enable_on_demand_instruction_discovery, enable_reasoning_summaries, enable_script_safety, enable_session_store, enable_skills, enable_streaming, env_value_mode, events_log_directory, excluded_tools, feature_flags, installed_plugins, integration_id, is_experimental_mode, log_interactive_shells, lsp_client_name, manage_schedule_enabled, max_inline_binary_bytes, model, model_capabilities_overrides, organization_custom_instructions, provider, reasoning_effort, reasoning_summary, response_budget, running_in_interactive_mode, sandbox_config, session_capabilities, shell_init_profile, shell_process_flags, skill_directories, skip_custom_instructions, skip_embedding_retrieval, suppress_custom_agent_prompt, tool_filter_precedence, trajectory_file, working_directory) + return SessionUpdateOptionsParams(additional_content_exclusion_policies, agent_context, allow_all_mcp_server_instructions, ask_user_disabled, available_tools, capi, client_name, coauthor_enabled, context_tier, continue_on_auto_mode, copilot_url, custom_agents_local_only, disabled_instruction_sources, disabled_skills, enable_file_hooks, enable_host_git_operations, enable_on_demand_instruction_discovery, enable_reasoning_summaries, enable_script_safety, enable_session_store, enable_skills, enable_streaming, env_value_mode, events_log_directory, excluded_tools, feature_flags, installed_plugins, integration_id, is_experimental_mode, log_interactive_shells, lsp_client_name, manage_schedule_enabled, max_inline_binary_bytes, model, model_capabilities_overrides, organization_custom_instructions, provider, reasoning_effort, reasoning_summary, response_limits, running_in_interactive_mode, sandbox_config, session_capabilities, shell_init_profile, shell_process_flags, skill_directories, skip_custom_instructions, skip_embedding_retrieval, suppress_custom_agent_prompt, tool_filter_precedence, trajectory_file, working_directory) def to_dict(self) -> dict: result: dict = {} @@ -20478,8 +20808,8 @@ def to_dict(self) -> dict: result["reasoningEffort"] = from_union([from_str, from_none], self.reasoning_effort) if self.reasoning_summary is not None: result["reasoningSummary"] = from_union([lambda x: to_enum(ReasoningSummary, x), from_none], self.reasoning_summary) - if self.response_budget is not None: - result["responseBudget"] = from_union([lambda x: to_class(ResponseBudgetConfig, x), from_none], self.response_budget) + if self.response_limits is not None: + result["responseLimits"] = from_union([lambda x: to_class(ResponseLimitsConfig, x), from_none], self.response_limits) if self.running_in_interactive_mode is not None: result["runningInInteractiveMode"] = from_union([from_bool, from_none], self.running_in_interactive_mode) if self.sandbox_config is not None: @@ -21106,6 +21436,7 @@ def to_dict(self) -> dict: result["modelId"] = from_str(self.model_id) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ModelCapabilities: """Model capabilities and limits""" @@ -21131,6 +21462,7 @@ def to_dict(self) -> dict: result["supports"] = from_union([lambda x: to_class(ModelCapabilitiesSupports, x), from_none], self.supports) return result +# Experimental: this type is part of an experimental API and may change or be removed. class ModelPickerCategory(Enum): """Model capability category for grouping in the model picker""" @@ -21138,6 +21470,7 @@ class ModelPickerCategory(Enum): POWERFUL = "powerful" VERSATILE = "versatile" +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class Model: """Schema for the `Model` type.""" @@ -21202,6 +21535,7 @@ def to_dict(self) -> dict: result["supportedReasoningEfforts"] = from_union([lambda x: from_list(from_str, x), from_none], self.supported_reasoning_efforts) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ModelList: """List of Copilot models available to the resolved user, including capabilities and billing @@ -21739,6 +22073,7 @@ class RPC: external_tool_text_result_for_llm_content_resource_link: ExternalToolTextResultForLlmContentResourceLink external_tool_text_result_for_llm_content_resource_link_icon: ExternalToolTextResultForLlmContentResourceLinkIcon external_tool_text_result_for_llm_content_resource_link_icon_theme: Theme + external_tool_text_result_for_llm_content_shell_exit: ExternalToolTextResultForLlmContentShellExit external_tool_text_result_for_llm_content_terminal: ExternalToolTextResultForLlmContentTerminal external_tool_text_result_for_llm_content_text: ExternalToolTextResultForLlmContentText filter_mapping: dict[str, ContentFilterMode] | ContentFilterMode @@ -22264,6 +22599,7 @@ class RPC: session_telemetry_engagement: SessionTelemetryEngagement session_update_options_params: SessionUpdateOptionsParams session_update_options_result: SessionUpdateOptionsResult + session_visibility_status: SessionVisibilityStatus session_working_directory_context: SessionWorkingDirectoryContext session_working_directory_context_host_type: HostType shell_cancel_user_requested_request: ShellCancelUserRequestedRequest @@ -22380,6 +22716,13 @@ class RPC: usage_metrics_token_detail: UsageMetricsTokenDetail user_auth_info: UserAuthInfo user_requested_shell_command_result: UserRequestedShellCommandResult + user_setting_metadata: UserSettingMetadata + user_settings_get_result: UserSettingsGetResult + user_settings_set_request: UserSettingsSetRequest + user_settings_set_result: UserSettingsSetResult + visibility_get_result: VisibilityGetResult + visibility_set_request: VisibilitySetRequest + visibility_set_result: VisibilitySetResult workspace_diff_file_change: WorkspaceDiffFileChange workspace_diff_file_change_type: WorkspaceDiffFileChangeType workspace_diff_mode: WorkspaceDiffMode @@ -22526,6 +22869,7 @@ def from_dict(obj: Any) -> 'RPC': external_tool_text_result_for_llm_content_resource_link = ExternalToolTextResultForLlmContentResourceLink.from_dict(obj.get("ExternalToolTextResultForLlmContentResourceLink")) external_tool_text_result_for_llm_content_resource_link_icon = ExternalToolTextResultForLlmContentResourceLinkIcon.from_dict(obj.get("ExternalToolTextResultForLlmContentResourceLinkIcon")) external_tool_text_result_for_llm_content_resource_link_icon_theme = Theme(obj.get("ExternalToolTextResultForLlmContentResourceLinkIconTheme")) + external_tool_text_result_for_llm_content_shell_exit = ExternalToolTextResultForLlmContentShellExit.from_dict(obj.get("ExternalToolTextResultForLlmContentShellExit")) external_tool_text_result_for_llm_content_terminal = ExternalToolTextResultForLlmContentTerminal.from_dict(obj.get("ExternalToolTextResultForLlmContentTerminal")) external_tool_text_result_for_llm_content_text = ExternalToolTextResultForLlmContentText.from_dict(obj.get("ExternalToolTextResultForLlmContentText")) filter_mapping = from_union([lambda x: from_dict(ContentFilterMode, x), ContentFilterMode], obj.get("FilterMapping")) @@ -23051,6 +23395,7 @@ def from_dict(obj: Any) -> 'RPC': session_telemetry_engagement = SessionTelemetryEngagement.from_dict(obj.get("SessionTelemetryEngagement")) session_update_options_params = SessionUpdateOptionsParams.from_dict(obj.get("SessionUpdateOptionsParams")) session_update_options_result = SessionUpdateOptionsResult.from_dict(obj.get("SessionUpdateOptionsResult")) + session_visibility_status = SessionVisibilityStatus(obj.get("SessionVisibilityStatus")) session_working_directory_context = SessionWorkingDirectoryContext.from_dict(obj.get("SessionWorkingDirectoryContext")) session_working_directory_context_host_type = HostType(obj.get("SessionWorkingDirectoryContextHostType")) shell_cancel_user_requested_request = ShellCancelUserRequestedRequest.from_dict(obj.get("ShellCancelUserRequestedRequest")) @@ -23167,6 +23512,13 @@ def from_dict(obj: Any) -> 'RPC': usage_metrics_token_detail = UsageMetricsTokenDetail.from_dict(obj.get("UsageMetricsTokenDetail")) user_auth_info = UserAuthInfo.from_dict(obj.get("UserAuthInfo")) user_requested_shell_command_result = UserRequestedShellCommandResult.from_dict(obj.get("UserRequestedShellCommandResult")) + user_setting_metadata = UserSettingMetadata.from_dict(obj.get("UserSettingMetadata")) + user_settings_get_result = UserSettingsGetResult.from_dict(obj.get("UserSettingsGetResult")) + user_settings_set_request = UserSettingsSetRequest.from_dict(obj.get("UserSettingsSetRequest")) + user_settings_set_result = UserSettingsSetResult.from_dict(obj.get("UserSettingsSetResult")) + visibility_get_result = VisibilityGetResult.from_dict(obj.get("VisibilityGetResult")) + visibility_set_request = VisibilitySetRequest.from_dict(obj.get("VisibilitySetRequest")) + visibility_set_result = VisibilitySetResult.from_dict(obj.get("VisibilitySetResult")) workspace_diff_file_change = WorkspaceDiffFileChange.from_dict(obj.get("WorkspaceDiffFileChange")) workspace_diff_file_change_type = WorkspaceDiffFileChangeType(obj.get("WorkspaceDiffFileChangeType")) workspace_diff_mode = WorkspaceDiffMode(obj.get("WorkspaceDiffMode")) @@ -23189,7 +23541,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, agent_discovery_path, agent_discovery_path_list, agent_discovery_path_scope, agent_get_current_result, agent_info, agent_info_source, agent_list, agent_registry_live_target_entry, agent_registry_live_target_entry_attention_kind, agent_registry_live_target_entry_kind, agent_registry_live_target_entry_last_terminal_event, agent_registry_live_target_entry_status, agent_registry_log_capture, agent_registry_log_capture_open_error_reason, agent_registry_spawn_error, agent_registry_spawn_permission_mode, agent_registry_spawn_registry_timeout, agent_registry_spawn_request, agent_registry_spawn_result, agent_registry_spawn_spawned, agent_registry_spawn_validation_error, agent_registry_spawn_validation_error_field, agent_registry_spawn_validation_error_reason, agent_reload_result, agents_discover_request, agent_select_request, agent_select_result, agents_get_discovery_paths_request, allow_all_permission_set_result, allow_all_permission_state, api_key_auth_info, auth_info, auth_info_type, cancel_user_requested_shell_command_result, canvas_action, canvas_action_invoke_request, canvas_action_invoke_result, canvas_close_request, canvas_host_context, canvas_host_context_capabilities, canvas_json_schema, canvas_list, canvas_list_open_result, canvas_open_request, canvas_provider_close_request, canvas_provider_invoke_action_request, canvas_provider_open_request, canvas_provider_open_result, canvas_session_context, capi_session_options, command_list, commands_handle_pending_command_request, commands_handle_pending_command_result, commands_invoke_request, commands_list_request, commands_respond_to_queued_command_request, commands_respond_to_queued_command_result, configure_session_extensions_params, connected_remote_session_metadata, connected_remote_session_metadata_kind, connected_remote_session_metadata_repository, connect_remote_session_params, connect_request, connect_result, content_filter_mode, 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, discovered_canvas, discovered_mcp_server, discovered_mcp_server_type, enqueue_command_params, enqueue_command_result, env_auth_info, event_log_read_request, event_log_release_interest_result, event_log_tail_result, event_log_types, events_agent_scope, events_cursor_status, events_read_result, execute_command_params, execute_command_result, extension, extension_context_push_input, extension_list, extensions_disable_request, extensions_enable_request, extension_source, extension_status, external_tool_result, external_tool_text_result_for_llm, external_tool_text_result_for_llm_binary_results_for_llm, external_tool_text_result_for_llm_binary_results_for_llm_type, external_tool_text_result_for_llm_content, external_tool_text_result_for_llm_content_audio, external_tool_text_result_for_llm_content_image, external_tool_text_result_for_llm_content_resource, external_tool_text_result_for_llm_content_resource_details, external_tool_text_result_for_llm_content_resource_link, external_tool_text_result_for_llm_content_resource_link_icon, external_tool_text_result_for_llm_content_resource_link_icon_theme, external_tool_text_result_for_llm_content_terminal, external_tool_text_result_for_llm_content_text, filter_mapping, fleet_start_request, fleet_start_result, folder_trust_add_params, folder_trust_check_params, folder_trust_check_result, gh_cli_auth_info, handle_pending_tool_call_request, handle_pending_tool_call_result, history_abort_manual_compaction_result, history_cancel_background_compaction_result, history_compact_context_window, history_compact_request, history_compact_result, history_summarize_for_handoff_result, history_truncate_request, history_truncate_result, hmac_auth_info, installed_plugin, installed_plugin_info, installed_plugin_source, installed_plugin_source_git_hub, installed_plugin_source_local, installed_plugin_source_url, instruction_discovery_path, instruction_discovery_path_kind, instruction_discovery_path_list, instruction_discovery_path_location, instructions_discover_request, instructions_get_discovery_paths_request, instructions_get_sources_result, instruction_source, instruction_source_location, instruction_source_type, llm_inference_headers, llm_inference_http_request_chunk_request, llm_inference_http_request_chunk_result, llm_inference_http_request_start_request, llm_inference_http_request_start_result, llm_inference_http_request_start_transport, llm_inference_http_response_chunk_error, llm_inference_http_response_chunk_request, llm_inference_http_response_chunk_result, llm_inference_http_response_start_request, llm_inference_http_response_start_result, llm_inference_set_provider_result, local_session_metadata_value, log_request, log_result, lsp_initialize_request, marketplace_add_result, marketplace_browse_result, marketplace_info, marketplace_list_result, marketplace_plugin_info, marketplace_refresh_entry, marketplace_refresh_result, marketplace_remove_result, mcp_allowed_server, mcp_apps_call_tool_request, mcp_apps_diagnose_capability, mcp_apps_diagnose_request, mcp_apps_diagnose_result, mcp_apps_diagnose_server, mcp_apps_host_context, mcp_apps_host_context_details, mcp_apps_host_context_details_available_display_mode, mcp_apps_host_context_details_display_mode, mcp_apps_host_context_details_platform, mcp_apps_host_context_details_theme, mcp_apps_list_tools_request, mcp_apps_list_tools_result, mcp_apps_read_resource_request, mcp_apps_read_resource_result, mcp_apps_resource_content, mcp_apps_set_host_context_details, mcp_apps_set_host_context_details_available_display_mode, mcp_apps_set_host_context_details_display_mode, mcp_apps_set_host_context_details_platform, mcp_apps_set_host_context_details_theme, mcp_apps_set_host_context_request, mcp_cancel_sampling_execution_params, mcp_cancel_sampling_execution_result, mcp_config_add_request, mcp_config_disable_request, mcp_config_enable_request, mcp_config_list, mcp_config_remove_request, mcp_config_update_request, mcp_configure_git_hub_request, mcp_configure_git_hub_result, mcp_disable_request, mcp_discover_request, mcp_discover_result, mcp_enable_request, mcp_execute_sampling_params, mcp_execute_sampling_request, mcp_execute_sampling_result, mcp_filtered_server, mcp_headers_handle_pending_headers_refresh_request, mcp_headers_handle_pending_headers_refresh_request_request, mcp_headers_handle_pending_headers_refresh_request_result, mcp_host_state, mcp_is_server_running_request, mcp_is_server_running_result, mcp_list_tools_request, mcp_list_tools_result, mcp_oauth_handle_pending_request, mcp_oauth_handle_pending_result, mcp_oauth_login_grant_type, mcp_oauth_login_request, mcp_oauth_login_result, mcp_oauth_pending_request_response, mcp_oauth_respond_request, mcp_oauth_respond_result, mcp_register_external_client_request, mcp_reload_with_config_request, mcp_remove_git_hub_result, mcp_restart_server_request, mcp_sampling_execution_action, mcp_sampling_execution_result, mcp_server, mcp_server_auth_config, mcp_server_auth_config_redirect_port, mcp_server_config, mcp_server_config_defer_tools, mcp_server_config_http, mcp_server_config_http_oauth_grant_type, mcp_server_config_http_type, mcp_server_config_stdio, mcp_server_failure_info, mcp_server_list, mcp_server_needs_auth_info, mcp_set_env_value_mode_details, mcp_set_env_value_mode_params, mcp_set_env_value_mode_result, mcp_start_server_request, mcp_start_servers_result, mcp_stop_server_request, mcp_tools, mcp_unregister_external_client_request, memory_configuration, metadata_context_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_token_prices, model_billing_token_prices_long_context, model_capabilities, model_capabilities_limits, model_capabilities_limits_vision, model_capabilities_override, model_capabilities_override_limits, model_capabilities_override_limits_vision, model_capabilities_override_supports, model_capabilities_supports, model_list, model_list_request, model_picker_category, model_picker_price_category, model_policy, model_policy_state, model_set_reasoning_effort_request, model_set_reasoning_effort_result, models_list_request, model_switch_to_request, model_switch_to_result, mode_set_request, named_provider_config, name_get_result, name_set_auto_request, name_set_auto_result, name_set_request, open_canvas_instance, options_update_additional_content_exclusion_policy, options_update_additional_content_exclusion_policy_rule, options_update_additional_content_exclusion_policy_rule_source, options_update_additional_content_exclusion_policy_scope, options_update_context_tier, options_update_env_value_mode, options_update_reasoning_summary, options_update_tool_filter_precedence, pending_permission_request, pending_permission_request_list, permission_decision, permission_decision_approved, permission_decision_approved_for_location, permission_decision_approved_for_session, permission_decision_approve_for_location, permission_decision_approve_for_location_approval, permission_decision_approve_for_location_approval_commands, permission_decision_approve_for_location_approval_custom_tool, permission_decision_approve_for_location_approval_extension_management, permission_decision_approve_for_location_approval_extension_permission_access, permission_decision_approve_for_location_approval_mcp, permission_decision_approve_for_location_approval_mcp_sampling, permission_decision_approve_for_location_approval_memory, permission_decision_approve_for_location_approval_read, permission_decision_approve_for_location_approval_write, permission_decision_approve_for_session, permission_decision_approve_for_session_approval, permission_decision_approve_for_session_approval_commands, permission_decision_approve_for_session_approval_custom_tool, permission_decision_approve_for_session_approval_extension_management, permission_decision_approve_for_session_approval_extension_permission_access, permission_decision_approve_for_session_approval_mcp, permission_decision_approve_for_session_approval_mcp_sampling, permission_decision_approve_for_session_approval_memory, permission_decision_approve_for_session_approval_read, permission_decision_approve_for_session_approval_write, permission_decision_approve_once, permission_decision_approve_permanently, permission_decision_cancelled, permission_decision_denied_by_content_exclusion_policy, permission_decision_denied_by_permission_request_hook, permission_decision_denied_by_rules, permission_decision_denied_interactively_by_user, permission_decision_denied_no_approval_rule_and_could_not_request_from_user, permission_decision_reject, permission_decision_request, permission_decision_user_not_available, permission_location_add_tool_approval_params, permission_location_apply_params, permission_location_apply_result, permission_location_resolve_params, permission_location_resolve_result, permission_location_type, permission_paths_add_params, permission_paths_allowed_check_params, permission_paths_allowed_check_result, permission_paths_config, permission_paths_list, permission_paths_update_primary_params, permission_paths_workspace_check_params, permission_paths_workspace_check_result, permission_prompt_shown_notification, permission_request_result, permission_rules_set, permissions_configure_additional_content_exclusion_policy, permissions_configure_additional_content_exclusion_policy_rule, permissions_configure_additional_content_exclusion_policy_rule_source, permissions_configure_additional_content_exclusion_policy_scope, permissions_configure_params, permissions_configure_result, permissions_folder_trust_add_trusted_result, permissions_get_allow_all_request, permissions_locations_add_tool_approval_details, permissions_locations_add_tool_approval_details_commands, permissions_locations_add_tool_approval_details_custom_tool, permissions_locations_add_tool_approval_details_extension_management, permissions_locations_add_tool_approval_details_extension_permission_access, permissions_locations_add_tool_approval_details_mcp, permissions_locations_add_tool_approval_details_mcp_sampling, permissions_locations_add_tool_approval_details_memory, permissions_locations_add_tool_approval_details_read, permissions_locations_add_tool_approval_details_write, permissions_locations_add_tool_approval_result, permissions_modify_rules_params, permissions_modify_rules_result, permissions_modify_rules_scope, permissions_notify_prompt_shown_result, permissions_paths_add_result, permissions_paths_list_request, permissions_paths_update_primary_result, permissions_pending_requests_request, permissions_reset_session_approvals_request, permissions_reset_session_approvals_result, permissions_set_allow_all_request, permissions_set_allow_all_source, permissions_set_approve_all_request, permissions_set_approve_all_result, permissions_set_approve_all_source, permissions_set_required_request, permissions_set_required_result, permissions_urls_set_unrestricted_mode_result, permission_urls_config, permission_urls_set_unrestricted_mode_params, ping_request, ping_result, plan_read_result, plan_read_sql_todos_result, plan_read_sql_todos_with_dependencies_result, plan_sql_todo_dependency, plan_sql_todos_row, plan_update_request, plugin, plugin_install_result, plugin_list, plugin_list_result, plugins_disable_request, plugins_enable_request, plugins_install_request, plugins_marketplaces_add_request, plugins_marketplaces_browse_request, plugins_marketplaces_refresh_request, plugins_marketplaces_remove_request, plugins_reload_request, plugins_uninstall_request, plugins_update_request, plugin_update_all_entry, plugin_update_all_result, plugin_update_result, poll_spawned_sessions_result, provider_add_request, provider_add_result, provider_config, provider_config_azure, provider_config_transport, provider_config_type, provider_config_wire_api, provider_endpoint, provider_endpoint_transport, provider_endpoint_type, provider_endpoint_wire_api, provider_get_endpoint_request, provider_model_config, provider_session_token, provider_token_acquire_request, provider_token_acquire_result, push_attachment, push_attachment_blob, push_attachment_directory, push_attachment_file, push_attachment_file_line_range, push_attachment_git_hub_actions_job, push_attachment_git_hub_commit, push_attachment_git_hub_file, push_attachment_git_hub_file_diff, push_attachment_git_hub_file_diff_side, push_attachment_git_hub_reference, push_attachment_git_hub_reference_type, push_attachment_git_hub_release, push_attachment_git_hub_repository, push_attachment_git_hub_snippet, push_attachment_git_hub_tree_comparison, push_attachment_git_hub_tree_comparison_side, push_attachment_git_hub_url, push_attachment_selection, push_attachment_selection_details, push_attachment_selection_details_end, push_attachment_selection_details_start, push_git_hub_repo_ref, queued_command_handled, queued_command_not_handled, queued_command_result, queue_pending_items, queue_pending_items_kind, queue_pending_items_result, queue_remove_most_recent_result, register_event_interest_params, register_event_interest_result, register_extension_tools_params, register_extension_tools_result, release_event_interest_params, remote_control_config, remote_control_config_existing_mc_session, remote_control_status, remote_control_status_active, remote_control_status_connecting, remote_control_status_error, remote_control_status_off, remote_control_status_result, remote_control_stop_result, remote_control_transfer_result, remote_enable_request, remote_enable_result, remote_notify_steerable_changed_request, remote_notify_steerable_changed_result, remote_session_connection_result, remote_session_metadata_repository, remote_session_metadata_task_type, remote_session_metadata_value, remote_session_mode, remote_session_repository, sandbox_config, sandbox_config_user_policy, sandbox_config_user_policy_experimental, sandbox_config_user_policy_experimental_seatbelt, sandbox_config_user_policy_filesystem, sandbox_config_user_policy_network, sandbox_config_user_policy_seatbelt, schedule_entry, schedule_list, schedule_stop_request, schedule_stop_result, secrets_add_filter_values_request, secrets_add_filter_values_result, send_agent_mode, send_attachments_to_message_params, send_mode, send_request, send_result, server_agent_list, server_instruction_source_list, server_skill, server_skill_list, session_activity, session_auth_status, session_bulk_delete_result, session_capability, session_context, session_context_host_type, session_enrich_metadata_result, session_fs_append_file_request, session_fs_error, session_fs_error_code, session_fs_exists_request, session_fs_exists_result, session_fs_mkdir_request, session_fs_readdir_request, session_fs_readdir_result, session_fs_readdir_with_types_entry, session_fs_readdir_with_types_entry_type, session_fs_readdir_with_types_request, session_fs_readdir_with_types_result, session_fs_read_file_request, session_fs_read_file_result, session_fs_rename_request, session_fs_rm_request, session_fs_set_provider_capabilities, session_fs_set_provider_conventions, session_fs_set_provider_request, session_fs_set_provider_result, session_fs_sqlite_exists_request, session_fs_sqlite_exists_result, session_fs_sqlite_query_request, session_fs_sqlite_query_result, session_fs_sqlite_query_type, session_fs_stat_request, session_fs_stat_result, session_fs_write_file_request, session_installed_plugin, session_installed_plugin_source, session_installed_plugin_source_git_hub, session_installed_plugin_source_local, session_installed_plugin_source_url, session_list, session_list_entry, session_list_filter, session_load_deferred_repo_hooks_result, session_log_level, session_mcp_apps_call_tool_result, session_metadata_snapshot, session_mode, session_model_list, session_open_options, session_open_options_additional_content_exclusion_policy, session_open_options_additional_content_exclusion_policy_rule, session_open_options_additional_content_exclusion_policy_rule_source, session_open_options_additional_content_exclusion_policy_scope, session_open_options_env_value_mode, session_open_options_reasoning_summary, session_open_params, session_open_result, session_prune_result, sessions_bulk_delete_request, sessions_check_in_use_request, sessions_check_in_use_result, sessions_close_request, sessions_close_result, sessions_enrich_metadata_request, session_set_credentials_params, session_set_credentials_result, sessions_find_by_prefix_request, sessions_find_by_prefix_result, sessions_find_by_task_id_request, sessions_find_by_task_id_result, sessions_fork_request, sessions_fork_result, sessions_get_board_entry_count_request, sessions_get_board_entry_count_result, sessions_get_event_file_path_request, sessions_get_event_file_path_result, sessions_get_last_for_context_request, sessions_get_last_for_context_result, sessions_get_persisted_remote_steerable_request, sessions_get_persisted_remote_steerable_result, session_sizes, sessions_list_request, sessions_load_deferred_repo_hooks_request, sessions_open_attach, sessions_open_cloud, sessions_open_create, sessions_open_handoff, sessions_open_handoff_task_type, sessions_open_progress, sessions_open_progress_status, sessions_open_progress_step, sessions_open_remote, sessions_open_resume, sessions_open_resume_last, sessions_open_status, session_source, sessions_poll_spawned_sessions_event, sessions_poll_spawned_sessions_request, 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_working_directory_context, session_working_directory_context_host_type, shell_cancel_user_requested_request, shell_exec_request, shell_exec_result, shell_execute_user_requested_request, shell_kill_request, shell_kill_result, shell_kill_signal, shutdown_request, skill, skill_discovery_path, skill_discovery_path_list, skill_discovery_scope, skill_list, skills_config_set_disabled_skills_request, skills_disable_request, skills_discover_request, skills_enable_request, skills_get_discovery_paths_request, skills_get_invoked_result, skills_invoked_skill, skills_load_diagnostics, slash_command_agent_prompt_result, slash_command_completed_result, slash_command_info, slash_command_input, slash_command_input_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_user_input_request, ui_register_direct_auto_mode_switch_handler_result, 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, workspace_diff_file_change, workspace_diff_file_change_type, workspace_diff_mode, workspace_diff_result, workspaces_checkpoints, workspaces_create_file_request, workspaces_diff_request, workspaces_get_workspace_result, workspaces_list_checkpoints_result, workspaces_list_files_result, workspaces_read_checkpoint_request, workspaces_read_checkpoint_result, workspaces_read_file_request, workspaces_read_file_result, workspaces_save_large_paste_request, workspaces_save_large_paste_result, workspace_summary_host_type, workspaces_workspace_details_host_type, session_context_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, agent_discovery_path, agent_discovery_path_list, agent_discovery_path_scope, agent_get_current_result, agent_info, agent_info_source, agent_list, agent_registry_live_target_entry, agent_registry_live_target_entry_attention_kind, agent_registry_live_target_entry_kind, agent_registry_live_target_entry_last_terminal_event, agent_registry_live_target_entry_status, agent_registry_log_capture, agent_registry_log_capture_open_error_reason, agent_registry_spawn_error, agent_registry_spawn_permission_mode, agent_registry_spawn_registry_timeout, agent_registry_spawn_request, agent_registry_spawn_result, agent_registry_spawn_spawned, agent_registry_spawn_validation_error, agent_registry_spawn_validation_error_field, agent_registry_spawn_validation_error_reason, agent_reload_result, agents_discover_request, agent_select_request, agent_select_result, agents_get_discovery_paths_request, allow_all_permission_set_result, allow_all_permission_state, api_key_auth_info, auth_info, auth_info_type, cancel_user_requested_shell_command_result, canvas_action, canvas_action_invoke_request, canvas_action_invoke_result, canvas_close_request, canvas_host_context, canvas_host_context_capabilities, canvas_json_schema, canvas_list, canvas_list_open_result, canvas_open_request, canvas_provider_close_request, canvas_provider_invoke_action_request, canvas_provider_open_request, canvas_provider_open_result, canvas_session_context, capi_session_options, command_list, commands_handle_pending_command_request, commands_handle_pending_command_result, commands_invoke_request, commands_list_request, commands_respond_to_queued_command_request, commands_respond_to_queued_command_result, configure_session_extensions_params, connected_remote_session_metadata, connected_remote_session_metadata_kind, connected_remote_session_metadata_repository, connect_remote_session_params, connect_request, connect_result, content_filter_mode, 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, discovered_canvas, discovered_mcp_server, discovered_mcp_server_type, enqueue_command_params, enqueue_command_result, env_auth_info, event_log_read_request, event_log_release_interest_result, event_log_tail_result, event_log_types, events_agent_scope, events_cursor_status, events_read_result, execute_command_params, execute_command_result, extension, extension_context_push_input, extension_list, extensions_disable_request, extensions_enable_request, extension_source, extension_status, external_tool_result, external_tool_text_result_for_llm, external_tool_text_result_for_llm_binary_results_for_llm, external_tool_text_result_for_llm_binary_results_for_llm_type, external_tool_text_result_for_llm_content, external_tool_text_result_for_llm_content_audio, external_tool_text_result_for_llm_content_image, external_tool_text_result_for_llm_content_resource, external_tool_text_result_for_llm_content_resource_details, external_tool_text_result_for_llm_content_resource_link, external_tool_text_result_for_llm_content_resource_link_icon, external_tool_text_result_for_llm_content_resource_link_icon_theme, external_tool_text_result_for_llm_content_shell_exit, external_tool_text_result_for_llm_content_terminal, external_tool_text_result_for_llm_content_text, filter_mapping, fleet_start_request, fleet_start_result, folder_trust_add_params, folder_trust_check_params, folder_trust_check_result, gh_cli_auth_info, handle_pending_tool_call_request, handle_pending_tool_call_result, history_abort_manual_compaction_result, history_cancel_background_compaction_result, history_compact_context_window, history_compact_request, history_compact_result, history_summarize_for_handoff_result, history_truncate_request, history_truncate_result, hmac_auth_info, installed_plugin, installed_plugin_info, installed_plugin_source, installed_plugin_source_git_hub, installed_plugin_source_local, installed_plugin_source_url, instruction_discovery_path, instruction_discovery_path_kind, instruction_discovery_path_list, instruction_discovery_path_location, instructions_discover_request, instructions_get_discovery_paths_request, instructions_get_sources_result, instruction_source, instruction_source_location, instruction_source_type, llm_inference_headers, llm_inference_http_request_chunk_request, llm_inference_http_request_chunk_result, llm_inference_http_request_start_request, llm_inference_http_request_start_result, llm_inference_http_request_start_transport, llm_inference_http_response_chunk_error, llm_inference_http_response_chunk_request, llm_inference_http_response_chunk_result, llm_inference_http_response_start_request, llm_inference_http_response_start_result, llm_inference_set_provider_result, local_session_metadata_value, log_request, log_result, lsp_initialize_request, marketplace_add_result, marketplace_browse_result, marketplace_info, marketplace_list_result, marketplace_plugin_info, marketplace_refresh_entry, marketplace_refresh_result, marketplace_remove_result, mcp_allowed_server, mcp_apps_call_tool_request, mcp_apps_diagnose_capability, mcp_apps_diagnose_request, mcp_apps_diagnose_result, mcp_apps_diagnose_server, mcp_apps_host_context, mcp_apps_host_context_details, mcp_apps_host_context_details_available_display_mode, mcp_apps_host_context_details_display_mode, mcp_apps_host_context_details_platform, mcp_apps_host_context_details_theme, mcp_apps_list_tools_request, mcp_apps_list_tools_result, mcp_apps_read_resource_request, mcp_apps_read_resource_result, mcp_apps_resource_content, mcp_apps_set_host_context_details, mcp_apps_set_host_context_details_available_display_mode, mcp_apps_set_host_context_details_display_mode, mcp_apps_set_host_context_details_platform, mcp_apps_set_host_context_details_theme, mcp_apps_set_host_context_request, mcp_cancel_sampling_execution_params, mcp_cancel_sampling_execution_result, mcp_config_add_request, mcp_config_disable_request, mcp_config_enable_request, mcp_config_list, mcp_config_remove_request, mcp_config_update_request, mcp_configure_git_hub_request, mcp_configure_git_hub_result, mcp_disable_request, mcp_discover_request, mcp_discover_result, mcp_enable_request, mcp_execute_sampling_params, mcp_execute_sampling_request, mcp_execute_sampling_result, mcp_filtered_server, mcp_headers_handle_pending_headers_refresh_request, mcp_headers_handle_pending_headers_refresh_request_request, mcp_headers_handle_pending_headers_refresh_request_result, mcp_host_state, mcp_is_server_running_request, mcp_is_server_running_result, mcp_list_tools_request, mcp_list_tools_result, mcp_oauth_handle_pending_request, mcp_oauth_handle_pending_result, mcp_oauth_login_grant_type, mcp_oauth_login_request, mcp_oauth_login_result, mcp_oauth_pending_request_response, mcp_oauth_respond_request, mcp_oauth_respond_result, mcp_register_external_client_request, mcp_reload_with_config_request, mcp_remove_git_hub_result, mcp_restart_server_request, mcp_sampling_execution_action, mcp_sampling_execution_result, mcp_server, mcp_server_auth_config, mcp_server_auth_config_redirect_port, mcp_server_config, mcp_server_config_defer_tools, mcp_server_config_http, mcp_server_config_http_oauth_grant_type, mcp_server_config_http_type, mcp_server_config_stdio, mcp_server_failure_info, mcp_server_list, mcp_server_needs_auth_info, mcp_set_env_value_mode_details, mcp_set_env_value_mode_params, mcp_set_env_value_mode_result, mcp_start_server_request, mcp_start_servers_result, mcp_stop_server_request, mcp_tools, mcp_unregister_external_client_request, memory_configuration, metadata_context_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_token_prices, model_billing_token_prices_long_context, model_capabilities, model_capabilities_limits, model_capabilities_limits_vision, model_capabilities_override, model_capabilities_override_limits, model_capabilities_override_limits_vision, model_capabilities_override_supports, model_capabilities_supports, model_list, model_list_request, model_picker_category, model_picker_price_category, model_policy, model_policy_state, model_set_reasoning_effort_request, model_set_reasoning_effort_result, models_list_request, model_switch_to_request, model_switch_to_result, mode_set_request, named_provider_config, name_get_result, name_set_auto_request, name_set_auto_result, name_set_request, open_canvas_instance, options_update_additional_content_exclusion_policy, options_update_additional_content_exclusion_policy_rule, options_update_additional_content_exclusion_policy_rule_source, options_update_additional_content_exclusion_policy_scope, options_update_context_tier, options_update_env_value_mode, options_update_reasoning_summary, options_update_tool_filter_precedence, pending_permission_request, pending_permission_request_list, permission_decision, permission_decision_approved, permission_decision_approved_for_location, permission_decision_approved_for_session, permission_decision_approve_for_location, permission_decision_approve_for_location_approval, permission_decision_approve_for_location_approval_commands, permission_decision_approve_for_location_approval_custom_tool, permission_decision_approve_for_location_approval_extension_management, permission_decision_approve_for_location_approval_extension_permission_access, permission_decision_approve_for_location_approval_mcp, permission_decision_approve_for_location_approval_mcp_sampling, permission_decision_approve_for_location_approval_memory, permission_decision_approve_for_location_approval_read, permission_decision_approve_for_location_approval_write, permission_decision_approve_for_session, permission_decision_approve_for_session_approval, permission_decision_approve_for_session_approval_commands, permission_decision_approve_for_session_approval_custom_tool, permission_decision_approve_for_session_approval_extension_management, permission_decision_approve_for_session_approval_extension_permission_access, permission_decision_approve_for_session_approval_mcp, permission_decision_approve_for_session_approval_mcp_sampling, permission_decision_approve_for_session_approval_memory, permission_decision_approve_for_session_approval_read, permission_decision_approve_for_session_approval_write, permission_decision_approve_once, permission_decision_approve_permanently, permission_decision_cancelled, permission_decision_denied_by_content_exclusion_policy, permission_decision_denied_by_permission_request_hook, permission_decision_denied_by_rules, permission_decision_denied_interactively_by_user, permission_decision_denied_no_approval_rule_and_could_not_request_from_user, permission_decision_reject, permission_decision_request, permission_decision_user_not_available, permission_location_add_tool_approval_params, permission_location_apply_params, permission_location_apply_result, permission_location_resolve_params, permission_location_resolve_result, permission_location_type, permission_paths_add_params, permission_paths_allowed_check_params, permission_paths_allowed_check_result, permission_paths_config, permission_paths_list, permission_paths_update_primary_params, permission_paths_workspace_check_params, permission_paths_workspace_check_result, permission_prompt_shown_notification, permission_request_result, permission_rules_set, permissions_configure_additional_content_exclusion_policy, permissions_configure_additional_content_exclusion_policy_rule, permissions_configure_additional_content_exclusion_policy_rule_source, permissions_configure_additional_content_exclusion_policy_scope, permissions_configure_params, permissions_configure_result, permissions_folder_trust_add_trusted_result, permissions_get_allow_all_request, permissions_locations_add_tool_approval_details, permissions_locations_add_tool_approval_details_commands, permissions_locations_add_tool_approval_details_custom_tool, permissions_locations_add_tool_approval_details_extension_management, permissions_locations_add_tool_approval_details_extension_permission_access, permissions_locations_add_tool_approval_details_mcp, permissions_locations_add_tool_approval_details_mcp_sampling, permissions_locations_add_tool_approval_details_memory, permissions_locations_add_tool_approval_details_read, permissions_locations_add_tool_approval_details_write, permissions_locations_add_tool_approval_result, permissions_modify_rules_params, permissions_modify_rules_result, permissions_modify_rules_scope, permissions_notify_prompt_shown_result, permissions_paths_add_result, permissions_paths_list_request, permissions_paths_update_primary_result, permissions_pending_requests_request, permissions_reset_session_approvals_request, permissions_reset_session_approvals_result, permissions_set_allow_all_request, permissions_set_allow_all_source, permissions_set_approve_all_request, permissions_set_approve_all_result, permissions_set_approve_all_source, permissions_set_required_request, permissions_set_required_result, permissions_urls_set_unrestricted_mode_result, permission_urls_config, permission_urls_set_unrestricted_mode_params, ping_request, ping_result, plan_read_result, plan_read_sql_todos_result, plan_read_sql_todos_with_dependencies_result, plan_sql_todo_dependency, plan_sql_todos_row, plan_update_request, plugin, plugin_install_result, plugin_list, plugin_list_result, plugins_disable_request, plugins_enable_request, plugins_install_request, plugins_marketplaces_add_request, plugins_marketplaces_browse_request, plugins_marketplaces_refresh_request, plugins_marketplaces_remove_request, plugins_reload_request, plugins_uninstall_request, plugins_update_request, plugin_update_all_entry, plugin_update_all_result, plugin_update_result, poll_spawned_sessions_result, provider_add_request, provider_add_result, provider_config, provider_config_azure, provider_config_transport, provider_config_type, provider_config_wire_api, provider_endpoint, provider_endpoint_transport, provider_endpoint_type, provider_endpoint_wire_api, provider_get_endpoint_request, provider_model_config, provider_session_token, provider_token_acquire_request, provider_token_acquire_result, push_attachment, push_attachment_blob, push_attachment_directory, push_attachment_file, push_attachment_file_line_range, push_attachment_git_hub_actions_job, push_attachment_git_hub_commit, push_attachment_git_hub_file, push_attachment_git_hub_file_diff, push_attachment_git_hub_file_diff_side, push_attachment_git_hub_reference, push_attachment_git_hub_reference_type, push_attachment_git_hub_release, push_attachment_git_hub_repository, push_attachment_git_hub_snippet, push_attachment_git_hub_tree_comparison, push_attachment_git_hub_tree_comparison_side, push_attachment_git_hub_url, push_attachment_selection, push_attachment_selection_details, push_attachment_selection_details_end, push_attachment_selection_details_start, push_git_hub_repo_ref, queued_command_handled, queued_command_not_handled, queued_command_result, queue_pending_items, queue_pending_items_kind, queue_pending_items_result, queue_remove_most_recent_result, register_event_interest_params, register_event_interest_result, register_extension_tools_params, register_extension_tools_result, release_event_interest_params, remote_control_config, remote_control_config_existing_mc_session, remote_control_status, remote_control_status_active, remote_control_status_connecting, remote_control_status_error, remote_control_status_off, remote_control_status_result, remote_control_stop_result, remote_control_transfer_result, remote_enable_request, remote_enable_result, remote_notify_steerable_changed_request, remote_notify_steerable_changed_result, remote_session_connection_result, remote_session_metadata_repository, remote_session_metadata_task_type, remote_session_metadata_value, remote_session_mode, remote_session_repository, sandbox_config, sandbox_config_user_policy, sandbox_config_user_policy_experimental, sandbox_config_user_policy_experimental_seatbelt, sandbox_config_user_policy_filesystem, sandbox_config_user_policy_network, sandbox_config_user_policy_seatbelt, schedule_entry, schedule_list, schedule_stop_request, schedule_stop_result, secrets_add_filter_values_request, secrets_add_filter_values_result, send_agent_mode, send_attachments_to_message_params, send_mode, send_request, send_result, server_agent_list, server_instruction_source_list, server_skill, server_skill_list, session_activity, session_auth_status, session_bulk_delete_result, session_capability, session_context, session_context_host_type, session_enrich_metadata_result, session_fs_append_file_request, session_fs_error, session_fs_error_code, session_fs_exists_request, session_fs_exists_result, session_fs_mkdir_request, session_fs_readdir_request, session_fs_readdir_result, session_fs_readdir_with_types_entry, session_fs_readdir_with_types_entry_type, session_fs_readdir_with_types_request, session_fs_readdir_with_types_result, session_fs_read_file_request, session_fs_read_file_result, session_fs_rename_request, session_fs_rm_request, session_fs_set_provider_capabilities, session_fs_set_provider_conventions, session_fs_set_provider_request, session_fs_set_provider_result, session_fs_sqlite_exists_request, session_fs_sqlite_exists_result, session_fs_sqlite_query_request, session_fs_sqlite_query_result, session_fs_sqlite_query_type, session_fs_stat_request, session_fs_stat_result, session_fs_write_file_request, session_installed_plugin, session_installed_plugin_source, session_installed_plugin_source_git_hub, session_installed_plugin_source_local, session_installed_plugin_source_url, session_list, session_list_entry, session_list_filter, session_load_deferred_repo_hooks_result, session_log_level, session_mcp_apps_call_tool_result, session_metadata_snapshot, session_mode, session_model_list, session_open_options, session_open_options_additional_content_exclusion_policy, session_open_options_additional_content_exclusion_policy_rule, session_open_options_additional_content_exclusion_policy_rule_source, session_open_options_additional_content_exclusion_policy_scope, session_open_options_env_value_mode, session_open_options_reasoning_summary, session_open_params, session_open_result, session_prune_result, sessions_bulk_delete_request, sessions_check_in_use_request, sessions_check_in_use_result, sessions_close_request, sessions_close_result, sessions_enrich_metadata_request, session_set_credentials_params, session_set_credentials_result, sessions_find_by_prefix_request, sessions_find_by_prefix_result, sessions_find_by_task_id_request, sessions_find_by_task_id_result, sessions_fork_request, sessions_fork_result, sessions_get_board_entry_count_request, sessions_get_board_entry_count_result, sessions_get_event_file_path_request, sessions_get_event_file_path_result, sessions_get_last_for_context_request, sessions_get_last_for_context_result, sessions_get_persisted_remote_steerable_request, sessions_get_persisted_remote_steerable_result, session_sizes, sessions_list_request, sessions_load_deferred_repo_hooks_request, sessions_open_attach, sessions_open_cloud, sessions_open_create, sessions_open_handoff, sessions_open_handoff_task_type, sessions_open_progress, sessions_open_progress_status, sessions_open_progress_step, sessions_open_remote, sessions_open_resume, sessions_open_resume_last, sessions_open_status, session_source, sessions_poll_spawned_sessions_event, sessions_poll_spawned_sessions_request, sessions_prune_old_request, sessions_register_extension_tools_on_session_options, sessions_release_lock_request, sessions_release_lock_result, sessions_reload_plugin_hooks_request, sessions_reload_plugin_hooks_result, sessions_save_request, sessions_save_result, sessions_set_additional_plugins_request, sessions_set_additional_plugins_result, sessions_set_remote_control_steering_request, sessions_start_remote_control_request, sessions_stop_remote_control_request, sessions_transfer_remote_control_request, session_telemetry_engagement, session_update_options_params, session_update_options_result, session_visibility_status, session_working_directory_context, session_working_directory_context_host_type, shell_cancel_user_requested_request, shell_exec_request, shell_exec_result, shell_execute_user_requested_request, shell_kill_request, shell_kill_result, shell_kill_signal, shutdown_request, skill, skill_discovery_path, skill_discovery_path_list, skill_discovery_scope, skill_list, skills_config_set_disabled_skills_request, skills_disable_request, skills_discover_request, skills_enable_request, skills_get_discovery_paths_request, skills_get_invoked_result, skills_invoked_skill, skills_load_diagnostics, slash_command_agent_prompt_result, slash_command_completed_result, slash_command_info, slash_command_input, slash_command_input_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_user_input_request, ui_register_direct_auto_mode_switch_handler_result, ui_unregister_direct_auto_mode_switch_handler_request, ui_unregister_direct_auto_mode_switch_handler_result, ui_user_input_response, update_subagent_settings_request, usage_get_metrics_result, usage_metrics_code_changes, usage_metrics_model_metric, usage_metrics_model_metric_requests, usage_metrics_model_metric_token_detail, usage_metrics_model_metric_usage, usage_metrics_token_detail, user_auth_info, user_requested_shell_command_result, user_setting_metadata, user_settings_get_result, user_settings_set_request, user_settings_set_result, visibility_get_result, visibility_set_request, visibility_set_result, workspace_diff_file_change, workspace_diff_file_change_type, workspace_diff_mode, workspace_diff_result, workspaces_checkpoints, workspaces_create_file_request, workspaces_diff_request, workspaces_get_workspace_result, workspaces_list_checkpoints_result, workspaces_list_files_result, workspaces_read_checkpoint_request, workspaces_read_checkpoint_result, workspaces_read_file_request, workspaces_read_file_result, workspaces_save_large_paste_request, workspaces_save_large_paste_result, workspace_summary_host_type, workspaces_workspace_details_host_type, session_context_info, subagent_settings, task_progress, workspace_summary) def to_dict(self) -> dict: result: dict = {} @@ -23313,6 +23665,7 @@ def to_dict(self) -> dict: result["ExternalToolTextResultForLlmContentResourceLink"] = to_class(ExternalToolTextResultForLlmContentResourceLink, self.external_tool_text_result_for_llm_content_resource_link) result["ExternalToolTextResultForLlmContentResourceLinkIcon"] = to_class(ExternalToolTextResultForLlmContentResourceLinkIcon, self.external_tool_text_result_for_llm_content_resource_link_icon) result["ExternalToolTextResultForLlmContentResourceLinkIconTheme"] = to_enum(Theme, self.external_tool_text_result_for_llm_content_resource_link_icon_theme) + result["ExternalToolTextResultForLlmContentShellExit"] = to_class(ExternalToolTextResultForLlmContentShellExit, self.external_tool_text_result_for_llm_content_shell_exit) result["ExternalToolTextResultForLlmContentTerminal"] = to_class(ExternalToolTextResultForLlmContentTerminal, self.external_tool_text_result_for_llm_content_terminal) result["ExternalToolTextResultForLlmContentText"] = to_class(ExternalToolTextResultForLlmContentText, self.external_tool_text_result_for_llm_content_text) result["FilterMapping"] = from_union([lambda x: from_dict(lambda x: to_enum(ContentFilterMode, x), x), lambda x: to_enum(ContentFilterMode, x)], self.filter_mapping) @@ -23838,6 +24191,7 @@ def to_dict(self) -> dict: result["SessionTelemetryEngagement"] = to_class(SessionTelemetryEngagement, self.session_telemetry_engagement) result["SessionUpdateOptionsParams"] = to_class(SessionUpdateOptionsParams, self.session_update_options_params) result["SessionUpdateOptionsResult"] = to_class(SessionUpdateOptionsResult, self.session_update_options_result) + result["SessionVisibilityStatus"] = to_enum(SessionVisibilityStatus, self.session_visibility_status) result["SessionWorkingDirectoryContext"] = to_class(SessionWorkingDirectoryContext, self.session_working_directory_context) result["SessionWorkingDirectoryContextHostType"] = to_enum(HostType, self.session_working_directory_context_host_type) result["ShellCancelUserRequestedRequest"] = to_class(ShellCancelUserRequestedRequest, self.shell_cancel_user_requested_request) @@ -23954,6 +24308,13 @@ def to_dict(self) -> dict: result["UsageMetricsTokenDetail"] = to_class(UsageMetricsTokenDetail, self.usage_metrics_token_detail) result["UserAuthInfo"] = to_class(UserAuthInfo, self.user_auth_info) result["UserRequestedShellCommandResult"] = to_class(UserRequestedShellCommandResult, self.user_requested_shell_command_result) + result["UserSettingMetadata"] = to_class(UserSettingMetadata, self.user_setting_metadata) + result["UserSettingsGetResult"] = to_class(UserSettingsGetResult, self.user_settings_get_result) + result["UserSettingsSetRequest"] = to_class(UserSettingsSetRequest, self.user_settings_set_request) + result["UserSettingsSetResult"] = to_class(UserSettingsSetResult, self.user_settings_set_result) + result["VisibilityGetResult"] = to_class(VisibilityGetResult, self.visibility_get_result) + result["VisibilitySetRequest"] = to_class(VisibilitySetRequest, self.visibility_set_request) + result["VisibilitySetResult"] = to_class(VisibilitySetResult, self.visibility_set_result) result["WorkspaceDiffFileChange"] = to_class(WorkspaceDiffFileChange, self.workspace_diff_file_change) result["WorkspaceDiffFileChangeType"] = to_enum(WorkspaceDiffFileChangeType, self.workspace_diff_file_change_type) result["WorkspaceDiffMode"] = to_enum(WorkspaceDiffMode, self.workspace_diff_mode) @@ -24014,7 +24375,7 @@ def _load_AuthInfo(obj: Any) -> "AuthInfo": case _: raise ValueError(f"Unknown AuthInfo type: {kind!r}") # A content block within a tool result, which may be text, terminal output, image, audio, or a resource -ExternalToolTextResultForLlmContent = ExternalToolTextResultForLlmContentText | ExternalToolTextResultForLlmContentTerminal | ExternalToolTextResultForLlmContentImage | ExternalToolTextResultForLlmContentAudio | ExternalToolTextResultForLlmContentResourceLink | ExternalToolTextResultForLlmContentResource +ExternalToolTextResultForLlmContent = ExternalToolTextResultForLlmContentText | ExternalToolTextResultForLlmContentTerminal | ExternalToolTextResultForLlmContentShellExit | ExternalToolTextResultForLlmContentImage | ExternalToolTextResultForLlmContentAudio | ExternalToolTextResultForLlmContentResourceLink | ExternalToolTextResultForLlmContentResource def _load_ExternalToolTextResultForLlmContent(obj: Any) -> "ExternalToolTextResultForLlmContent": assert isinstance(obj, dict) @@ -24022,6 +24383,7 @@ def _load_ExternalToolTextResultForLlmContent(obj: Any) -> "ExternalToolTextResu match kind: case "text": return ExternalToolTextResultForLlmContentText.from_dict(obj) case "terminal": return ExternalToolTextResultForLlmContentTerminal.from_dict(obj) + case "shell_exit": return ExternalToolTextResultForLlmContentShellExit.from_dict(obj) case "image": return ExternalToolTextResultForLlmContentImage.from_dict(obj) case "audio": return ExternalToolTextResultForLlmContentAudio.from_dict(obj) case "resource_link": return ExternalToolTextResultForLlmContentResourceLink.from_dict(obj) @@ -24282,6 +24644,7 @@ def _patch_model_capabilities(data: dict) -> dict: return data +# Experimental: this API group is experimental and may change or be removed. class ServerModelsApi: def __init__(self, client: "JsonRpcClient"): self._client = client @@ -24292,6 +24655,7 @@ async def list(self, params: ModelsListRequest, *, timeout: float | None = None) return ModelList.from_dict(_patch_model_capabilities(await self._client.request("models.list", params_dict, **_timeout_kwargs(timeout)))) +# Experimental: this API group is experimental and may change or be removed. class ServerToolsApi: def __init__(self, client: "JsonRpcClient"): self._client = client @@ -24302,6 +24666,7 @@ async def list(self, params: ToolsListRequest, *, timeout: float | None = None) return ToolList.from_dict(await self._client.request("tools.list", params_dict, **_timeout_kwargs(timeout))) +# Experimental: this API group is experimental and may change or be removed. class ServerAccountApi: def __init__(self, client: "JsonRpcClient"): self._client = client @@ -24330,6 +24695,7 @@ async def logout(self, params: AccountLogoutRequest, *, timeout: float | None = return AccountLogoutResult.from_dict(await self._client.request("account.logout", params_dict, **_timeout_kwargs(timeout))) +# Experimental: this API group is experimental and may change or be removed. class ServerSecretsApi: def __init__(self, client: "JsonRpcClient"): self._client = client @@ -24340,6 +24706,7 @@ async def add_filter_values(self, params: SecretsAddFilterValuesRequest, *, time return SecretsAddFilterValuesResult.from_dict(await self._client.request("secrets.addFilterValues", params_dict, **_timeout_kwargs(timeout))) +# Experimental: this API group is experimental and may change or be removed. class ServerMcpConfigApi: def __init__(self, client: "JsonRpcClient"): self._client = client @@ -24378,6 +24745,7 @@ async def reload(self, *, timeout: float | None = None) -> None: await self._client.request("mcp.config.reload", {}, **_timeout_kwargs(timeout)) +# Experimental: this API group is experimental and may change or be removed. class ServerMcpApi: def __init__(self, client: "JsonRpcClient"): self._client = client @@ -24459,6 +24827,7 @@ async def disable(self, params: PluginsDisableRequest, *, timeout: float | None await self._client.request("plugins.disable", params_dict, **_timeout_kwargs(timeout)) +# Experimental: this API group is experimental and may change or be removed. class ServerSkillsConfigApi: def __init__(self, client: "JsonRpcClient"): self._client = client @@ -24469,6 +24838,7 @@ async def set_disabled_skills(self, params: SkillsConfigSetDisabledSkillsRequest await self._client.request("skills.config.setDisabledSkills", params_dict, **_timeout_kwargs(timeout)) +# Experimental: this API group is experimental and may change or be removed. class ServerSkillsApi: def __init__(self, client: "JsonRpcClient"): self._client = client @@ -24480,7 +24850,7 @@ async def discover(self, params: SkillsDiscoverRequest, *, timeout: float | None return ServerSkillList.from_dict(await self._client.request("skills.discover", params_dict, **_timeout_kwargs(timeout))) async def get_discovery_paths(self, params: SkillsGetDiscoveryPathsRequest, *, timeout: float | None = None) -> SkillDiscoveryPathList: - "Returns the canonical directories where a client may create skills that the runtime will recognize, including ones that do not exist yet. Project directories become active once created.\n\nArgs:\n params: Optional project paths to enumerate.\n\nReturns:\n Canonical locations where skills can be created so the runtime will recognize them.\n\n.. warning:: This API is experimental and may change or be removed in future versions." + "Returns the canonical directories where a client may create skills that the runtime will recognize, including ones that do not exist yet. Project directories become active once created.\n\nArgs:\n params: Optional project paths to enumerate.\n\nReturns:\n Canonical locations where skills can be created so the runtime will recognize them." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return SkillDiscoveryPathList.from_dict(await self._client.request("skills.getDiscoveryPaths", params_dict, **_timeout_kwargs(timeout))) @@ -24517,6 +24887,7 @@ async def get_discovery_paths(self, params: InstructionsGetDiscoveryPathsRequest return InstructionDiscoveryPathList.from_dict(await self._client.request("instructions.getDiscoveryPaths", params_dict, **_timeout_kwargs(timeout))) +# Experimental: this API group is experimental and may change or be removed. class ServerUserSettingsApi: def __init__(self, client: "JsonRpcClient"): self._client = client @@ -24525,13 +24896,24 @@ async def reload(self, *, timeout: float | None = None) -> None: "Drops this runtime process's in-memory user settings cache so the next settings read observes disk." await self._client.request("user.settings.reload", {}, **_timeout_kwargs(timeout)) + async def get(self, *, timeout: float | None = None) -> UserSettingsGetResult: + "Lists every known user setting (settings.json overlaid with the legacy config.json, config.json wins), each with its effective value, its default, and whether it is at the default — so settings the user has never set still appear with their default value. Does not include repository- or enterprise-managed overrides that the runtime layers on top at session time.\n\nReturns:\n Per-key metadata for every known user setting (settings.json overlaid with the legacy config.json, config.json wins), including settings left at their default. Excludes repository- and enterprise-managed overrides." + return UserSettingsGetResult.from_dict(await self._client.request("user.settings.get", {}, **_timeout_kwargs(timeout))) + + async def set(self, params: UserSettingsSetRequest, *, timeout: float | None = None) -> UserSettingsSetResult: + "Writes one or more user settings to settings.json, replacing each provided top-level key. A key whose value is null is removed. Returns the keys whose new value is shadowed by a legacy config.json entry (config.json wins on read), which the runtime leaves in place — such writes do not take effect until the legacy value is removed.\n\nArgs:\n params: Partial user settings to write to settings.json. Each top-level key is written individually, replacing the existing value; a key whose value is null is removed.\n\nReturns:\n Outcome of writing user settings." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return UserSettingsSetResult.from_dict(await self._client.request("user.settings.set", params_dict, **_timeout_kwargs(timeout))) + +# Experimental: this API group is experimental and may change or be removed. class ServerUserApi: def __init__(self, client: "JsonRpcClient"): self._client = client self.settings = ServerUserSettingsApi(client) +# Experimental: this API group is experimental and may change or be removed. class ServerRuntimeApi: def __init__(self, client: "JsonRpcClient"): self._client = client @@ -24541,6 +24923,7 @@ async def shutdown(self, *, timeout: float | None = None) -> None: await self._client.request("runtime.shutdown", {}, **_timeout_kwargs(timeout)) +# Experimental: this API group is experimental and may change or be removed. class ServerSessionFsApi: def __init__(self, client: "JsonRpcClient"): self._client = client @@ -24722,7 +25105,7 @@ def __init__(self, client: "JsonRpcClient"): self.agent_registry = ServerAgentRegistryApi(client) async def ping(self, params: PingRequest, *, timeout: float | None = None) -> PingResult: - "Checks server responsiveness and returns protocol information.\n\nArgs:\n params: Optional message to echo back to the caller.\n\nReturns:\n Server liveness response, including the echoed message, current server timestamp, and protocol version." + "Checks server responsiveness and returns protocol information.\n\nArgs:\n params: Optional message to echo back to the caller.\n\nReturns:\n Server liveness response, including the echoed message, current server timestamp, and protocol version.\n\n.. warning:: This API is experimental and may change or be removed in future versions." 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))) @@ -24770,7 +25153,7 @@ def __init__(self, client: "JsonRpcClient"): self.sessions = _InternalServerSessionsApi(client) async def _connect(self, params: _ConnectRequest, *, timeout: float | None = None) -> _ConnectResult: - "Performs the SDK server connection handshake and validates the optional connection token. Marked internal because this is JSON-RPC transport plumbing invoked automatically by an SDK client's own `connect()` wrapper, not a user-facing method. Stays internal as long as the SDK client owns the handshake; would only become public if the SDK ever exposed the raw schema surface to consumers without a connection wrapper.\n\nArgs:\n params: Optional connection token presented by the SDK client during the handshake.\n\nReturns:\n Handshake result reporting the server's protocol version and package version on success.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + "Performs the SDK server connection handshake and validates the optional connection token. Marked internal because this is JSON-RPC transport plumbing invoked automatically by an SDK client's own `connect()` wrapper, not a user-facing method. Stays internal as long as the SDK client owns the handshake; would only become public if the SDK ever exposed the raw schema surface to consumers without a connection wrapper.\n\nArgs:\n params: Optional connection token presented by the SDK client during the handshake.\n\nReturns:\n Handshake result reporting the server's protocol version and package version on success.\n\n.. warning:: This API is experimental and may change or be removed in future versions.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return _ConnectResult.from_dict(await self._client.request("connect", params_dict, **_timeout_kwargs(timeout))) @@ -25859,6 +26242,23 @@ async def notify_steerable_changed(self, params: RemoteNotifySteerableChangedReq return RemoteNotifySteerableChangedResult.from_dict(await self._client.request("session.remote.notifySteerableChanged", params_dict, **_timeout_kwargs(timeout))) +# Experimental: this API group is experimental and may change or be removed. +class VisibilityApi: + def __init__(self, client: "JsonRpcClient", session_id: str): + self._client = client + self._session_id = session_id + + async def get(self, *, timeout: float | None = None) -> VisibilityGetResult: + "Returns the session's current Mission Control sharing status and shareable GitHub URL. Reflects whether the synced session is visible to repository readers (\"repo\") or restricted to its creator and collaborators (\"unshared\").\n\nReturns:\n Current sharing status and shareable GitHub URL for a session." + return VisibilityGetResult.from_dict(await self._client.request("session.visibility.get", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + + async def set(self, params: VisibilitySetRequest, *, timeout: float | None = None) -> VisibilitySetResult: + "Sets the session's Mission Control sharing status, controlling whether the synced session is visible to repository readers. Returns the effective status and shareable GitHub URL after the change.\n\nArgs:\n params: Desired sharing status for the session.\n\nReturns:\n Effective sharing status and shareable GitHub URL after updating session visibility." + params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} + params_dict["sessionId"] = self._session_id + return VisibilitySetResult.from_dict(await self._client.request("session.visibility.set", params_dict, **_timeout_kwargs(timeout))) + + # Experimental: this API group is experimental and may change or be removed. class ScheduleApi: def __init__(self, client: "JsonRpcClient", session_id: str): @@ -25911,6 +26311,7 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self.event_log = EventLogApi(client, session_id) self.usage = UsageApi(client, session_id) self.remote = RemoteApi(client, session_id) + self.visibility = VisibilityApi(client, session_id) self.schedule = ScheduleApi(client, session_id) async def suspend(self, *, timeout: float | None = None) -> None: @@ -26368,6 +26769,8 @@ async def handle_llm_inference_http_request_chunk(params: dict) -> dict | None: "ExternalToolTextResultForLlmContentResourceLinkIconTheme", "ExternalToolTextResultForLlmContentResourceLinkType", "ExternalToolTextResultForLlmContentResourceType", + "ExternalToolTextResultForLlmContentShellExit", + "ExternalToolTextResultForLlmContentShellExitType", "ExternalToolTextResultForLlmContentTerminal", "ExternalToolTextResultForLlmContentTerminalType", "ExternalToolTextResultForLlmContentText", @@ -26957,6 +27360,7 @@ async def handle_llm_inference_http_request_chunk(params: dict) -> dict | None: "SessionTelemetryEngagement", "SessionUpdateOptionsParams", "SessionUpdateOptionsResult", + "SessionVisibilityStatus", "SessionWorkingDirectoryContext", "SessionWorkingDirectoryContextHostType", "SessionsBulkDeleteRequest", @@ -27157,6 +27561,14 @@ async def handle_llm_inference_http_request_chunk(params: dict) -> dict | None: "UserAuthInfo", "UserAuthInfoType", "UserRequestedShellCommandResult", + "UserSettingMetadata", + "UserSettingsGetResult", + "UserSettingsSetRequest", + "UserSettingsSetResult", + "VisibilityApi", + "VisibilityGetResult", + "VisibilitySetRequest", + "VisibilitySetResult", "Workspace", "WorkspaceDiffFileChange", "WorkspaceDiffFileChangeType", diff --git a/python/copilot/generated/session_events.py b/python/copilot/generated/session_events.py index 00f6bb35cd..7ad5e9af95 100644 --- a/python/copilot/generated/session_events.py +++ b/python/copilot/generated/session_events.py @@ -136,6 +136,7 @@ class SessionEventType(Enum): SESSION_WARNING = "session.warning" SESSION_MODEL_CHANGE = "session.model_change" SESSION_MODE_CHANGED = "session.mode_changed" + SESSION_RESPONSE_LIMITS_CHANGED = "session.response_limits_changed" SESSION_PERMISSIONS_CHANGED = "session.permissions_changed" SESSION_PLAN_CHANGED = "session.plan_changed" SESSION_TODOS_CHANGED = "session.todos_changed" @@ -302,6 +303,38 @@ def to_dict(self) -> dict: return {_compat_to_json_key(key): _compat_to_json_value(value) for key, value in self._values.items() if value is not None} +# Deprecated: this type is deprecated and will be removed in a future version. +@dataclass +class ToolExecutionCompleteContentTerminal: + "Deprecated for shell command exit metadata. Use ToolExecutionCompleteContentShellExit instead." + text: str + type: ClassVar[str] = "terminal" + cwd: str | None = None + exit_code: int | None = None + + @staticmethod + def from_dict(obj: Any) -> "ToolExecutionCompleteContentTerminal": + assert isinstance(obj, dict) + text = from_str(obj.get("text")) + cwd = from_union([from_none, from_str], obj.get("cwd")) + exit_code = from_union([from_none, from_int], obj.get("exitCode")) + return ToolExecutionCompleteContentTerminal( + text=text, + cwd=cwd, + exit_code=exit_code, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["text"] = from_str(self.text) + result["type"] = self.type + if self.cwd is not None: + result["cwd"] = from_union([from_none, from_str], self.cwd) + if self.exit_code is not None: + result["exitCode"] = from_union([from_none, to_int], self.exit_code) + return result + + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class AssistantMessageServerTools: @@ -4778,27 +4811,22 @@ def to_dict(self) -> dict: @dataclass -class ResponseBudgetConfig: - "Optional response budget limits." +class ResponseLimitsConfig: + "Optional response limits." max_ai_credits: float | None = None - max_model_iterations: int | None = None @staticmethod - def from_dict(obj: Any) -> "ResponseBudgetConfig": + def from_dict(obj: Any) -> "ResponseLimitsConfig": assert isinstance(obj, dict) max_ai_credits = from_union([from_none, from_float], obj.get("maxAiCredits")) - max_model_iterations = from_union([from_none, from_int], obj.get("maxModelIterations")) - return ResponseBudgetConfig( + return ResponseLimitsConfig( max_ai_credits=max_ai_credits, - max_model_iterations=max_model_iterations, ) def to_dict(self) -> dict: result: dict = {} if self.max_ai_credits is not None: result["maxAiCredits"] = from_union([from_none, to_float], self.max_ai_credits) - if self.max_model_iterations is not None: - result["maxModelIterations"] = from_union([from_none, to_int], self.max_model_iterations) return result @@ -5568,6 +5596,25 @@ def to_dict(self) -> dict: return result +@dataclass +class SessionResponseLimitsChangedData: + "Response limits update details. Null clears the limits." + response_limits: ResponseLimitsConfig | None + + @staticmethod + def from_dict(obj: Any) -> "SessionResponseLimitsChangedData": + assert isinstance(obj, dict) + response_limits = from_union([from_none, ResponseLimitsConfig.from_dict], obj.get("responseLimits")) + return SessionResponseLimitsChangedData( + response_limits=response_limits, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["responseLimits"] = from_union([from_none, lambda x: to_class(ResponseLimitsConfig, x)], self.response_limits) + return result + + @dataclass class SessionResumeData: "Session resume metadata including current context and event count" @@ -5581,7 +5628,7 @@ class SessionResumeData: reasoning_effort: str | None = None reasoning_summary: ReasoningSummary | None = None remote_steerable: bool | None = None - response_budget: ResponseBudgetConfig | None = None + response_limits: ResponseLimitsConfig | None = None selected_model: str | None = None session_was_active: bool | None = None @@ -5598,7 +5645,7 @@ def from_dict(obj: Any) -> "SessionResumeData": reasoning_effort = from_union([from_none, from_str], obj.get("reasoningEffort")) reasoning_summary = from_union([from_none, lambda x: parse_enum(ReasoningSummary, x)], obj.get("reasoningSummary")) remote_steerable = from_union([from_none, from_bool], obj.get("remoteSteerable")) - response_budget = from_union([from_none, ResponseBudgetConfig.from_dict], obj.get("responseBudget")) + response_limits = from_union([from_none, ResponseLimitsConfig.from_dict], obj.get("responseLimits")) selected_model = from_union([from_none, from_str], obj.get("selectedModel")) session_was_active = from_union([from_none, from_bool], obj.get("sessionWasActive")) return SessionResumeData( @@ -5612,7 +5659,7 @@ def from_dict(obj: Any) -> "SessionResumeData": reasoning_effort=reasoning_effort, reasoning_summary=reasoning_summary, remote_steerable=remote_steerable, - response_budget=response_budget, + response_limits=response_limits, selected_model=selected_model, session_was_active=session_was_active, ) @@ -5637,8 +5684,8 @@ def to_dict(self) -> dict: result["reasoningSummary"] = from_union([from_none, lambda x: to_enum(ReasoningSummary, x)], self.reasoning_summary) if self.remote_steerable is not None: result["remoteSteerable"] = from_union([from_none, from_bool], self.remote_steerable) - if self.response_budget is not None: - result["responseBudget"] = from_union([from_none, lambda x: to_class(ResponseBudgetConfig, x)], self.response_budget) + if self.response_limits is not None: + result["responseLimits"] = from_union([from_none, lambda x: to_class(ResponseLimitsConfig, x)], self.response_limits) if self.selected_model is not None: result["selectedModel"] = from_union([from_none, from_str], self.selected_model) if self.session_was_active is not None: @@ -5890,7 +5937,7 @@ class SessionStartData: reasoning_effort: str | None = None reasoning_summary: ReasoningSummary | None = None remote_steerable: bool | None = None - response_budget: ResponseBudgetConfig | None = None + response_limits: ResponseLimitsConfig | None = None selected_model: str | None = None @staticmethod @@ -5908,7 +5955,7 @@ def from_dict(obj: Any) -> "SessionStartData": reasoning_effort = from_union([from_none, from_str], obj.get("reasoningEffort")) reasoning_summary = from_union([from_none, lambda x: parse_enum(ReasoningSummary, x)], obj.get("reasoningSummary")) remote_steerable = from_union([from_none, from_bool], obj.get("remoteSteerable")) - response_budget = from_union([from_none, ResponseBudgetConfig.from_dict], obj.get("responseBudget")) + response_limits = from_union([from_none, ResponseLimitsConfig.from_dict], obj.get("responseLimits")) selected_model = from_union([from_none, from_str], obj.get("selectedModel")) return SessionStartData( copilot_version=copilot_version, @@ -5923,7 +5970,7 @@ def from_dict(obj: Any) -> "SessionStartData": reasoning_effort=reasoning_effort, reasoning_summary=reasoning_summary, remote_steerable=remote_steerable, - response_budget=response_budget, + response_limits=response_limits, selected_model=selected_model, ) @@ -5948,8 +5995,8 @@ def to_dict(self) -> dict: result["reasoningSummary"] = from_union([from_none, lambda x: to_enum(ReasoningSummary, x)], self.reasoning_summary) if self.remote_steerable is not None: result["remoteSteerable"] = from_union([from_none, from_bool], self.remote_steerable) - if self.response_budget is not None: - result["responseBudget"] = from_union([from_none, lambda x: to_class(ResponseBudgetConfig, x)], self.response_budget) + if self.response_limits is not None: + result["responseLimits"] = from_union([from_none, lambda x: to_class(ResponseLimitsConfig, x)], self.response_limits) if self.selected_model is not None: result["selectedModel"] = from_union([from_none, from_str], self.selected_model) return result @@ -7042,33 +7089,42 @@ def to_dict(self) -> dict: @dataclass -class ToolExecutionCompleteContentTerminal: - "Terminal/shell output content block with optional exit code and working directory" - text: str - type: ClassVar[str] = "terminal" +class ToolExecutionCompleteContentShellExit: + "Shell command exit metadata with optional output preview" + exit_code: int + shell_id: str + type: ClassVar[str] = "shell_exit" cwd: str | None = None - exit_code: int | None = None + output_preview: str | None = None + output_truncated: bool | None = None @staticmethod - def from_dict(obj: Any) -> "ToolExecutionCompleteContentTerminal": + def from_dict(obj: Any) -> "ToolExecutionCompleteContentShellExit": assert isinstance(obj, dict) - text = from_str(obj.get("text")) + exit_code = from_int(obj.get("exitCode")) + shell_id = from_str(obj.get("shellId")) cwd = from_union([from_none, from_str], obj.get("cwd")) - exit_code = from_union([from_none, from_int], obj.get("exitCode")) - return ToolExecutionCompleteContentTerminal( - text=text, - cwd=cwd, + output_preview = from_union([from_none, from_str], obj.get("outputPreview")) + output_truncated = from_union([from_none, from_bool], obj.get("outputTruncated")) + return ToolExecutionCompleteContentShellExit( exit_code=exit_code, + shell_id=shell_id, + cwd=cwd, + output_preview=output_preview, + output_truncated=output_truncated, ) def to_dict(self) -> dict: result: dict = {} - result["text"] = from_str(self.text) + result["exitCode"] = to_int(self.exit_code) + result["shellId"] = from_str(self.shell_id) result["type"] = self.type if self.cwd is not None: result["cwd"] = from_union([from_none, from_str], self.cwd) - if self.exit_code is not None: - result["exitCode"] = from_union([from_none, to_int], self.exit_code) + if self.output_preview is not None: + result["outputPreview"] = from_union([from_none, from_str], self.output_preview) + if self.output_truncated is not None: + result["outputTruncated"] = from_union([from_none, from_bool], self.output_truncated) return result @@ -8220,6 +8276,7 @@ def _load_ToolExecutionCompleteContent(obj: Any) -> "ToolExecutionCompleteConten match kind: case "text": return ToolExecutionCompleteContentText.from_dict(obj) case "terminal": return ToolExecutionCompleteContentTerminal.from_dict(obj) + case "shell_exit": return ToolExecutionCompleteContentShellExit.from_dict(obj) case "image": return ToolExecutionCompleteContentImage.from_dict(obj) case "audio": return ToolExecutionCompleteContentAudio.from_dict(obj) case "resource_link": return ToolExecutionCompleteContentResourceLink.from_dict(obj) @@ -8243,7 +8300,7 @@ def _load_UserToolSessionApproval(obj: Any) -> "UserToolSessionApproval": # A content block within a tool result, which may be text, terminal output, image, audio, or a resource -ToolExecutionCompleteContent = ToolExecutionCompleteContentText | ToolExecutionCompleteContentTerminal | ToolExecutionCompleteContentImage | ToolExecutionCompleteContentAudio | ToolExecutionCompleteContentResourceLink | ToolExecutionCompleteContentResource +ToolExecutionCompleteContent = ToolExecutionCompleteContentText | ToolExecutionCompleteContentTerminal | ToolExecutionCompleteContentShellExit | ToolExecutionCompleteContentImage | ToolExecutionCompleteContentAudio | ToolExecutionCompleteContentResourceLink | ToolExecutionCompleteContentResource # A model-facing binary result as persisted: full inline data, a size-omitted marker, or a deduplicated asset reference @@ -8743,7 +8800,7 @@ class WorkspaceFileChangedOperation(Enum): UPDATE = "update" -SessionEventData = SessionStartData | SessionResumeData | SessionRemoteSteerableChangedData | SessionErrorData | SessionIdleData | SessionTitleChangedData | SessionScheduleCreatedData | SessionScheduleCancelledData | SessionScheduleRearmedData | SessionAutopilotObjectiveChangedData | SessionInfoData | SessionWarningData | SessionModelChangeData | SessionModeChangedData | SessionPermissionsChangedData | SessionPlanChangedData | SessionTodosChangedData | SessionWorkspaceFileChangedData | SessionHandoffData | SessionTruncationData | SessionSnapshotRewindData | SessionShutdownData | SessionContextChangedData | SessionUsageInfoData | SessionCompactionStartData | SessionCompactionCompleteData | SessionTaskCompleteData | UserMessageData | PendingMessagesModifiedData | AssistantTurnStartData | AssistantIntentData | AssistantReasoningData | AssistantReasoningDeltaData | AssistantStreamingDeltaData | AssistantMessageData | AssistantMessageStartData | AssistantMessageDeltaData | AssistantTurnEndData | AssistantIdleData | AssistantUsageData | ModelCallFailureData | AbortData | ToolUserRequestedData | ToolExecutionStartData | ToolExecutionPartialResultData | ToolExecutionProgressData | ToolExecutionCompleteData | SkillInvokedData | SubagentStartedData | SubagentCompletedData | SubagentFailedData | SubagentSelectedData | SubagentDeselectedData | HookStartData | HookEndData | HookProgressData | SessionBinaryAssetData | SystemMessageData | SystemNotificationData | PermissionRequestedData | PermissionCompletedData | UserInputRequestedData | UserInputCompletedData | ElicitationRequestedData | ElicitationCompletedData | SamplingRequestedData | SamplingCompletedData | McpOauthRequiredData | McpOauthCompletedData | McpHeadersRefreshRequiredData | McpHeadersRefreshCompletedData | SessionCustomNotificationData | ExternalToolRequestedData | ExternalToolCompletedData | CommandQueuedData | CommandExecuteData | CommandCompletedData | AutoModeSwitchRequestedData | AutoModeSwitchCompletedData | CommandsChangedData | CapabilitiesChangedData | ExitPlanModeRequestedData | ExitPlanModeCompletedData | SessionToolsUpdatedData | SessionBackgroundTasksChangedData | SessionSkillsLoadedData | SessionCustomAgentsUpdatedData | SessionMcpServersLoadedData | SessionMcpServerStatusChangedData | SessionExtensionsLoadedData | SessionCanvasOpenedData | SessionCanvasRegistryChangedData | SessionCanvasClosedData | SessionCanvasUnavailableData | SessionCanvasRecordedData | SessionCanvasRemovedData | SessionExtensionsAttachmentsPushedData | McpAppToolCallCompleteData | RawSessionEventData | Data +SessionEventData = SessionStartData | SessionResumeData | SessionRemoteSteerableChangedData | SessionErrorData | SessionIdleData | SessionTitleChangedData | SessionScheduleCreatedData | SessionScheduleCancelledData | SessionScheduleRearmedData | SessionAutopilotObjectiveChangedData | SessionInfoData | SessionWarningData | SessionModelChangeData | SessionModeChangedData | SessionResponseLimitsChangedData | SessionPermissionsChangedData | SessionPlanChangedData | SessionTodosChangedData | SessionWorkspaceFileChangedData | SessionHandoffData | SessionTruncationData | SessionSnapshotRewindData | SessionShutdownData | SessionContextChangedData | SessionUsageInfoData | SessionCompactionStartData | SessionCompactionCompleteData | SessionTaskCompleteData | UserMessageData | PendingMessagesModifiedData | AssistantTurnStartData | AssistantIntentData | AssistantReasoningData | AssistantReasoningDeltaData | AssistantStreamingDeltaData | AssistantMessageData | AssistantMessageStartData | AssistantMessageDeltaData | AssistantTurnEndData | AssistantIdleData | AssistantUsageData | ModelCallFailureData | AbortData | ToolUserRequestedData | ToolExecutionStartData | ToolExecutionPartialResultData | ToolExecutionProgressData | ToolExecutionCompleteData | SkillInvokedData | SubagentStartedData | SubagentCompletedData | SubagentFailedData | SubagentSelectedData | SubagentDeselectedData | HookStartData | HookEndData | HookProgressData | SessionBinaryAssetData | SystemMessageData | SystemNotificationData | PermissionRequestedData | PermissionCompletedData | UserInputRequestedData | UserInputCompletedData | ElicitationRequestedData | ElicitationCompletedData | SamplingRequestedData | SamplingCompletedData | McpOauthRequiredData | McpOauthCompletedData | McpHeadersRefreshRequiredData | McpHeadersRefreshCompletedData | SessionCustomNotificationData | ExternalToolRequestedData | ExternalToolCompletedData | CommandQueuedData | CommandExecuteData | CommandCompletedData | AutoModeSwitchRequestedData | AutoModeSwitchCompletedData | CommandsChangedData | CapabilitiesChangedData | ExitPlanModeRequestedData | ExitPlanModeCompletedData | SessionToolsUpdatedData | SessionBackgroundTasksChangedData | SessionSkillsLoadedData | SessionCustomAgentsUpdatedData | SessionMcpServersLoadedData | SessionMcpServerStatusChangedData | SessionExtensionsLoadedData | SessionCanvasOpenedData | SessionCanvasRegistryChangedData | SessionCanvasClosedData | SessionCanvasUnavailableData | SessionCanvasRecordedData | SessionCanvasRemovedData | SessionExtensionsAttachmentsPushedData | McpAppToolCallCompleteData | RawSessionEventData | Data @dataclass @@ -8783,6 +8840,7 @@ def from_dict(obj: Any) -> "SessionEvent": case SessionEventType.SESSION_WARNING: data = SessionWarningData.from_dict(data_obj) case SessionEventType.SESSION_MODEL_CHANGE: data = SessionModelChangeData.from_dict(data_obj) case SessionEventType.SESSION_MODE_CHANGED: data = SessionModeChangedData.from_dict(data_obj) + case SessionEventType.SESSION_RESPONSE_LIMITS_CHANGED: data = SessionResponseLimitsChangedData.from_dict(data_obj) case SessionEventType.SESSION_PERMISSIONS_CHANGED: data = SessionPermissionsChangedData.from_dict(data_obj) case SessionEventType.SESSION_PLAN_CHANGED: data = SessionPlanChangedData.from_dict(data_obj) case SessionEventType.SESSION_TODOS_CHANGED: data = SessionTodosChangedData.from_dict(data_obj) @@ -9070,7 +9128,7 @@ def session_event_to_dict(x: SessionEvent) -> Any: "PlanChangedOperation", "RawSessionEventData", "ReasoningSummary", - "ResponseBudgetConfig", + "ResponseLimitsConfig", "SamplingCompletedData", "SamplingRequestedData", "SessionAutopilotObjectiveChangedData", @@ -9104,6 +9162,7 @@ def session_event_to_dict(x: SessionEvent) -> Any: "SessionPermissionsChangedData", "SessionPlanChangedData", "SessionRemoteSteerableChangedData", + "SessionResponseLimitsChangedData", "SessionResumeData", "SessionScheduleCancelledData", "SessionScheduleCreatedData", @@ -9156,6 +9215,7 @@ def session_event_to_dict(x: SessionEvent) -> Any: "ToolExecutionCompleteContentResourceLink", "ToolExecutionCompleteContentResourceLinkIcon", "ToolExecutionCompleteContentResourceLinkIconTheme", + "ToolExecutionCompleteContentShellExit", "ToolExecutionCompleteContentTerminal", "ToolExecutionCompleteContentText", "ToolExecutionCompleteData", diff --git a/rust/src/generated/api_types.rs b/rust/src/generated/api_types.rs index 711aed2e3f..b95c907e3a 100644 --- a/rust/src/generated/api_types.rs +++ b/rust/src/generated/api_types.rs @@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize}; use super::session_events::{ AbortReason, ContextTier, McpServerSource, McpServerStatus, PermissionPromptRequest, - PermissionRule, ReasoningSummary, ResponseBudgetConfig, SessionMode, ShutdownType, SkillSource, + PermissionRule, ReasoningSummary, ResponseLimitsConfig, SessionMode, ShutdownType, SkillSource, UserToolSessionApproval, }; use crate::types::{RequestId, SessionEvent, SessionId}; @@ -93,6 +93,10 @@ pub mod rpc_methods { pub const INSTRUCTIONS_GETDISCOVERYPATHS: &str = "instructions.getDiscoveryPaths"; /// `user.settings.reload` pub const USER_SETTINGS_RELOAD: &str = "user.settings.reload"; + /// `user.settings.get` + pub const USER_SETTINGS_GET: &str = "user.settings.get"; + /// `user.settings.set` + pub const USER_SETTINGS_SET: &str = "user.settings.set"; /// `runtime.shutdown` pub const RUNTIME_SHUTDOWN: &str = "runtime.shutdown"; /// `sessionFs.setProvider` @@ -514,6 +518,10 @@ pub mod rpc_methods { pub const SESSION_REMOTE_DISABLE: &str = "session.remote.disable"; /// `session.remote.notifySteerableChanged` pub const SESSION_REMOTE_NOTIFYSTEERABLECHANGED: &str = "session.remote.notifySteerableChanged"; + /// `session.visibility.get` + pub const SESSION_VISIBILITY_GET: &str = "session.visibility.get"; + /// `session.visibility.set` + pub const SESSION_VISIBILITY_SET: &str = "session.visibility.set"; /// `session.schedule.list` pub const SESSION_SCHEDULE_LIST: &str = "session.schedule.list"; /// `session.schedule.stop` @@ -587,6 +595,13 @@ pub struct AbortResult { } /// Schema for the `AccountAllUsers` type. +/// +///

+/// +/// **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 AccountAllUsers { @@ -598,6 +613,13 @@ pub struct AccountAllUsers { } /// Current authentication state +/// +///
+/// +/// **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 AccountGetCurrentAuthResult { @@ -610,6 +632,13 @@ pub struct AccountGetCurrentAuthResult { } /// Optional GitHub token used to look up quota for a specific user instead of the global auth context. +/// +///
+/// +/// **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 AccountGetQuotaRequest { @@ -619,6 +648,13 @@ pub struct AccountGetQuotaRequest { } /// Schema for the `AccountQuotaSnapshot` type. +/// +///
+/// +/// **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 AccountQuotaSnapshot { @@ -642,6 +678,13 @@ pub struct AccountQuotaSnapshot { } /// Quota usage snapshots for the resolved user, keyed by quota type. +/// +///
+/// +/// **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 AccountGetQuotaResult { @@ -650,6 +693,13 @@ pub struct AccountGetQuotaResult { } /// Credentials to store after successful authentication +/// +///
+/// +/// **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 AccountLoginRequest { @@ -662,6 +712,13 @@ pub struct AccountLoginRequest { } /// Result of a successful login; throws on failure +/// +///
+/// +/// **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 AccountLoginResult { @@ -670,6 +727,13 @@ pub struct AccountLoginResult { } /// User to log out +/// +///
+/// +/// **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 AccountLogoutRequest { @@ -678,6 +742,13 @@ pub struct AccountLogoutRequest { } /// Logout result indicating if more users remain +/// +///
+/// +/// **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 AccountLogoutResult { @@ -1119,6 +1190,13 @@ pub struct AllowAllPermissionState { } /// Schema for the `CopilotUserResponseEndpoints` type. +/// +///
+/// +/// **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 CopilotUserResponseEndpoints { @@ -1133,6 +1211,13 @@ pub struct CopilotUserResponseEndpoints { } /// Schema for the `CopilotUserResponseQuotaSnapshotsChat` type. +/// +///
+/// +/// **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 CopilotUserResponseQuotaSnapshotsChat { @@ -1166,6 +1251,13 @@ pub struct CopilotUserResponseQuotaSnapshotsChat { } /// Schema for the `CopilotUserResponseQuotaSnapshotsCompletions` type. +/// +///
+/// +/// **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 CopilotUserResponseQuotaSnapshotsCompletions { @@ -1199,6 +1291,13 @@ pub struct CopilotUserResponseQuotaSnapshotsCompletions { } /// Schema for the `CopilotUserResponseQuotaSnapshotsPremiumInteractions` type. +/// +///
+/// +/// **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 CopilotUserResponseQuotaSnapshotsPremiumInteractions { @@ -1232,6 +1331,13 @@ pub struct CopilotUserResponseQuotaSnapshotsPremiumInteractions { } /// Schema for the `CopilotUserResponseQuotaSnapshots` type. +/// +///
+/// +/// **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 CopilotUserResponseQuotaSnapshots { @@ -1250,6 +1356,13 @@ pub struct CopilotUserResponseQuotaSnapshots { } /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set. +/// +///
+/// +/// **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 CopilotUserResponse { @@ -1344,6 +1457,13 @@ pub struct CopilotUserResponse { } /// Schema for the `ApiKeyAuthInfo` type. +/// +///
+/// +/// **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 ApiKeyAuthInfo { @@ -2510,6 +2630,13 @@ pub struct ConnectRemoteSessionParams { } /// Optional connection token presented by the SDK client during the handshake. +/// +///
+/// +/// **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(crate) struct ConnectRequest { @@ -2519,6 +2646,13 @@ pub(crate) struct ConnectRequest { } /// Handshake result reporting the server's protocol version and package version on success. +/// +///
+/// +/// **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(crate) struct ConnectResult { @@ -2531,6 +2665,13 @@ pub(crate) struct ConnectResult { } /// Schema for the `CopilotApiTokenAuthInfo` type. +/// +///
+/// +/// **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 CopilotApiTokenAuthInfo { @@ -2598,6 +2739,13 @@ pub struct CurrentToolMetadata { } /// Schema for the `DiscoveredMcpServer` type. +/// +///
+/// +/// **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 DiscoveredMcpServer { @@ -2649,6 +2797,13 @@ pub struct EnqueueCommandResult { } /// Schema for the `EnvAuthInfo` type. +/// +///
+/// +/// **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 EnvAuthInfo { @@ -3039,6 +3194,34 @@ pub struct ExternalToolTextResultForLlmContentResourceLink { pub uri: String, } +/// Shell command exit metadata with optional output preview +/// +///
+/// +/// **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 ExternalToolTextResultForLlmContentShellExit { + /// Working directory where the shell command was executed + #[serde(skip_serializing_if = "Option::is_none")] + pub cwd: Option, + /// Exit code from the completed shell command + pub exit_code: i64, + /// Output associated with this shell command, if available. May be partial, truncated, or a preview; not guaranteed to be full output. + #[serde(skip_serializing_if = "Option::is_none")] + pub output_preview: Option, + /// Whether outputPreview is known to be incomplete or truncated + #[serde(skip_serializing_if = "Option::is_none")] + pub output_truncated: Option, + /// Shell id, as assigned by Copilot runtime + pub shell_id: String, + /// Content block type discriminator + pub r#type: ExternalToolTextResultForLlmContentShellExitType, +} + /// Terminal/shell output content block with optional exit code and working directory /// ///
@@ -3156,6 +3339,13 @@ pub struct FolderTrustCheckResult { } /// Schema for the `GhCliAuthInfo` type. +/// +///
+/// +/// **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 GhCliAuthInfo { @@ -3353,6 +3543,13 @@ pub struct HistoryTruncateResult { } /// Schema for the `HMACAuthInfo` type. +/// +///
+/// +/// **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 HMACAuthInfo { @@ -4370,6 +4567,13 @@ pub struct McpCancelSamplingExecutionResult { } /// MCP server name and configuration to add to user configuration. +/// +///
+/// +/// **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 McpConfigAddRequest { @@ -4380,6 +4584,13 @@ pub struct McpConfigAddRequest { } /// MCP server names to disable for new sessions. +/// +///
+/// +/// **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 McpConfigDisableRequest { @@ -4388,6 +4599,13 @@ pub struct McpConfigDisableRequest { } /// MCP server names to enable for new sessions. +/// +///
+/// +/// **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 McpConfigEnableRequest { @@ -4396,6 +4614,13 @@ pub struct McpConfigEnableRequest { } /// User-configured MCP servers, keyed by server name. +/// +///
+/// +/// **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 McpConfigList { @@ -4404,6 +4629,13 @@ pub struct McpConfigList { } /// MCP server name to remove from user configuration. +/// +///
+/// +/// **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 McpConfigRemoveRequest { @@ -4412,6 +4644,13 @@ pub struct McpConfigRemoveRequest { } /// MCP server name and replacement configuration to write to user configuration. +/// +///
+/// +/// **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 McpConfigUpdateRequest { @@ -4468,6 +4707,13 @@ pub struct McpDisableRequest { } /// Optional working directory used as context for MCP server discovery. +/// +///
+/// +/// **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 McpDiscoverRequest { @@ -4477,6 +4723,13 @@ pub struct McpDiscoverRequest { } /// MCP servers discovered from user, workspace, plugin, and built-in sources. +/// +///
+/// +/// **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 McpDiscoverResult { @@ -4997,6 +5250,13 @@ pub struct McpServer { } /// Authentication settings with optional redirect port configuration. +/// +///
+/// +/// **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 McpServerAuthConfigRedirectPort { @@ -5006,6 +5266,13 @@ pub struct McpServerAuthConfigRedirectPort { } /// Remote MCP server configuration accessed over HTTP or SSE. +/// +///
+/// +/// **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 McpServerConfigHttp { @@ -5050,6 +5317,13 @@ pub struct McpServerConfigHttp { } /// Stdio MCP server configuration launched as a child process. +/// +///
+/// +/// **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 McpServerConfigStdio { @@ -5463,6 +5737,13 @@ pub struct MetadataSnapshotRemoteMetadata { } /// Long context tier pricing (available for models with extended context windows) +/// +///
+/// +/// **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 ModelBillingTokenPricesLongContext { @@ -5494,6 +5775,13 @@ pub struct ModelBillingTokenPricesLongContext { } /// Token-level pricing information for this model +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ModelBillingTokenPrices { @@ -5531,6 +5819,13 @@ pub struct ModelBillingTokenPrices { } /// Billing information +/// +///
+/// +/// **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 ModelBilling { @@ -5546,6 +5841,13 @@ pub struct ModelBilling { } /// Vision-specific limits +/// +///
+/// +/// **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 ModelCapabilitiesLimitsVision { @@ -5561,6 +5863,13 @@ pub struct ModelCapabilitiesLimitsVision { } /// Token limits for prompts, outputs, and context window +/// +///
+/// +/// **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 ModelCapabilitiesLimits { @@ -5582,6 +5891,13 @@ pub struct ModelCapabilitiesLimits { } /// Feature flags indicating what the model supports +/// +///
+/// +/// **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 ModelCapabilitiesSupports { @@ -5594,6 +5910,13 @@ pub struct ModelCapabilitiesSupports { } /// Model capabilities and limits +/// +///
+/// +/// **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 ModelCapabilities { @@ -5606,6 +5929,13 @@ pub struct ModelCapabilities { } /// Policy state (if applicable) +/// +///
+/// +/// **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 ModelPolicy { @@ -5617,6 +5947,13 @@ pub struct ModelPolicy { } /// Schema for the `Model` type. +/// +///
+/// +/// **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 Model { @@ -5741,6 +6078,13 @@ pub struct ModelCapabilitiesOverride { } /// List of Copilot models available to the resolved user, including capabilities and billing metadata. +/// +///
+/// +/// **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 ModelList { @@ -5795,6 +6139,13 @@ pub struct ModelSetReasoningEffortResult { } /// Optional GitHub token used to list models for a specific user instead of the global auth context. +/// +///
+/// +/// **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 ModelsListRequest { @@ -7473,6 +7824,13 @@ pub struct PermissionUrlsSetUnrestrictedModeParams { } /// Optional message to echo back to the caller. +/// +///
+/// +/// **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 PingRequest { @@ -7482,6 +7840,13 @@ pub struct PingRequest { } /// Server liveness response, including the echoed message, current server timestamp, and protocol version. +/// +///
+/// +/// **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 PingResult { @@ -9364,6 +9729,13 @@ pub struct ScheduleStopResult { } /// Secret values to add to the redaction filter. +/// +///
+/// +/// **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 SecretsAddFilterValuesRequest { @@ -9372,6 +9744,13 @@ pub struct SecretsAddFilterValuesRequest { } /// Confirmation that the secret values were registered. +/// +///
+/// +/// **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 SecretsAddFilterValuesResult { @@ -9495,6 +9874,13 @@ pub struct ServerInstructionSourceList { } /// Schema for the `ServerSkill` type. +/// +///
+/// +/// **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 ServerSkill { @@ -9520,6 +9906,13 @@ pub struct ServerSkill { } /// Skills discovered across global and project sources. +/// +///
+/// +/// **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 ServerSkillList { @@ -9897,6 +10290,13 @@ pub struct SessionFsRmRequest { } /// Optional capabilities declared by the provider +/// +///
+/// +/// **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 SessionFsSetProviderCapabilities { @@ -9906,6 +10306,13 @@ pub struct SessionFsSetProviderCapabilities { } /// Initial working directory, session-state path layout, and path conventions used to register the calling SDK client as the session filesystem provider. +/// +///
+/// +/// **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 SessionFsSetProviderRequest { @@ -9921,6 +10328,13 @@ pub struct SessionFsSetProviderRequest { } /// Indicates whether the calling client was registered as the session filesystem provider. +/// +///
+/// +/// **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 SessionFsSetProviderResult { @@ -10262,6 +10676,8 @@ pub struct SessionMetadataSnapshot { /// Remote-session-specific metadata. Populated only when `isRemote` is true. Fields are immutable for the lifetime of the session. #[serde(skip_serializing_if = "Option::is_none")] pub remote_metadata: Option, + /// Current response limits for the session, or null when no limits are active + pub response_limits: Option, /// Currently selected model identifier, if any #[serde(skip_serializing_if = "Option::is_none")] pub selected_model: Option, @@ -10527,9 +10943,9 @@ pub struct SessionOpenOptions { /// Whether this session supports remote steering. #[serde(skip_serializing_if = "Option::is_none")] pub remote_steerable: Option, - /// Initial response budget limits for the session. + /// Initial response limits for the session. #[serde(skip_serializing_if = "Option::is_none")] - pub response_budget: Option, + pub response_limits: Option, /// Whether the host is an interactive UI. #[serde(skip_serializing_if = "Option::is_none")] pub running_in_interactive_mode: Option, @@ -11587,9 +12003,9 @@ pub struct SessionUpdateOptionsParams { /// Reasoning summary mode for supported model clients. #[serde(skip_serializing_if = "Option::is_none")] pub reasoning_summary: Option, - /// Optional response budget limits. Pass null to clear the response budget. + /// Optional response limits. Pass null to clear the response limits. #[serde(skip_serializing_if = "Option::is_none")] - pub response_budget: Option, + pub response_limits: Option, /// Whether the session is running in an interactive UI. #[serde(skip_serializing_if = "Option::is_none")] pub running_in_interactive_mode: Option, @@ -11848,6 +12264,13 @@ pub struct SkillList { } /// Skill names to mark as disabled in global configuration, replacing any previous list. +/// +///
+/// +/// **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 SkillsConfigSetDisabledSkillsRequest { @@ -11871,6 +12294,13 @@ pub struct SkillsDisableRequest { } /// Optional project paths and additional skill directories to include in discovery. +/// +///
+/// +/// **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 SkillsDiscoverRequest { @@ -12584,6 +13014,13 @@ pub struct TelemetrySetFeatureOverridesRequest { } /// Schema for the `TokenAuthInfo` type. +/// +///
+/// +/// **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 TokenAuthInfo { @@ -12599,6 +13036,13 @@ pub struct TokenAuthInfo { } /// Schema for the `Tool` type. +/// +///
+/// +/// **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 Tool { @@ -12618,6 +13062,13 @@ pub struct Tool { } /// Built-in tools available for the requested model, with their parameters and instructions. +/// +///
+/// +/// **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 ToolList { @@ -12653,6 +13104,13 @@ pub struct ToolsGetCurrentMetadataResult { pub struct ToolsInitializeAndValidateResult {} /// Optional model identifier whose tool overrides should be applied to the listing. +/// +///
+/// +/// **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 ToolsListRequest { @@ -13439,6 +13897,13 @@ pub struct UsageGetMetricsResult { } /// Schema for the `UserAuthInfo` type. +/// +///
+/// +/// **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 UserAuthInfo { @@ -13478,6 +13943,127 @@ pub struct UserRequestedShellCommandResult { pub tool_call_id: String, } +/// A single user setting's effective value alongside its default, so consumers can render settings left at their default. +/// +///
+/// +/// **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 UserSettingMetadata { + /// The centrally-known default for this setting (null when no default is registered). + pub default: serde_json::Value, + /// True when the user has not set an explicit value for this setting (i.e. it is left at its default). Reflects whether the user has overridden the key, not whether the effective value happens to equal the default — a key explicitly set to a value identical to the default still reports false. + pub is_default: bool, + /// The effective value: the user's value if set, otherwise the default. + pub value: serde_json::Value, +} + +/// Per-key metadata for every known user setting (settings.json overlaid with the legacy config.json, config.json wins), including settings left at their default. Excludes repository- and enterprise-managed overrides. +/// +///
+/// +/// **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 UserSettingsGetResult { + /// Every known user setting keyed by setting name, each with its effective value, default, and whether it is at the default. + pub settings: HashMap, +} + +/// Partial user settings to write to settings.json. Each top-level key is written individually, replacing the existing value; a key whose value is null is removed. +/// +///
+/// +/// **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 UserSettingsSetRequest { + /// Partial user settings to write, as a free-form object keyed by setting name + pub settings: serde_json::Value, +} + +/// Outcome of writing user settings. +/// +///
+/// +/// **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 UserSettingsSetResult { + /// Top-level keys whose write landed in settings.json but is shadowed by a value still present in the legacy config.json (config.json wins on read). The write does not take effect until the legacy value is removed. + pub shadowed_keys: Vec, +} + +/// Current sharing status and shareable GitHub URL for a session. +/// +///
+/// +/// **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 VisibilityGetResult { + /// Shareable GitHub URL for the session. Present when the session is synced and the URL can be resolved. + #[serde(skip_serializing_if = "Option::is_none")] + pub share_url: Option, + /// Current sharing status. Absent when the session is not synced or the status could not be retrieved (e.g. the user is not authenticated). + #[serde(skip_serializing_if = "Option::is_none")] + pub status: Option, + /// Whether the session has been synced to Mission Control (i.e. has a GitHub task). When false, the session cannot be shared and `status`/`shareUrl` are absent. + pub synced: bool, +} + +/// Desired sharing status for the session. +/// +///
+/// +/// **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 VisibilitySetRequest { + /// Sharing status to apply. "repo" makes the session visible to repository readers; "unshared" restricts it to the creator and collaborators. + pub status: SessionVisibilityStatus, +} + +/// Effective sharing status and shareable GitHub URL after updating session visibility. +/// +///
+/// +/// **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 VisibilitySetResult { + /// Shareable GitHub URL for the session. Present when the session is synced and the URL can be resolved. + #[serde(skip_serializing_if = "Option::is_none")] + pub share_url: Option, + /// Effective sharing status after the update. May differ from the requested status for task types that are already visible to repository readers by default. Absent when the update could not be applied (e.g. the session is not synced or the user is not authenticated). + #[serde(skip_serializing_if = "Option::is_none")] + pub status: Option, + /// Whether the session has been synced to Mission Control (i.e. has a GitHub task). When false, the visibility change could not be applied and `status`/`shareUrl` are absent. + pub synced: bool, +} + /// A single changed file and its unified diff. /// ///
@@ -13815,6 +14401,13 @@ pub struct WorkspaceSummary { } /// List of Copilot models available to the resolved user, including capabilities and billing metadata. +/// +///
+/// +/// **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 ModelsListResult { @@ -13823,6 +14416,13 @@ pub struct ModelsListResult { } /// Built-in tools available for the requested model, with their parameters and instructions. +/// +///
+/// +/// **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 ToolsListResult { @@ -13831,6 +14431,13 @@ pub struct ToolsListResult { } /// User-configured MCP servers, keyed by server name. +/// +///
+/// +/// **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 McpConfigListResult { @@ -13991,6 +14598,13 @@ pub struct PluginsMarketplacesRefreshResult { } /// Skills discovered across global and project sources. +/// +///
+/// +/// **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 SkillsDiscoverResult { @@ -16787,6 +17401,8 @@ pub struct SessionMetadataSnapshotResult { /// Remote-session-specific metadata. Populated only when `isRemote` is true. Fields are immutable for the lifetime of the session. #[serde(skip_serializing_if = "Option::is_none")] pub remote_metadata: Option, + /// Current response limits for the session, or null when no limits are active + pub response_limits: Option, /// Currently selected model identifier, if any #[serde(skip_serializing_if = "Option::is_none")] pub selected_model: Option, @@ -17410,6 +18026,63 @@ pub struct SessionRemoteDisableParams { #[serde(rename_all = "camelCase")] pub struct SessionRemoteNotifySteerableChangedResult {} +/// Identifies the target session. +/// +///
+/// +/// **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 SessionVisibilityGetParams { + /// Target session identifier + pub session_id: SessionId, +} + +/// Current sharing status and shareable GitHub URL for a session. +/// +///
+/// +/// **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 SessionVisibilityGetResult { + /// Shareable GitHub URL for the session. Present when the session is synced and the URL can be resolved. + #[serde(skip_serializing_if = "Option::is_none")] + pub share_url: Option, + /// Current sharing status. Absent when the session is not synced or the status could not be retrieved (e.g. the user is not authenticated). + #[serde(skip_serializing_if = "Option::is_none")] + pub status: Option, + /// Whether the session has been synced to Mission Control (i.e. has a GitHub task). When false, the session cannot be shared and `status`/`shareUrl` are absent. + pub synced: bool, +} + +/// Effective sharing status and shareable GitHub URL after updating session visibility. +/// +///
+/// +/// **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 SessionVisibilitySetResult { + /// Shareable GitHub URL for the session. Present when the session is synced and the URL can be resolved. + #[serde(skip_serializing_if = "Option::is_none")] + pub share_url: Option, + /// Effective sharing status after the update. May differ from the requested status for task types that are already visible to repository readers by default. Absent when the update could not be applied (e.g. the session is not synced or the user is not authenticated). + #[serde(skip_serializing_if = "Option::is_none")] + pub status: Option, + /// Whether the session has been synced to Mission Control (i.e. has a GitHub task). When false, the visibility change could not be applied and `status`/`shareUrl` are absent. + pub synced: bool, +} + /// Identifies the target session. /// ///
@@ -17539,6 +18212,13 @@ pub type McpExecuteSamplingResult = HashMap; pub type UIElicitationResponseContent = HashMap; /// List of all authenticated users +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
pub type AccountGetAllUsersResult = Vec; /// Standard MCP CallToolResult @@ -18142,6 +18822,13 @@ pub enum ConnectedRemoteSessionMetadataKind { } /// Controls how MCP tool result content is filtered: none leaves content unchanged, markdown sanitizes HTML while preserving Markdown-friendly output, and hidden_characters removes characters that can hide directives. +/// +///
+/// +/// **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 ContentFilterMode { /// Leave MCP tool result content unchanged. @@ -18176,6 +18863,13 @@ pub enum CopilotApiTokenAuthInfoType { } /// Server transport type: stdio, http, sse (deprecated), or memory +/// +///
+/// +/// **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 DiscoveredMcpServerType { /// Server communicates over stdio with a local child process. @@ -18388,6 +19082,14 @@ pub enum ExternalToolTextResultForLlmContentResourceLinkType { ResourceLink, } +/// Content block type discriminator +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ExternalToolTextResultForLlmContentShellExitType { + #[serde(rename = "shell_exit")] + #[default] + ShellExit, +} + /// Content block type discriminator #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum ExternalToolTextResultForLlmContentTerminalType { @@ -18929,6 +19631,13 @@ pub enum McpSamplingExecutionAction { } /// Controls if tools provided by this server can be loaded on demand via tool search (auto) or always included in the initial tool list (never) +/// +///
+/// +/// **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 McpServerConfigDeferTools { /// Tools may be deferred under certain conditions @@ -18944,6 +19653,13 @@ pub enum McpServerConfigDeferTools { } /// OAuth grant type to use when authenticating to the remote MCP server. +/// +///
+/// +/// **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 McpServerConfigHttpOauthGrantType { /// Interactive browser-based authorization code flow with PKCE. @@ -18959,6 +19675,13 @@ pub enum McpServerConfigHttpOauthGrantType { } /// Remote transport type. Defaults to "http" when omitted. +/// +///
+/// +/// **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 McpServerConfigHttpType { /// Streamable HTTP transport. @@ -19065,6 +19788,13 @@ pub enum MetadataSnapshotRemoteMetadataTaskType { } /// Model capability category for grouping in the model picker +/// +///
+/// +/// **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 ModelPickerCategory { /// Lightweight model category optimized for faster, lower-cost interactions. @@ -19083,6 +19813,13 @@ pub enum ModelPickerCategory { } /// Relative cost tier for token-based billing users +/// +///
+/// +/// **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 ModelPickerPriceCategory { /// Lowest relative token cost tier. @@ -19104,6 +19841,13 @@ pub enum ModelPickerPriceCategory { } /// Current policy state for this model +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum ModelPolicyState { /// The model is enabled by policy. @@ -20300,6 +21044,13 @@ pub enum SessionFsReaddirWithTypesEntryType { } /// Path conventions used by this filesystem +/// +///
+/// +/// **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 SessionFsSetProviderConventions { /// Paths use Windows path conventions. @@ -20664,6 +21415,28 @@ pub enum SessionSource { Unknown, } +/// Sharing status for a synced session. "repo" makes the session visible to anyone with read access to the repository; "unshared" restricts it to the creator and collaborators. +/// +///
+/// +/// **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 SessionVisibilityStatus { + /// The session is visible to repository readers. + #[serde(rename = "repo")] + Repo, + /// The session is restricted to its creator and collaborators. + #[serde(rename = "unshared")] + Unshared, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// Signal to send (default: SIGTERM) /// ///
diff --git a/rust/src/generated/rpc.rs b/rust/src/generated/rpc.rs index c5a99ece11..610db961c5 100644 --- a/rust/src/generated/rpc.rs +++ b/rust/src/generated/rpc.rs @@ -137,6 +137,14 @@ impl<'a> ClientRpc<'a> { /// # Returns /// /// Server liveness response, including the echoed message, current server timestamp, and protocol version. + /// + ///
+ /// + /// **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 ping(&self, params: PingRequest) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self @@ -157,6 +165,14 @@ impl<'a> ClientRpc<'a> { /// # Returns /// /// Handshake result reporting the server's protocol version and package version on success. + /// + ///
+ /// + /// **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(crate) async fn connect(&self, params: ConnectRequest) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self @@ -181,6 +197,14 @@ impl<'a> ClientRpcAccount<'a> { /// # Returns /// /// Quota usage snapshots for the resolved user, keyed by quota type. + /// + ///
+ /// + /// **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 get_quota(&self) -> Result { let wire_params = serde_json::json!({}); let _value = self @@ -201,6 +225,14 @@ impl<'a> ClientRpcAccount<'a> { /// # Returns /// /// Quota usage snapshots for the resolved user, keyed by quota type. + /// + ///
+ /// + /// **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 get_quota_with_params( &self, params: AccountGetQuotaRequest, @@ -220,6 +252,14 @@ impl<'a> ClientRpcAccount<'a> { /// # Returns /// /// Current authentication state + /// + ///
+ /// + /// **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 get_current_auth(&self) -> Result { let wire_params = serde_json::json!({}); let _value = self @@ -236,6 +276,14 @@ impl<'a> ClientRpcAccount<'a> { /// # Returns /// /// List of all authenticated users + /// + ///
+ /// + /// **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 get_all_users(&self) -> Result { let wire_params = serde_json::json!({}); let _value = self @@ -256,6 +304,14 @@ impl<'a> ClientRpcAccount<'a> { /// # Returns /// /// Result of a successful login; throws on failure + /// + ///
+ /// + /// **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 login(&self, params: AccountLoginRequest) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self @@ -276,6 +332,14 @@ impl<'a> ClientRpcAccount<'a> { /// # Returns /// /// Logout result indicating if more users remain + /// + ///
+ /// + /// **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 logout(&self, params: AccountLogoutRequest) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self @@ -590,6 +654,14 @@ impl<'a> ClientRpcMcp<'a> { /// # Returns /// /// MCP servers discovered from user, workspace, plugin, and built-in sources. + /// + ///
+ /// + /// **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 discover(&self, params: McpDiscoverRequest) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self @@ -614,6 +686,14 @@ impl<'a> ClientRpcMcpConfig<'a> { /// # Returns /// /// User-configured MCP servers, keyed by server name. + /// + ///
+ /// + /// **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 list(&self) -> Result { let wire_params = serde_json::json!({}); let _value = self @@ -630,6 +710,14 @@ impl<'a> ClientRpcMcpConfig<'a> { /// # Parameters /// /// * `params` - MCP server name and configuration to add to user configuration. + /// + ///
+ /// + /// **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 add(&self, params: McpConfigAddRequest) -> Result<(), Error> { let wire_params = serde_json::to_value(params)?; let _value = self @@ -646,6 +734,14 @@ impl<'a> ClientRpcMcpConfig<'a> { /// # Parameters /// /// * `params` - MCP server name and replacement configuration to write to user configuration. + /// + ///
+ /// + /// **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 update(&self, params: McpConfigUpdateRequest) -> Result<(), Error> { let wire_params = serde_json::to_value(params)?; let _value = self @@ -662,6 +758,14 @@ impl<'a> ClientRpcMcpConfig<'a> { /// # Parameters /// /// * `params` - MCP server name to remove from user configuration. + /// + ///
+ /// + /// **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 remove(&self, params: McpConfigRemoveRequest) -> Result<(), Error> { let wire_params = serde_json::to_value(params)?; let _value = self @@ -678,6 +782,14 @@ impl<'a> ClientRpcMcpConfig<'a> { /// # Parameters /// /// * `params` - MCP server names to enable for new sessions. + /// + ///
+ /// + /// **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 enable(&self, params: McpConfigEnableRequest) -> Result<(), Error> { let wire_params = serde_json::to_value(params)?; let _value = self @@ -694,6 +806,14 @@ impl<'a> ClientRpcMcpConfig<'a> { /// # Parameters /// /// * `params` - MCP server names to disable for new sessions. + /// + ///
+ /// + /// **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 disable(&self, params: McpConfigDisableRequest) -> Result<(), Error> { let wire_params = serde_json::to_value(params)?; let _value = self @@ -706,6 +826,14 @@ impl<'a> ClientRpcMcpConfig<'a> { /// Drops this runtime process's in-memory MCP server-definition cache so the next MCP config read observes disk. /// /// Wire method: `mcp.config.reload`. + /// + ///
+ /// + /// **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 reload(&self) -> Result<(), Error> { let wire_params = serde_json::json!({}); let _value = self @@ -730,6 +858,14 @@ impl<'a> ClientRpcModels<'a> { /// # Returns /// /// List of Copilot models available to the resolved user, including capabilities and billing metadata. + /// + ///
+ /// + /// **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 list(&self) -> Result { let wire_params = serde_json::json!({}); let _value = self @@ -750,6 +886,14 @@ impl<'a> ClientRpcModels<'a> { /// # Returns /// /// List of Copilot models available to the resolved user, including capabilities and billing metadata. + /// + ///
+ /// + /// **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 list_with_params(&self, params: ModelsListRequest) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self @@ -1144,6 +1288,14 @@ impl<'a> ClientRpcRuntime<'a> { /// Gracefully shuts down an SDK-owned runtime. The response is sent only after cleanup completes; callers may then terminate the owned runtime process. /// /// Wire method: `runtime.shutdown`. + /// + ///
+ /// + /// **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 shutdown(&self) -> Result<(), Error> { let wire_params = serde_json::json!({}); let _value = self @@ -1172,6 +1324,14 @@ impl<'a> ClientRpcSecrets<'a> { /// # Returns /// /// Confirmation that the secret values were registered. + /// + ///
+ /// + /// **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 add_filter_values( &self, params: SecretsAddFilterValuesRequest, @@ -1203,6 +1363,14 @@ impl<'a> ClientRpcSessionFs<'a> { /// # Returns /// /// Indicates whether the calling client was registered as the session filesystem provider. + /// + ///
+ /// + /// **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 set_provider( &self, params: SessionFsSetProviderRequest, @@ -2210,6 +2378,14 @@ impl<'a> ClientRpcSkills<'a> { /// # Returns /// /// Skills discovered across global and project sources. + /// + ///
+ /// + /// **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 discover(&self, params: SkillsDiscoverRequest) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self @@ -2265,6 +2441,14 @@ impl<'a> ClientRpcSkillsConfig<'a> { /// # Parameters /// /// * `params` - Skill names to mark as disabled in global configuration, replacing any previous list. + /// + ///
+ /// + /// **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 set_disabled_skills( &self, params: SkillsConfigSetDisabledSkillsRequest, @@ -2299,6 +2483,14 @@ impl<'a> ClientRpcTools<'a> { /// # Returns /// /// Built-in tools available for the requested model, with their parameters and instructions. + /// + ///
+ /// + /// **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 list(&self, params: ToolsListRequest) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self @@ -2334,6 +2526,14 @@ impl<'a> ClientRpcUserSettings<'a> { /// Drops this runtime process's in-memory user settings cache so the next settings read observes disk. /// /// Wire method: `user.settings.reload`. + /// + ///
+ /// + /// **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 reload(&self) -> Result<(), Error> { let wire_params = serde_json::json!({}); let _value = self @@ -2342,6 +2542,61 @@ impl<'a> ClientRpcUserSettings<'a> { .await?; Ok(()) } + + /// Lists every known user setting (settings.json overlaid with the legacy config.json, config.json wins), each with its effective value, its default, and whether it is at the default — so settings the user has never set still appear with their default value. Does not include repository- or enterprise-managed overrides that the runtime layers on top at session time. + /// + /// Wire method: `user.settings.get`. + /// + /// # Returns + /// + /// Per-key metadata for every known user setting (settings.json overlaid with the legacy config.json, config.json wins), including settings left at their default. Excludes repository- and enterprise-managed overrides. + /// + ///
+ /// + /// **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 get(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::USER_SETTINGS_GET, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Writes one or more user settings to settings.json, replacing each provided top-level key. A key whose value is null is removed. Returns the keys whose new value is shadowed by a legacy config.json entry (config.json wins on read), which the runtime leaves in place — such writes do not take effect until the legacy value is removed. + /// + /// Wire method: `user.settings.set`. + /// + /// # Parameters + /// + /// * `params` - Partial user settings to write to settings.json. Each top-level key is written individually, replacing the existing value; a key whose value is null is removed. + /// + /// # Returns + /// + /// Outcome of writing user settings. + /// + ///
+ /// + /// **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 set( + &self, + params: UserSettingsSetRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::USER_SETTINGS_SET, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// Typed view over a [`Session`]'s RPC namespace. @@ -2561,6 +2816,13 @@ impl<'a> SessionRpc<'a> { } } + /// `session.visibility.*` sub-namespace. + pub fn visibility(&self) -> SessionRpcVisibility<'a> { + SessionRpcVisibility { + session: self.session, + } + } + /// `session.workspaces.*` sub-namespace. pub fn workspaces(&self) -> SessionRpcWorkspaces<'a> { SessionRpcWorkspaces { @@ -7871,6 +8133,69 @@ impl<'a> SessionRpcUsage<'a> { } } +/// `session.visibility.*` RPCs. +#[derive(Clone, Copy)] +pub struct SessionRpcVisibility<'a> { + pub(crate) session: &'a Session, +} + +impl<'a> SessionRpcVisibility<'a> { + /// Returns the session's current Mission Control sharing status and shareable GitHub URL. Reflects whether the synced session is visible to repository readers ("repo") or restricted to its creator and collaborators ("unshared"). + /// + /// Wire method: `session.visibility.get`. + /// + /// # Returns + /// + /// Current sharing status and shareable GitHub URL for a session. + /// + ///
+ /// + /// **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 get(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_VISIBILITY_GET, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Sets the session's Mission Control sharing status, controlling whether the synced session is visible to repository readers. Returns the effective status and shareable GitHub URL after the change. + /// + /// Wire method: `session.visibility.set`. + /// + /// # Parameters + /// + /// * `params` - Desired sharing status for the session. + /// + /// # Returns + /// + /// Effective sharing status and shareable GitHub URL after updating session visibility. + /// + ///
+ /// + /// **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 set(&self, params: VisibilitySetRequest) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_VISIBILITY_SET, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } +} + /// `session.workspaces.*` RPCs. #[derive(Clone, Copy)] pub struct SessionRpcWorkspaces<'a> { diff --git a/rust/src/generated/session_events.rs b/rust/src/generated/session_events.rs index 36fecc054b..49d8cf5feb 100644 --- a/rust/src/generated/session_events.rs +++ b/rust/src/generated/session_events.rs @@ -37,6 +37,8 @@ pub enum SessionEventType { SessionModelChange, #[serde(rename = "session.mode_changed")] SessionModeChanged, + #[serde(rename = "session.response_limits_changed")] + SessionResponseLimitsChanged, #[serde(rename = "session.permissions_changed")] SessionPermissionsChanged, #[serde(rename = "session.plan_changed")] @@ -294,6 +296,8 @@ pub enum SessionEventData { SessionModelChange(SessionModelChangeData), #[serde(rename = "session.mode_changed")] SessionModeChanged(SessionModeChangedData), + #[serde(rename = "session.response_limits_changed")] + SessionResponseLimitsChanged(SessionResponseLimitsChangedData), #[serde(rename = "session.permissions_changed")] SessionPermissionsChanged(SessionPermissionsChangedData), #[serde(rename = "session.plan_changed")] @@ -562,16 +566,13 @@ pub struct WorkingDirectoryContext { pub repository_host: Option, } -/// Optional response budget limits. +/// Optional response limits. #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct ResponseBudgetConfig { +pub struct ResponseLimitsConfig { /// Maximum AI Credits allowed while responding to one top-level user message. #[serde(skip_serializing_if = "Option::is_none")] pub max_ai_credits: Option, - /// Maximum model-call iterations allowed while responding to one top-level user message. - #[serde(skip_serializing_if = "Option::is_none")] - pub max_model_iterations: Option, } /// Session event "session.start". Session initialization metadata including context and configuration @@ -603,9 +604,9 @@ pub struct SessionStartData { /// Whether this session supports remote steering via GitHub #[serde(skip_serializing_if = "Option::is_none")] pub remote_steerable: Option, - /// Response budget limits configured at session creation time, if any + /// Response limits configured at session creation time, if any #[serde(skip_serializing_if = "Option::is_none")] - pub response_budget: Option, + pub response_limits: Option, /// Model selected at session creation time, if any #[serde(skip_serializing_if = "Option::is_none")] pub selected_model: Option, @@ -647,9 +648,9 @@ pub struct SessionResumeData { /// Whether this session supports remote steering via GitHub #[serde(skip_serializing_if = "Option::is_none")] pub remote_steerable: Option, - /// Response budget limits currently configured at resume time; null when no budget is active + /// Response limits currently configured at resume time; null when no limits are active #[serde(skip_serializing_if = "Option::is_none")] - pub response_budget: Option, + pub response_limits: Option, /// ISO 8601 timestamp when the session was resumed pub resume_time: String, /// Model currently selected at resume time @@ -847,6 +848,14 @@ pub struct SessionModeChangedData { pub previous_mode: SessionMode, } +/// Session event "session.response_limits_changed". Response limits update details. Null clears the limits. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SessionResponseLimitsChangedData { + /// Current response limits for the session, or null when no limits are active + pub response_limits: Option, +} + /// Session event "session.permissions_changed". Permissions change details carrying the aggregate allow-all boolean transition. #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -2013,7 +2022,9 @@ pub struct ToolExecutionCompleteContentText { pub r#type: ToolExecutionCompleteContentTextType, } -/// Terminal/shell output content block with optional exit code and working directory +/// Deprecated for shell command exit metadata. Use ToolExecutionCompleteContentShellExit instead. +#[doc(hidden)] +#[deprecated] #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ToolExecutionCompleteContentTerminal { @@ -2029,6 +2040,27 @@ pub struct ToolExecutionCompleteContentTerminal { pub r#type: ToolExecutionCompleteContentTerminalType, } +/// Shell command exit metadata with optional output preview +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ToolExecutionCompleteContentShellExit { + /// Working directory where the shell command was executed + #[serde(skip_serializing_if = "Option::is_none")] + pub cwd: Option, + /// Exit code from the completed shell command + pub exit_code: i64, + /// Output associated with this shell command, if available. May be partial, truncated, or a preview; not guaranteed to be full output. + #[serde(skip_serializing_if = "Option::is_none")] + pub output_preview: Option, + /// Whether outputPreview is known to be incomplete or truncated + #[serde(skip_serializing_if = "Option::is_none")] + pub output_truncated: Option, + /// Shell id, as assigned by Copilot runtime + pub shell_id: String, + /// Content block type discriminator + pub r#type: ToolExecutionCompleteContentShellExitType, +} + /// Image content block with base64-encoded data #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -4340,6 +4372,14 @@ pub enum ToolExecutionCompleteContentTerminalType { Terminal, } +/// Content block type discriminator +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ToolExecutionCompleteContentShellExitType { + #[serde(rename = "shell_exit")] + #[default] + ShellExit, +} + /// Content block type discriminator #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum ToolExecutionCompleteContentImageType { @@ -4401,6 +4441,7 @@ pub enum ToolExecutionCompleteContentResourceType { pub enum ToolExecutionCompleteContent { Text(ToolExecutionCompleteContentText), Terminal(ToolExecutionCompleteContentTerminal), + ShellExit(ToolExecutionCompleteContentShellExit), Image(ToolExecutionCompleteContentImage), Audio(ToolExecutionCompleteContentAudio), ResourceLink(ToolExecutionCompleteContentResourceLink), diff --git a/test/harness/package-lock.json b/test/harness/package-lock.json index edcdf87e4d..56ef29cc9d 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.66-1", + "@github/copilot": "^1.0.66-2", "@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.66-1", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.66-1.tgz", - "integrity": "sha512-Cf0rTsG1wfdRzGmD9PC0TPYxQojItwo6Hv/Jp6GwakrBswLn4PlxW/pCQA7n3o2DahTQDX2y6Z9olAdx0dHFQA==", + "version": "1.0.66-2", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.66-2.tgz", + "integrity": "sha512-nAhhtfjpryklyombieuu18NK2g+BmEk4/8qvXVj8k+w/63tiVpLxFh865Vf6NQiVh/S7hbjMghTbrptsspYg2w==", "dev": true, "license": "SEE LICENSE IN LICENSE.md", "dependencies": { @@ -513,20 +513,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.66-1", - "@github/copilot-darwin-x64": "1.0.66-1", - "@github/copilot-linux-arm64": "1.0.66-1", - "@github/copilot-linux-x64": "1.0.66-1", - "@github/copilot-linuxmusl-arm64": "1.0.66-1", - "@github/copilot-linuxmusl-x64": "1.0.66-1", - "@github/copilot-win32-arm64": "1.0.66-1", - "@github/copilot-win32-x64": "1.0.66-1" + "@github/copilot-darwin-arm64": "1.0.66-2", + "@github/copilot-darwin-x64": "1.0.66-2", + "@github/copilot-linux-arm64": "1.0.66-2", + "@github/copilot-linux-x64": "1.0.66-2", + "@github/copilot-linuxmusl-arm64": "1.0.66-2", + "@github/copilot-linuxmusl-x64": "1.0.66-2", + "@github/copilot-win32-arm64": "1.0.66-2", + "@github/copilot-win32-x64": "1.0.66-2" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.66-1", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.66-1.tgz", - "integrity": "sha512-HTum+52pVBlrUrUjn/r/Q6kd2c0pvGsi6NyfuaGLRKStSQj00Iz5urYlo0hcq5JKF9eGB7ow+aeYc7BDIUVnhw==", + "version": "1.0.66-2", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.66-2.tgz", + "integrity": "sha512-gjLRtAQOdFQUOTm7nYi+zufkGxMlQlTzUyncQ3W4u1+WdGQbx5fWqMg/yd+j1yMN9PEETyF/ZHZqAaFWkEpQww==", "cpu": [ "arm64" ], @@ -541,9 +541,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.66-1", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.66-1.tgz", - "integrity": "sha512-gniq5/n2nX8cBQncjwvU7nAGYj21ALSknNUqhPWIQYwx+IM6KnGeBgSpldubJCMDjkZkbPYqskVcxTGvw0GGHA==", + "version": "1.0.66-2", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.66-2.tgz", + "integrity": "sha512-wWWBsVwJtRTXqCK8lVpzwbJd3Tm1F23avf942K+PmsGYiZZYNcS5pt4umQRRj0sHKgO/muuA4eg/tMfGNi5fgA==", "cpu": [ "x64" ], @@ -558,9 +558,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.66-1", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.66-1.tgz", - "integrity": "sha512-PG/xIIndXo0NpKYXR8GYPXAA3p/kuf4lsA898Pq+9UH5wU9ybqo5P/n5HBLXNOQnpP8+u9pjL9rPbvtwxMkzaQ==", + "version": "1.0.66-2", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.66-2.tgz", + "integrity": "sha512-j0hjx77JNFR3ZS8z3flY2j5SfGZMfKigYVFpDlTJM8FhfkMCUJ5IUhsZwSTimhHlxrsXuI31S6g0WsZLmBUe6A==", "cpu": [ "arm64" ], @@ -575,9 +575,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.66-1", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.66-1.tgz", - "integrity": "sha512-Tb11uVan2f8YjFLiTvPUC8yLSYdmoMru9J8axZRuiSgOtRfmaJGxHoM/axPYW+874YAn4gSygs7OPUt1C+67Xw==", + "version": "1.0.66-2", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.66-2.tgz", + "integrity": "sha512-vWaNbh4WdwkiI40Thcfbwi8tZFKo06r+Dm9Zfb8uY4wAz3X5PaGeSq+8XrNoV3uaRWltI0ncSIrq5tSOyDtRPg==", "cpu": [ "x64" ], @@ -592,9 +592,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.66-1", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.66-1.tgz", - "integrity": "sha512-GJEVj60B5MeJ8kfnf/dRmyX4EwU4HWL7yUZkrAG6xznSyHHPoTWtZ/tudQX/mf69emXtO7Nt9cLOcNIEdYRPMg==", + "version": "1.0.66-2", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.66-2.tgz", + "integrity": "sha512-LbWy5NlWasBeV/i+Xol+8dW7kbAQr6MF46apbseRNHYkhwyF/417WtLfirP8O2hPuqyU72q/HAQziFXkz14pIQ==", "cpu": [ "arm64" ], @@ -609,9 +609,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.66-1", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.66-1.tgz", - "integrity": "sha512-I5k9mMRuIO+pmPGDiblFXd+HOBJo92XEIBwbZMaAW3qRuyF5UcEFuWlczOCYzcTreXfBqNkG1P9qsBeDDNXfnQ==", + "version": "1.0.66-2", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.66-2.tgz", + "integrity": "sha512-djOu52fGIU7eUhQdUS0K5xB2eFdi8LTTbxvphHWlrN1AD1BdZ+VX9Pk2avt6yCfW+Hh0loh2pNsCbTfNyxvULA==", "cpu": [ "x64" ], @@ -626,9 +626,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.66-1", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.66-1.tgz", - "integrity": "sha512-tUkNUkx5F2TIefY3KDORon3THo256hr/ZVUMEph5fr6xSib4d8gGgNjzok/4kEfIR3a7L/45g0Qi+CzQNtjSdA==", + "version": "1.0.66-2", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.66-2.tgz", + "integrity": "sha512-YQu01atiwoz8XfrHKqvI1xNjnc2IIIxgJDkQ6PxwrWPZ4IO320izwlXbW2ZaOz9yDgjWNis6EJ4Ryz8K+mM6kg==", "cpu": [ "arm64" ], @@ -643,9 +643,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.66-1", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.66-1.tgz", - "integrity": "sha512-ktTbksWav2WSVi8BbTYxD4CJ+OrPximk5zPWff3stsU1MrG0XjZtlML1KUY3d/rrq2lpfZqh0ooF+A4bt8IFsQ==", + "version": "1.0.66-2", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.66-2.tgz", + "integrity": "sha512-4/kTs+lKc67f7KEAQ+Gt3sEBFDSEGoUxJujddV/+fS8EAg9uF2g6e3NzS1I4+htyRM4Oq/Z6xfWjGUgQsi9rfw==", "cpu": [ "x64" ], diff --git a/test/harness/package.json b/test/harness/package.json index 0052364850..98193f99d5 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.66-1", + "@github/copilot": "^1.0.66-2", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "@types/node-forge": "^1.3.14", From e896500300757b760575a79379f966b249a0a55b Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 29 Jun 2026 15:37:07 -0400 Subject: [PATCH 2/5] Fix SDK checks for Copilot 1.0.66-2 Update generated deprecation handling and adjust callback hook E2E coverage to assert the native runtime rejection introduced by the new CLI. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- dotnet/src/Generated/Rpc.cs | 16 + dotnet/src/Generated/SessionEvents.cs | 24 + .../E2E/HookLifecycleAndOutputE2ETests.cs | 405 +---------- dotnet/test/E2E/HooksE2ETests.cs | 174 +---- dotnet/test/E2E/PreMcpToolCallHookE2ETests.cs | 148 +--- dotnet/test/E2E/SubagentHooksE2ETests.cs | 60 +- go/internal/e2e/hooks_e2e_test.go | 299 ++------ go/internal/e2e/hooks_extended_e2e_test.go | 450 ++---------- .../e2e/pre_mcp_tool_call_hook_e2e_test.go | 192 +----- go/internal/e2e/subagent_hooks_e2e_test.go | 86 +-- .../java/com/github/copilot/HooksTest.java | 214 +----- .../copilot/PreMcpToolCallHookTest.java | 149 +--- nodejs/test/e2e/hooks.e2e.test.ts | 170 +---- nodejs/test/e2e/hooks_extended.e2e.test.ts | 358 +--------- .../e2e/pre_mcp_tool_call_hook.e2e.test.ts | 131 +--- nodejs/test/e2e/subagent_hooks.e2e.test.ts | 94 +-- python/e2e/test_hooks_e2e.py | 158 +---- python/e2e/test_hooks_extended_e2e.py | 247 +------ python/e2e/test_pre_mcp_tool_call_hook_e2e.py | 118 +--- python/e2e/test_subagent_hooks_e2e.py | 90 +-- rust/src/generated/api_types.rs | 1 + rust/src/generated/rpc.rs | 1 + rust/src/generated/session_events.rs | 2 + rust/tests/e2e/hooks.rs | 237 ++----- rust/tests/e2e/hooks_extended.rs | 643 ++---------------- rust/tests/e2e/pre_mcp_tool_call_hook.rs | 234 +------ rust/tests/e2e/subagent_hooks.rs | 120 +--- scripts/codegen/csharp.ts | 8 +- scripts/codegen/rust.ts | 4 + 29 files changed, 622 insertions(+), 4211 deletions(-) diff --git a/dotnet/src/Generated/Rpc.cs b/dotnet/src/Generated/Rpc.cs index 78969e8b77..b5fe00305f 100644 --- a/dotnet/src/Generated/Rpc.cs +++ b/dotnet/src/Generated/Rpc.cs @@ -76,7 +76,11 @@ public sealed class ModelBillingTokenPricesLongContext { /// Use cacheReadPrice instead. AI Credits cost per billing batch of cached tokens. [EditorBrowsable(EditorBrowsableState.Never)] +#if NET5_0_OR_GREATER + [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] +#else [Obsolete("This member is deprecated and will be removed in a future version.")] +#endif [JsonPropertyName("cachePrice")] public double? CachePrice { get; set; } @@ -90,7 +94,11 @@ public sealed class ModelBillingTokenPricesLongContext /// Use maxPromptTokens instead. Prompt token budget for the long context tier. The total context window is this value plus the model's max_output_tokens. [EditorBrowsable(EditorBrowsableState.Never)] +#if NET5_0_OR_GREATER + [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] +#else [Obsolete("This member is deprecated and will be removed in a future version.")] +#endif [JsonPropertyName("contextMax")] public long? ContextMax { get; set; } @@ -117,7 +125,11 @@ public sealed class ModelBillingTokenPrices /// Use cacheReadPrice instead. AI Credits cost per billing batch of cached tokens. [EditorBrowsable(EditorBrowsableState.Never)] +#if NET5_0_OR_GREATER + [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] +#else [Obsolete("This member is deprecated and will be removed in a future version.")] +#endif [JsonPropertyName("cachePrice")] public double? CachePrice { get; set; } @@ -131,7 +143,11 @@ public sealed class ModelBillingTokenPrices /// Use maxPromptTokens instead. Prompt token budget for the default tier. The total context window is this value plus the model's max_output_tokens. [EditorBrowsable(EditorBrowsableState.Never)] +#if NET5_0_OR_GREATER + [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] +#else [Obsolete("This member is deprecated and will be removed in a future version.")] +#endif [JsonPropertyName("contextMax")] public long? ContextMax { get; set; } diff --git a/dotnet/src/Generated/SessionEvents.cs b/dotnet/src/Generated/SessionEvents.cs index 7f55dde080..234785d0de 100644 --- a/dotnet/src/Generated/SessionEvents.cs +++ b/dotnet/src/Generated/SessionEvents.cs @@ -2419,7 +2419,11 @@ public sealed partial class AssistantMessageData /// Tool call ID of the parent tool invocation when this event originates from a sub-agent. [EditorBrowsable(EditorBrowsableState.Never)] +#if NET5_0_OR_GREATER + [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] +#else [Obsolete("This member is deprecated and will be removed in a future version.")] +#endif [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("parentToolCallId")] public string? ParentToolCallId { get; set; } @@ -2491,7 +2495,11 @@ public sealed partial class AssistantMessageDeltaData /// Tool call ID of the parent tool invocation when this event originates from a sub-agent. [EditorBrowsable(EditorBrowsableState.Never)] +#if NET5_0_OR_GREATER + [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] +#else [Obsolete("This member is deprecated and will be removed in a future version.")] +#endif [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("parentToolCallId")] public string? ParentToolCallId { get; set; } @@ -2591,7 +2599,11 @@ public sealed partial class AssistantUsageData /// Parent tool call ID when this usage originates from a sub-agent. [EditorBrowsable(EditorBrowsableState.Never)] +#if NET5_0_OR_GREATER + [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] +#else [Obsolete("This member is deprecated and will be removed in a future version.")] +#endif [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("parentToolCallId")] public string? ParentToolCallId { get; set; } @@ -2759,7 +2771,11 @@ public sealed partial class ToolExecutionStartData /// Tool call ID of the parent tool invocation when this event originates from a sub-agent. [EditorBrowsable(EditorBrowsableState.Never)] +#if NET5_0_OR_GREATER + [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] +#else [Obsolete("This member is deprecated and will be removed in a future version.")] +#endif [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("parentToolCallId")] public string? ParentToolCallId { get; set; } @@ -2837,7 +2853,11 @@ public sealed partial class ToolExecutionCompleteData /// Tool call ID of the parent tool invocation when this event originates from a sub-agent. [EditorBrowsable(EditorBrowsableState.Never)] +#if NET5_0_OR_GREATER + [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] +#else [Obsolete("This member is deprecated and will be removed in a future version.")] +#endif [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("parentToolCallId")] public string? ParentToolCallId { get; set; } @@ -5360,7 +5380,11 @@ public sealed partial class ToolExecutionCompleteContentText : ToolExecutionComp /// Deprecated for shell command exit metadata. Use ToolExecutionCompleteContentShellExit instead. /// The terminal variant of . [EditorBrowsable(EditorBrowsableState.Never)] +#if NET5_0_OR_GREATER +[Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] +#else [Obsolete("This member is deprecated and will be removed in a future version.")] +#endif public sealed partial class ToolExecutionCompleteContentTerminal : ToolExecutionCompleteContent { /// diff --git a/dotnet/test/E2E/HookLifecycleAndOutputE2ETests.cs b/dotnet/test/E2E/HookLifecycleAndOutputE2ETests.cs index b9366c134e..e7fd50dd9c 100644 --- a/dotnet/test/E2E/HookLifecycleAndOutputE2ETests.cs +++ b/dotnet/test/E2E/HookLifecycleAndOutputE2ETests.cs @@ -2,394 +2,63 @@ * Copyright (c) Microsoft Corporation. All rights reserved. *--------------------------------------------------------------------------------------------*/ -using Microsoft.Extensions.AI; using Xunit; using Xunit.Abstractions; namespace GitHub.Copilot.Test.E2E; -/// -/// E2E coverage for every handler exposed on : -/// OnPreToolUse, OnPostToolUse, OnPostToolUseFailure, OnUserPromptSubmitted, -/// OnSessionStart, OnSessionEnd, OnErrorOccurred. Output-shape behavior -/// (modifiedPrompt / additionalContext / errorHandling / modifiedArgs / -/// modifiedResult / sessionSummary) is asserted alongside hook invocation. If a -/// new handler is added to SessionHooks, add a corresponding test here. -/// public class HookLifecycleAndOutputE2ETests(E2ETestFixture fixture, ITestOutputHelper output) : E2ETestBase(fixture, "hooks_extended", output) { - private static readonly string[] ValidErrorContexts = ["model_call", "tool_execution", "system", "user_input"]; + private const string UnsupportedSdkHooksMessage = "SDK hook callbacks are no longer supported"; - [Fact] - public async Task Should_Invoke_OnSessionStart_Hook_On_New_Session() + private async Task AssertUnsupportedHooksAsync(SessionHooks hooks) { - var sessionStartInputs = new List(); - CopilotSession? session = null; - session = await CreateSessionAsync(new SessionConfig + var ex = await Assert.ThrowsAnyAsync(() => CreateSessionAsync(new SessionConfig { - Hooks = new SessionHooks - { - OnSessionStart = (input, invocation) => - { - sessionStartInputs.Add(input); - Assert.Equal(session!.SessionId, invocation.SessionId); - return Task.FromResult(null); - }, - }, - }); - - await session.SendAndWaitAsync(new MessageOptions { Prompt = "Say hi" }); - - Assert.NotEmpty(sessionStartInputs); - Assert.Equal("new", sessionStartInputs[0].Source); - Assert.True(sessionStartInputs[0].Timestamp > DateTimeOffset.UnixEpoch); - Assert.False(string.IsNullOrEmpty(sessionStartInputs[0].WorkingDirectory)); - - await session.DisposeAsync(); - } - - [Fact] - public async Task Should_Invoke_OnUserPromptSubmitted_Hook_When_Sending_A_Message() - { - var userPromptInputs = new List(); - CopilotSession? session = null; - session = await CreateSessionAsync(new SessionConfig - { - Hooks = new SessionHooks - { - OnUserPromptSubmitted = (input, invocation) => - { - userPromptInputs.Add(input); - Assert.Equal(session!.SessionId, invocation.SessionId); - return Task.FromResult(null); - }, - }, - }); - - await session.SendAndWaitAsync(new MessageOptions { Prompt = "Say hello" }); - - Assert.NotEmpty(userPromptInputs); - Assert.Contains("Say hello", userPromptInputs[0].Prompt); - Assert.True(userPromptInputs[0].Timestamp > DateTimeOffset.UnixEpoch); - Assert.False(string.IsNullOrEmpty(userPromptInputs[0].WorkingDirectory)); - - await session.DisposeAsync(); - } - - [Fact] - public async Task Should_Invoke_OnSessionEnd_Hook_When_Session_Is_Disconnected() - { - var sessionEndInputs = new List(); - var sessionEndHookInvoked = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - CopilotSession? session = null; - session = await CreateSessionAsync(new SessionConfig - { - Hooks = new SessionHooks - { - OnSessionEnd = (input, invocation) => - { - sessionEndInputs.Add(input); - sessionEndHookInvoked.TrySetResult(input); - Assert.Equal(session!.SessionId, invocation.SessionId); - return Task.FromResult(null); - }, - }, - }); - - await session.SendAndWaitAsync(new MessageOptions { Prompt = "Say hi" }); - - await session.DisposeAsync(); - - await sessionEndHookInvoked.Task.WaitAsync(TimeSpan.FromSeconds(10)); - Assert.NotEmpty(sessionEndInputs); - } - - [Fact] - public async Task Should_Invoke_OnErrorOccurred_Hook_When_Error_Occurs() - { - CopilotSession? session = null; - session = await CreateSessionAsync(new SessionConfig - { - Hooks = new SessionHooks - { - OnErrorOccurred = (input, invocation) => - { - Assert.Equal(session!.SessionId, invocation.SessionId); - Assert.True(input.Timestamp > DateTimeOffset.UnixEpoch); - Assert.False(string.IsNullOrEmpty(input.WorkingDirectory)); - Assert.False(string.IsNullOrEmpty(input.Error)); - Assert.Contains(input.ErrorContext, ValidErrorContexts); - return Task.FromResult(null); - }, - }, - }); - - await session.SendAndWaitAsync(new MessageOptions { Prompt = "Say hi" }); - - // OnErrorOccurred is dispatched by the runtime for actual errors. In a normal - // session it may not fire — this test verifies the hook is properly wired and - // that the session works correctly with it registered. If the hook *did* fire, - // the assertions above would have run. - Assert.False(string.IsNullOrEmpty(session.SessionId)); - - await session.DisposeAsync(); - } - - [Fact] - public async Task Should_Invoke_UserPromptSubmitted_Hook_And_Modify_Prompt() - { - var inputs = new List(); - var session = await CreateSessionAsync(new SessionConfig - { - Hooks = new SessionHooks - { - OnUserPromptSubmitted = (input, invocation) => - { - inputs.Add(input); - Assert.False(string.IsNullOrWhiteSpace(invocation.SessionId)); - return Task.FromResult(new UserPromptSubmittedHookOutput - { - ModifiedPrompt = "Reply with exactly: HOOKED_PROMPT", - }); - }, - }, - }); - - var response = await session.SendAndWaitAsync(new MessageOptions { Prompt = "Say something else" }); - - Assert.NotEmpty(inputs); - Assert.Contains("Say something else", inputs[0].Prompt); - Assert.Contains("HOOKED_PROMPT", response?.Data.Content ?? string.Empty); - } - - [Fact] - public async Task Should_Invoke_SessionStart_Hook() - { - var inputs = new List(); - var session = await CreateSessionAsync(new SessionConfig - { - Hooks = new SessionHooks - { - OnSessionStart = (input, invocation) => - { - inputs.Add(input); - Assert.False(string.IsNullOrWhiteSpace(invocation.SessionId)); - return Task.FromResult(new SessionStartHookOutput - { - AdditionalContext = "Session start hook context.", - }); - }, - }, - }); - - await session.SendAndWaitAsync(new MessageOptions { Prompt = "Say hi" }); - - Assert.NotEmpty(inputs); - Assert.Equal("new", inputs[0].Source); - Assert.False(string.IsNullOrEmpty(inputs[0].WorkingDirectory)); + OnPermissionRequest = PermissionHandler.ApproveAll, + Hooks = hooks, + })); + Assert.Contains(UnsupportedSdkHooksMessage, ex.ToString(), StringComparison.Ordinal); } - [Fact] - public async Task Should_Invoke_SessionEnd_Hook() - { - var inputs = new List(); - var hookInvoked = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var session = await CreateSessionAsync(new SessionConfig + public static IEnumerable HookCases => + [ + [new SessionHooks { - Hooks = new SessionHooks - { - OnSessionEnd = (input, invocation) => - { - inputs.Add(input); - hookInvoked.TrySetResult(input); - Assert.False(string.IsNullOrWhiteSpace(invocation.SessionId)); - return Task.FromResult(new SessionEndHookOutput - { - SessionSummary = "session ended", - }); - }, - }, - }); - - await session.SendAndWaitAsync(new MessageOptions { Prompt = "Say bye" }); - await session.DisposeAsync(); - await hookInvoked.Task.WaitAsync(TimeSpan.FromSeconds(10)); - - Assert.NotEmpty(inputs); - } - - [Fact] - public async Task Should_Register_ErrorOccurred_Hook() - { - var inputs = new List(); - var session = await CreateSessionAsync(new SessionConfig + OnUserPromptSubmitted = (_, _) => Task.FromResult(new UserPromptSubmittedHookOutput { ModifiedPrompt = "not used" }), + }], + [new SessionHooks { - Hooks = new SessionHooks - { - OnErrorOccurred = (input, invocation) => - { - inputs.Add(input); - Assert.False(string.IsNullOrWhiteSpace(invocation.SessionId)); - return Task.FromResult(new ErrorOccurredHookOutput - { - ErrorHandling = "skip", - }); - }, - }, - }); - - await session.SendAndWaitAsync(new MessageOptions + OnSessionStart = (_, _) => Task.FromResult(new SessionStartHookOutput { AdditionalContext = "not used" }), + }], + [new SessionHooks { - Prompt = "Say hi", - }); - - // OnErrorOccurred is dispatched only by genuine runtime errors (e.g. provider - // failures, internal exceptions). A normal turn cannot deterministically trigger - // one, so this test is **registration-only**: it verifies the SDK accepts the hook, - // wires it through to the runtime via session.create, and that the lambda above is - // not invoked inappropriately during a healthy turn. End-to-end coverage of an - // actually-fired ErrorOccurred event would require a fault injection point that - // does not exist in the public surface today. - Assert.Empty(inputs); - Assert.NotNull(session.SessionId); - } - - [Fact] - public async Task Should_Allow_PreToolUse_To_Return_ModifiedArgs_And_SuppressOutput() - { - var inputs = new List(); - var session = await CreateSessionAsync(new SessionConfig + OnSessionEnd = (_, _) => Task.FromResult(new SessionEndHookOutput { SessionSummary = "not used" }), + }], + [new SessionHooks { - OnPermissionRequest = PermissionHandler.ApproveAll, - Tools = - [ - AIFunctionFactory.Create( - (string value) => value, - "echo_value", - "Echoes the supplied value") - ], - Hooks = new SessionHooks - { - OnPreToolUse = (input, invocation) => - { - inputs.Add(input); - if (input.ToolName != "echo_value") - { - return Task.FromResult(new PreToolUseHookOutput - { - PermissionDecision = "allow", - }); - } - - return Task.FromResult(new PreToolUseHookOutput - { - PermissionDecision = "allow", - ModifiedArgs = new Dictionary { ["value"] = "modified by hook" }, - SuppressOutput = false, - }); - }, - }, - }); - - var response = await session.SendAndWaitAsync(new MessageOptions + OnErrorOccurred = (_, _) => Task.FromResult(new ErrorOccurredHookOutput { ErrorHandling = "skip" }), + }], + [new SessionHooks { - Prompt = "Call echo_value with value 'original', then reply with the result.", - }); - - Assert.NotEmpty(inputs); - Assert.Contains(inputs, input => input.ToolName == "echo_value"); - Assert.Contains("modified by hook", response?.Data.Content ?? string.Empty); - } - - [Fact] - public async Task Should_Allow_PostToolUse_To_Return_ModifiedResult() - { - var inputs = new List(); - var session = await CreateSessionAsync(new SessionConfig + OnPreToolUse = (_, _) => Task.FromResult(new PreToolUseHookOutput { PermissionDecision = "allow" }), + }], + [new SessionHooks { - OnPermissionRequest = PermissionHandler.ApproveAll, - Hooks = new SessionHooks - { - OnPostToolUse = (input, invocation) => - { - inputs.Add(input); - if (input.ToolName != "view") - { - return Task.FromResult(null); - } - - return Task.FromResult(new PostToolUseHookOutput - { - ModifiedResult = new ToolResultObject - { - TextResultForLlm = "modified by post hook", - ResultType = "success", - ToolTelemetry = new Dictionary(), - }, - SuppressOutput = false, - }); - }, - }, - }); - - var response = await session.SendAndWaitAsync(new MessageOptions + OnPostToolUse = (_, _) => Task.FromResult(new PostToolUseHookOutput { SuppressOutput = false }), + }], + [new SessionHooks { - Prompt = "Call the view tool to read the current directory, then reply done.", - }); - - Assert.Contains(inputs, input => input.ToolName == "view"); - Assert.Contains("done", (response?.Data.Content ?? string.Empty).ToLowerInvariant()); - } - - [Fact(Skip = "Fails with 1.0.64-0 runtime: built-in tools are not available when hooks restrict availableTools, so the failure path cannot be exercised. Follow up with runtime team.")] - public async Task Should_Invoke_PostToolUseFailure_Hook_For_Failed_Tool_Result() + OnPostToolUse = (_, _) => Task.FromResult(null), + OnPostToolUseFailure = (_, _) => Task.FromResult(new PostToolUseFailureHookOutput { AdditionalContext = "not used" }), + }], + ]; + + [Theory] + [MemberData(nameof(HookCases))] + public async Task Rejects_SDK_Callback_Hooks(SessionHooks hooks) { - var failureInputs = new List(); - var postToolUseInputs = new List(); - CopilotSession? session = null; - session = await CreateSessionAsync(new SessionConfig - { - OnPermissionRequest = PermissionHandler.ApproveAll, - AvailableTools = ["report_intent"], - Hooks = new SessionHooks - { - OnPostToolUse = (input, invocation) => - { - postToolUseInputs.Add(input); - return Task.FromResult(null); - }, - OnPostToolUseFailure = (input, invocation) => - { - failureInputs.Add(input); - Assert.Equal(session!.SessionId, invocation.SessionId); - return Task.FromResult(new PostToolUseFailureHookOutput - { - AdditionalContext = "HOOK_FAILURE_GUIDANCE_APPLIED", - }); - }, - }, - }); - - var response = await session.SendAndWaitAsync(new MessageOptions - { - Prompt = "Call the view tool with path 'missing.txt'. If it fails, use the hook guidance to answer.", - }); - - Assert.Empty(postToolUseInputs); - var input = Assert.Single(failureInputs); - Assert.Equal("view", input.ToolName); - Assert.Contains("does not exist", input.Error); - Assert.NotNull(input.ToolArgs); - Assert.True(input.Timestamp > DateTimeOffset.UnixEpoch); - Assert.False(string.IsNullOrEmpty(input.WorkingDirectory)); - Assert.Contains("HOOK_FAILURE_GUIDANCE_APPLIED", response?.Data.Content ?? string.Empty); - - var exchanges = await WaitForExchangesAsync(2); - var toolMessage = exchanges[^1].Request.Messages.Single(message => message.Role == "tool"); - Assert.Contains("does not exist", toolMessage.StringContent); - Assert.Contains( - exchanges[^1].Request.Messages, - message => (message.StringContent ?? string.Empty).Contains("HOOK_FAILURE_GUIDANCE_APPLIED", StringComparison.Ordinal)); + await AssertUnsupportedHooksAsync(hooks); } } diff --git a/dotnet/test/E2E/HooksE2ETests.cs b/dotnet/test/E2E/HooksE2ETests.cs index ab971c26e6..0b0ad37e6c 100644 --- a/dotnet/test/E2E/HooksE2ETests.cs +++ b/dotnet/test/E2E/HooksE2ETests.cs @@ -2,7 +2,6 @@ * Copyright (c) Microsoft Corporation. All rights reserved. *--------------------------------------------------------------------------------------------*/ -using GitHub.Copilot.Test.Harness; using Xunit; using Xunit.Abstractions; @@ -10,162 +9,43 @@ namespace GitHub.Copilot.Test.E2E; public class HooksE2ETests(E2ETestFixture fixture, ITestOutputHelper output) : E2ETestBase(fixture, "hooks", output) { - [Fact] - public async Task Should_Invoke_PreToolUse_Hook_When_Model_Runs_A_Tool() - { - var preToolUseInputs = new List(); - CopilotSession? session = null; - session = await CreateSessionAsync(new SessionConfig - { - OnPermissionRequest = PermissionHandler.ApproveAll, - Hooks = new SessionHooks - { - OnPreToolUse = (input, invocation) => - { - preToolUseInputs.Add(input); - Assert.Equal(session!.SessionId, invocation.SessionId); - return Task.FromResult(new PreToolUseHookOutput { PermissionDecision = "allow" }); - } - } - }); - - // Create a file for the model to read - await File.WriteAllTextAsync(Path.Combine(Ctx.WorkDir, "hello.txt"), "Hello from the test!"); - - await session.SendAsync(new MessageOptions - { - Prompt = "Read the contents of hello.txt and tell me what it says" - }); - - await TestHelper.GetFinalAssistantMessageAsync(session); - - // Should have received at least one preToolUse hook call - Assert.NotEmpty(preToolUseInputs); - - // Should have received the tool name - Assert.Contains(preToolUseInputs, i => !string.IsNullOrEmpty(i.ToolName)); - } + private const string UnsupportedSdkHooksMessage = "SDK hook callbacks are no longer supported"; - [Fact] - public async Task Should_Invoke_PostToolUse_Hook_After_Model_Runs_A_Tool() + private async Task AssertUnsupportedHooksAsync(SessionHooks hooks) { - var postToolUseInputs = new List(); - CopilotSession? session = null; - session = await CreateSessionAsync(new SessionConfig + var ex = await Assert.ThrowsAnyAsync(() => CreateSessionAsync(new SessionConfig { OnPermissionRequest = PermissionHandler.ApproveAll, - Hooks = new SessionHooks - { - OnPostToolUse = (input, invocation) => - { - postToolUseInputs.Add(input); - Assert.Equal(session!.SessionId, invocation.SessionId); - return Task.FromResult(null); - } - } - }); - - // Create a file for the model to read - await File.WriteAllTextAsync(Path.Combine(Ctx.WorkDir, "world.txt"), "World from the test!"); - - await session.SendAsync(new MessageOptions - { - Prompt = "Read the contents of world.txt and tell me what it says" - }); - - await TestHelper.GetFinalAssistantMessageAsync(session); - - // Should have received at least one postToolUse hook call - Assert.NotEmpty(postToolUseInputs); - - // Should have received the tool name and result - Assert.Contains(postToolUseInputs, i => !string.IsNullOrEmpty(i.ToolName)); - Assert.Contains(postToolUseInputs, i => i.ToolResult != null); + Hooks = hooks, + })); + Assert.Contains(UnsupportedSdkHooksMessage, ex.ToString(), StringComparison.Ordinal); } - [Fact] - public async Task Should_Invoke_Both_PreToolUse_And_PostToolUse_Hooks_For_Single_Tool_Call() - { - var preToolUseInputs = new List(); - var postToolUseInputs = new List(); - - var session = await CreateSessionAsync(new SessionConfig + public static IEnumerable HookCases => + [ + [new SessionHooks { - OnPermissionRequest = PermissionHandler.ApproveAll, - Hooks = new SessionHooks - { - OnPreToolUse = (input, invocation) => - { - preToolUseInputs.Add(input); - return Task.FromResult(new PreToolUseHookOutput { PermissionDecision = "allow" }); - }, - OnPostToolUse = (input, invocation) => - { - postToolUseInputs.Add(input); - return Task.FromResult(null); - } - } - }); - - await File.WriteAllTextAsync(Path.Combine(Ctx.WorkDir, "both.txt"), "Testing both hooks!"); - - await session.SendAsync(new MessageOptions + OnPreToolUse = (_, _) => Task.FromResult(new PreToolUseHookOutput { PermissionDecision = "allow" }), + }], + [new SessionHooks { - Prompt = "Read the contents of both.txt" - }); - - await TestHelper.GetFinalAssistantMessageAsync(session); - - // Both hooks should have been called - Assert.NotEmpty(preToolUseInputs); - Assert.NotEmpty(postToolUseInputs); - - // The same tool should appear in both - var preToolNames = preToolUseInputs.Select(i => i.ToolName).Where(n => !string.IsNullOrEmpty(n)).ToHashSet(); - var postToolNames = postToolUseInputs.Select(i => i.ToolName).Where(n => !string.IsNullOrEmpty(n)).ToHashSet(); - Assert.True(preToolNames.Overlaps(postToolNames), "Expected the same tool to appear in both pre and post hooks"); - } - - [Fact] - public async Task Should_Deny_Tool_Execution_When_PreToolUse_Returns_Deny() - { - var preToolUseInputs = new List(); - - var session = await CreateSessionAsync(new SessionConfig + OnPostToolUse = (_, _) => Task.FromResult(null), + }], + [new SessionHooks { - OnPermissionRequest = PermissionHandler.ApproveAll, - Hooks = new SessionHooks - { - OnPreToolUse = (input, invocation) => - { - preToolUseInputs.Add(input); - // Deny all tool calls - return Task.FromResult(new PreToolUseHookOutput { PermissionDecision = "deny" }); - } - } - }); - - // Create a file - var originalContent = "Original content that should not be modified"; - await File.WriteAllTextAsync(Path.Combine(Ctx.WorkDir, "protected.txt"), originalContent); - - await session.SendAsync(new MessageOptions + OnPreToolUse = (_, _) => Task.FromResult(new PreToolUseHookOutput { PermissionDecision = "deny" }), + }], + [new SessionHooks { - Prompt = "Edit protected.txt and replace 'Original' with 'Modified'" - }); + OnPreToolUse = (_, _) => Task.FromResult(new PreToolUseHookOutput { PermissionDecision = "allow" }), + OnPostToolUse = (_, _) => Task.FromResult(null), + }], + ]; - var response = await TestHelper.GetFinalAssistantMessageAsync(session); - - // The hook should have been called - Assert.NotEmpty(preToolUseInputs); - - // The response should be defined - Assert.NotNull(response); - - // Strengthen: verify the actual deny behavior — the protected file was NOT - // modified by the runtime even though the LLM tried to edit it. The pre-tool-use - // hook denial blocks tool execution before it can mutate state. - var actualContent = await File.ReadAllTextAsync(Path.Join(Ctx.WorkDir, "protected.txt")); - Assert.Equal(originalContent, actualContent); + [Theory] + [MemberData(nameof(HookCases))] + public async Task Rejects_SDK_Callback_Hooks(SessionHooks hooks) + { + await AssertUnsupportedHooksAsync(hooks); } } diff --git a/dotnet/test/E2E/PreMcpToolCallHookE2ETests.cs b/dotnet/test/E2E/PreMcpToolCallHookE2ETests.cs index 8e5240a9a8..37b41e970b 100644 --- a/dotnet/test/E2E/PreMcpToolCallHookE2ETests.cs +++ b/dotnet/test/E2E/PreMcpToolCallHookE2ETests.cs @@ -2,163 +2,27 @@ * Copyright (c) Microsoft Corporation. All rights reserved. *--------------------------------------------------------------------------------------------*/ -using System.Text.Json; using Xunit; using Xunit.Abstractions; namespace GitHub.Copilot.Test.E2E; -/// -/// E2E tests for the preMcpToolCall hook, verifying meta manipulation scenarios: -/// setting meta, replacing meta, and removing meta. -/// public class PreMcpToolCallHookE2ETests(E2ETestFixture fixture, ITestOutputHelper output) : E2ETestBase(fixture, "pre_mcp_tool_call_hook", output) { - private static string FindMetaEchoTestHarnessDir() - { - var dir = new DirectoryInfo(AppContext.BaseDirectory); - while (dir != null) - { - var candidate = Path.Combine(dir.FullName, "test", "harness", "test-mcp-meta-echo-server.mjs"); - if (File.Exists(candidate)) - return Path.GetDirectoryName(candidate)!; - dir = dir.Parent; - } - throw new InvalidOperationException("Could not find test/harness/test-mcp-meta-echo-server.mjs"); - } - - private static Dictionary CreateMetaEchoMcpConfig(string testHarnessDir) => new() - { - ["meta-echo"] = new McpStdioServerConfig - { - Command = "node", - Args = [Path.Combine(testHarnessDir, "test-mcp-meta-echo-server.mjs")], - WorkingDirectory = testHarnessDir, - Tools = ["*"] - } - }; - - [Fact] - public async Task Should_Set_Meta_Via_PreMcpToolCall_Hook() - { - var testHarnessDir = FindMetaEchoTestHarnessDir(); - var hookInputs = new List(); - - var session = await CreateSessionAsync(new SessionConfig - { - McpServers = CreateMetaEchoMcpConfig(testHarnessDir), - Hooks = new SessionHooks - { - OnPreMcpToolCall = (input, invocation) => - { - hookInputs.Add(input); - using var doc = JsonDocument.Parse("""{"injected":"by-hook","source":"test"}"""); - return Task.FromResult(new PreMcpToolCallHookOutput - { - MetaToUse = doc.RootElement.Clone() - }); - }, - }, - OnPermissionRequest = PermissionHandler.ApproveAll, - }); - - var message = await session.SendAndWaitAsync(new MessageOptions - { - Prompt = "Use the meta-echo/echo_meta tool with value 'test-set'. Reply with just the raw tool result." - }); - - Assert.NotNull(message); - Assert.Contains("injected", message!.Data.Content); - Assert.Contains("by-hook", message.Data.Content); - - Assert.NotEmpty(hookInputs); - Assert.Equal("meta-echo", hookInputs[0].ServerName); - Assert.Equal("echo_meta", hookInputs[0].ToolName); - Assert.False(string.IsNullOrEmpty(hookInputs[0].WorkingDirectory)); - Assert.True(hookInputs[0].Timestamp > DateTimeOffset.UnixEpoch); - - await session.DisposeAsync(); - } + private const string UnsupportedSdkHooksMessage = "SDK hook callbacks are no longer supported"; [Fact] - public async Task Should_Replace_Meta_Via_PreMcpToolCall_Hook() + public async Task Rejects_SDK_PreMcpToolCall_Callback_Hooks() { - var testHarnessDir = FindMetaEchoTestHarnessDir(); - var hookInputs = new List(); - - var session = await CreateSessionAsync(new SessionConfig + var ex = await Assert.ThrowsAnyAsync(() => CreateSessionAsync(new SessionConfig { - McpServers = CreateMetaEchoMcpConfig(testHarnessDir), - Hooks = new SessionHooks - { - OnPreMcpToolCall = (input, invocation) => - { - hookInputs.Add(input); - // Completely replace: ignore input.Meta entirely - using var doc = JsonDocument.Parse("""{"completely":"replaced"}"""); - return Task.FromResult(new PreMcpToolCallHookOutput - { - MetaToUse = doc.RootElement.Clone() - }); - }, - }, OnPermissionRequest = PermissionHandler.ApproveAll, - }); - - var message = await session.SendAndWaitAsync(new MessageOptions - { - Prompt = "Use the meta-echo/echo_meta tool with value 'test-replace'. Reply with just the raw tool result." - }); - - Assert.NotNull(message); - Assert.Contains("completely", message!.Data.Content); - Assert.Contains("replaced", message.Data.Content); - - Assert.NotEmpty(hookInputs); - Assert.Equal("meta-echo", hookInputs[0].ServerName); - Assert.Equal("echo_meta", hookInputs[0].ToolName); - - await session.DisposeAsync(); - } - - [Fact] - public async Task Should_Remove_Meta_Via_PreMcpToolCall_Hook() - { - var testHarnessDir = FindMetaEchoTestHarnessDir(); - var hookInputs = new List(); - - var session = await CreateSessionAsync(new SessionConfig - { - McpServers = CreateMetaEchoMcpConfig(testHarnessDir), Hooks = new SessionHooks { - OnPreMcpToolCall = (input, invocation) => - { - hookInputs.Add(input); - // Return output with null MetaToUse to signal removal - return Task.FromResult(new PreMcpToolCallHookOutput - { - MetaToUse = null - }); - }, + OnPreMcpToolCall = (_, _) => Task.FromResult(new PreMcpToolCallHookOutput()), }, - OnPermissionRequest = PermissionHandler.ApproveAll, - }); - - var message = await session.SendAndWaitAsync(new MessageOptions - { - Prompt = "Use the meta-echo/echo_meta tool with value 'test-remove'. Reply with just the raw tool result." - }); - - Assert.NotNull(message); - Assert.Contains("\"meta\":null", message!.Data.Content); - Assert.Contains("test-remove", message.Data.Content); - - Assert.NotEmpty(hookInputs); - Assert.Equal("meta-echo", hookInputs[0].ServerName); - Assert.Equal("echo_meta", hookInputs[0].ToolName); - - await session.DisposeAsync(); + })); + Assert.Contains(UnsupportedSdkHooksMessage, ex.ToString(), StringComparison.Ordinal); } } diff --git a/dotnet/test/E2E/SubagentHooksE2ETests.cs b/dotnet/test/E2E/SubagentHooksE2ETests.cs index 5c85432152..4bef6a95b5 100644 --- a/dotnet/test/E2E/SubagentHooksE2ETests.cs +++ b/dotnet/test/E2E/SubagentHooksE2ETests.cs @@ -2,8 +2,6 @@ * Copyright (c) Microsoft Corporation. All rights reserved. *--------------------------------------------------------------------------------------------*/ -using System.Collections.Concurrent; -using GitHub.Copilot.Test.Harness; using Xunit; using Xunit.Abstractions; @@ -12,62 +10,20 @@ namespace GitHub.Copilot.Test.E2E; public class SubagentHooksE2ETests(E2ETestFixture fixture, ITestOutputHelper output) : E2ETestBase(fixture, "subagent_hooks", output) { + private const string UnsupportedSdkHooksMessage = "SDK hook callbacks are no longer supported"; + [Fact] - public async Task Should_Invoke_PreToolUse_And_PostToolUse_Hooks_For_Sub_Agent_Tool_Calls() + public async Task Rejects_SDK_Callback_Hooks_For_Sub_Agent_Hook_Propagation() { - var hookLog = new ConcurrentBag<(string Kind, string ToolName, string SessionId)>(); - - // Create a client with the session-based subagents feature flag - var env = new Dictionary(Ctx.GetEnvironment()); - env["COPILOT_EXP_COPILOT_CLI_SESSION_BASED_SUBAGENTS"] = "true"; - var client = Ctx.CreateClient(options: new CopilotClientOptions { Environment = env }); - - var session = await client.CreateSessionAsync(new SessionConfig + var ex = await Assert.ThrowsAnyAsync(() => CreateSessionAsync(new SessionConfig { OnPermissionRequest = PermissionHandler.ApproveAll, Hooks = new SessionHooks { - OnPreToolUse = (input, invocation) => - { - hookLog.Add(("pre", input.ToolName, input.SessionId)); - return Task.FromResult(new PreToolUseHookOutput - { - PermissionDecision = "allow" - }); - }, - OnPostToolUse = (input, invocation) => - { - hookLog.Add(("post", input.ToolName, input.SessionId)); - return Task.FromResult(null); - }, + OnPreToolUse = (_, _) => Task.FromResult(new PreToolUseHookOutput { PermissionDecision = "allow" }), + OnPostToolUse = (_, _) => Task.FromResult(null), }, - }); - - // Create a file for the sub-agent to read - await File.WriteAllTextAsync(Path.Combine(Ctx.WorkDir, "subagent-test.txt"), "Hello from subagent test!"); - - await session.SendAndWaitAsync( - new MessageOptions - { - Prompt = "Use the task tool to spawn an explore agent that reads the file " - + "subagent-test.txt in the current directory and reports its contents. " - + "You must use the task tool." - }, - timeout: TimeSpan.FromSeconds(120)); - - var log = hookLog.ToArray(); - - // Parent tool hooks fire for "task" - var taskPre = log.Where(h => h.Kind == "pre" && h.ToolName == "task").ToArray(); - Assert.True(taskPre.Length >= 1, "preToolUse should fire for the parent's 'task' tool call"); - - // Sub-agent tool hooks fire for "view" - var viewPre = log.Where(h => h.Kind == "pre" && h.ToolName == "view").ToArray(); - var viewPost = log.Where(h => h.Kind == "post" && h.ToolName == "view").ToArray(); - Assert.True(viewPre.Length > 0, "preToolUse should fire for the sub-agent's 'view' tool call"); - Assert.True(viewPost.Length > 0, "postToolUse should fire for the sub-agent's 'view' tool call"); - - // input.SessionId distinguishes parent from sub-agent - Assert.NotEqual(viewPre[0].SessionId, taskPre[0].SessionId); + })); + Assert.Contains(UnsupportedSdkHooksMessage, ex.ToString(), StringComparison.Ordinal); } } diff --git a/go/internal/e2e/hooks_e2e_test.go b/go/internal/e2e/hooks_e2e_test.go index 5e392fa895..faf55efa3f 100644 --- a/go/internal/e2e/hooks_e2e_test.go +++ b/go/internal/e2e/hooks_e2e_test.go @@ -1,273 +1,68 @@ package e2e import ( - "os" - "path/filepath" - "sync" + "strings" "testing" copilot "github.com/github/copilot-sdk/go" "github.com/github/copilot-sdk/go/internal/e2e/testharness" ) +const unsupportedSDKHooksMessage = "SDK hook callbacks are no longer supported" + +func assertUnsupportedHooks(t *testing.T, client *copilot.Client, hooks *copilot.SessionHooks) { + t.Helper() + + session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ + OnPermissionRequest: copilot.PermissionHandler.ApproveAll, + Hooks: hooks, + }) + if err == nil { + if session != nil { + _ = session.Disconnect() + } + t.Fatal("expected SDK callback hooks to be rejected") + } + if !strings.Contains(err.Error(), unsupportedSDKHooksMessage) { + t.Fatalf("expected unsupported hooks error, got %v", err) + } +} + func TestHooksE2E(t *testing.T) { ctx := testharness.NewTestContext(t) client := ctx.NewClient() t.Cleanup(func() { client.ForceStop() }) - t.Run("should invoke preToolUse hook when model runs a tool", func(t *testing.T) { - ctx.ConfigureForTest(t) - - var preToolUseInputs []copilot.PreToolUseHookInput - var mu sync.Mutex - - session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ - OnPermissionRequest: copilot.PermissionHandler.ApproveAll, - Hooks: &copilot.SessionHooks{ - OnPreToolUse: func(input copilot.PreToolUseHookInput, invocation copilot.HookInvocation) (*copilot.PreToolUseHookOutput, error) { - mu.Lock() - preToolUseInputs = append(preToolUseInputs, input) - mu.Unlock() - - if invocation.SessionID == "" { - t.Error("Expected non-empty session ID in invocation") - } - - return &copilot.PreToolUseHookOutput{PermissionDecision: "allow"}, nil - }, + cases := map[string]*copilot.SessionHooks{ + "preToolUse": { + OnPreToolUse: func(copilot.PreToolUseHookInput, copilot.HookInvocation) (*copilot.PreToolUseHookOutput, error) { + return &copilot.PreToolUseHookOutput{PermissionDecision: "allow"}, nil }, - }) - if err != nil { - t.Fatalf("Failed to create session: %v", err) - } - - // Create a file for the model to read - testFile := filepath.Join(ctx.WorkDir, "hello.txt") - err = os.WriteFile(testFile, []byte("Hello from the test!"), 0644) - if err != nil { - t.Fatalf("Failed to write test file: %v", err) - } - - _, err = session.SendAndWait(t.Context(), copilot.MessageOptions{ - Prompt: "Read the contents of hello.txt and tell me what it says", - }) - if err != nil { - t.Fatalf("Failed to send message: %v", err) - } - - mu.Lock() - defer mu.Unlock() - - if len(preToolUseInputs) == 0 { - t.Error("Expected at least one preToolUse hook call") - } - - hasToolName := false - for _, input := range preToolUseInputs { - if input.ToolName != "" { - hasToolName = true - break - } - } - if !hasToolName { - t.Error("Expected at least one input with a tool name") - } - }) - - t.Run("should invoke postToolUse hook after model runs a tool", func(t *testing.T) { - ctx.ConfigureForTest(t) - - var postToolUseInputs []copilot.PostToolUseHookInput - var mu sync.Mutex - - session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ - OnPermissionRequest: copilot.PermissionHandler.ApproveAll, - Hooks: &copilot.SessionHooks{ - OnPostToolUse: func(input copilot.PostToolUseHookInput, invocation copilot.HookInvocation) (*copilot.PostToolUseHookOutput, error) { - mu.Lock() - postToolUseInputs = append(postToolUseInputs, input) - mu.Unlock() - - if invocation.SessionID == "" { - t.Error("Expected non-empty session ID in invocation") - } - - return nil, nil - }, + }, + "postToolUse": { + OnPostToolUse: func(copilot.PostToolUseHookInput, copilot.HookInvocation) (*copilot.PostToolUseHookOutput, error) { + return nil, nil }, - }) - if err != nil { - t.Fatalf("Failed to create session: %v", err) - } - - // Create a file for the model to read - testFile := filepath.Join(ctx.WorkDir, "world.txt") - err = os.WriteFile(testFile, []byte("World from the test!"), 0644) - if err != nil { - t.Fatalf("Failed to write test file: %v", err) - } - - _, err = session.SendAndWait(t.Context(), copilot.MessageOptions{ - Prompt: "Read the contents of world.txt and tell me what it says", - }) - if err != nil { - t.Fatalf("Failed to send message: %v", err) - } - - mu.Lock() - defer mu.Unlock() - - if len(postToolUseInputs) == 0 { - t.Error("Expected at least one postToolUse hook call") - } - - hasToolName := false - hasResult := false - for _, input := range postToolUseInputs { - if input.ToolName != "" { - hasToolName = true - } - if input.ToolResult != nil { - hasResult = true - } - } - if !hasToolName { - t.Error("Expected at least one input with a tool name") - } - if !hasResult { - t.Error("Expected at least one input with a tool result") - } - }) - - t.Run("should invoke both preToolUse and postToolUse hooks for a single tool call", func(t *testing.T) { - ctx.ConfigureForTest(t) - - var preToolUseInputs []copilot.PreToolUseHookInput - var postToolUseInputs []copilot.PostToolUseHookInput - var mu sync.Mutex - - session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ - OnPermissionRequest: copilot.PermissionHandler.ApproveAll, - Hooks: &copilot.SessionHooks{ - OnPreToolUse: func(input copilot.PreToolUseHookInput, invocation copilot.HookInvocation) (*copilot.PreToolUseHookOutput, error) { - mu.Lock() - preToolUseInputs = append(preToolUseInputs, input) - mu.Unlock() - return &copilot.PreToolUseHookOutput{PermissionDecision: "allow"}, nil - }, - OnPostToolUse: func(input copilot.PostToolUseHookInput, invocation copilot.HookInvocation) (*copilot.PostToolUseHookOutput, error) { - mu.Lock() - postToolUseInputs = append(postToolUseInputs, input) - mu.Unlock() - return nil, nil - }, + }, + "preToolUse denial": { + OnPreToolUse: func(copilot.PreToolUseHookInput, copilot.HookInvocation) (*copilot.PreToolUseHookOutput, error) { + return &copilot.PreToolUseHookOutput{PermissionDecision: "deny"}, nil }, - }) - if err != nil { - t.Fatalf("Failed to create session: %v", err) - } - - testFile := filepath.Join(ctx.WorkDir, "both.txt") - err = os.WriteFile(testFile, []byte("Testing both hooks!"), 0644) - if err != nil { - t.Fatalf("Failed to write test file: %v", err) - } - - _, err = session.SendAndWait(t.Context(), copilot.MessageOptions{ - Prompt: "Read the contents of both.txt", - }) - if err != nil { - t.Fatalf("Failed to send message: %v", err) - } - - mu.Lock() - defer mu.Unlock() - - if len(preToolUseInputs) == 0 { - t.Error("Expected at least one preToolUse hook call") - } - if len(postToolUseInputs) == 0 { - t.Error("Expected at least one postToolUse hook call") - } - - // Check that the same tool appears in both - preToolNames := make(map[string]bool) - for _, input := range preToolUseInputs { - if input.ToolName != "" { - preToolNames[input.ToolName] = true - } - } - - foundCommon := false - for _, input := range postToolUseInputs { - if preToolNames[input.ToolName] { - foundCommon = true - break - } - } - if !foundCommon { - t.Error("Expected the same tool to appear in both pre and post hooks") - } - }) - - t.Run("should deny tool execution when preToolUse returns deny", func(t *testing.T) { - ctx.ConfigureForTest(t) - - var preToolUseInputs []copilot.PreToolUseHookInput - var mu sync.Mutex - - session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ - OnPermissionRequest: copilot.PermissionHandler.ApproveAll, - Hooks: &copilot.SessionHooks{ - OnPreToolUse: func(input copilot.PreToolUseHookInput, invocation copilot.HookInvocation) (*copilot.PreToolUseHookOutput, error) { - mu.Lock() - preToolUseInputs = append(preToolUseInputs, input) - mu.Unlock() - // Deny all tool calls - return &copilot.PreToolUseHookOutput{PermissionDecision: "deny"}, nil - }, + }, + "combined preToolUse and postToolUse": { + OnPreToolUse: func(copilot.PreToolUseHookInput, copilot.HookInvocation) (*copilot.PreToolUseHookOutput, error) { + return &copilot.PreToolUseHookOutput{PermissionDecision: "allow"}, nil }, - }) - if err != nil { - t.Fatalf("Failed to create session: %v", err) - } - - // Create a file - originalContent := "Original content that should not be modified" - testFile := filepath.Join(ctx.WorkDir, "protected.txt") - err = os.WriteFile(testFile, []byte(originalContent), 0644) - if err != nil { - t.Fatalf("Failed to write test file: %v", err) - } + OnPostToolUse: func(copilot.PostToolUseHookInput, copilot.HookInvocation) (*copilot.PostToolUseHookOutput, error) { + return nil, nil + }, + }, + } - response, err := session.SendAndWait(t.Context(), copilot.MessageOptions{ - Prompt: "Edit protected.txt and replace 'Original' with 'Modified'", + for name, hooks := range cases { + t.Run("rejects SDK callback hook "+name, func(t *testing.T) { + ctx.ConfigureForTest(t) + assertUnsupportedHooks(t, client, hooks) }) - if err != nil { - t.Fatalf("Failed to send message: %v", err) - } - - mu.Lock() - defer mu.Unlock() - - if len(preToolUseInputs) == 0 { - t.Error("Expected at least one preToolUse hook call") - } - - // The response should be defined - if response == nil { - t.Error("Expected non-nil response") - } - - // Strengthen: verify the actual deny behavior — the protected file was NOT - // modified by the runtime even though the LLM tried to edit it. The - // pre-tool-use hook denial blocks tool execution before it can mutate state. - actualContent, readErr := os.ReadFile(testFile) - if readErr != nil { - t.Fatalf("Failed to read protected.txt: %v", readErr) - } - if string(actualContent) != originalContent { - t.Errorf("protected.txt should be unchanged after deny; got: %q", string(actualContent)) - } - }) + } } diff --git a/go/internal/e2e/hooks_extended_e2e_test.go b/go/internal/e2e/hooks_extended_e2e_test.go index f53dd13f6a..137e796364 100644 --- a/go/internal/e2e/hooks_extended_e2e_test.go +++ b/go/internal/e2e/hooks_extended_e2e_test.go @@ -1,419 +1,81 @@ package e2e import ( - "fmt" "strings" - "sync" "testing" - "time" copilot "github.com/github/copilot-sdk/go" "github.com/github/copilot-sdk/go/internal/e2e/testharness" ) -// Mirrors dotnet/test/HookLifecycleAndOutputTests.cs (snapshot category "hooks_extended"). -// -// Covers each handler exposed on copilot.SessionHooks: OnPreToolUse, -// OnPostToolUse, OnPostToolUseFailure, OnUserPromptSubmitted, OnSessionStart, -// OnSessionEnd, OnErrorOccurred. Output-shape behavior (modifiedPrompt / -// additionalContext / errorHandling / modifiedArgs / modifiedResult / -// sessionSummary) is asserted alongside hook invocation. If a new handler is -// added to SessionHooks, add a corresponding test here. +func assertUnsupportedExtendedHooks(t *testing.T, client *copilot.Client, hooks *copilot.SessionHooks) { + t.Helper() + + session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ + OnPermissionRequest: copilot.PermissionHandler.ApproveAll, + Hooks: hooks, + }) + if err == nil { + if session != nil { + _ = session.Disconnect() + } + t.Fatal("expected SDK callback hooks to be rejected") + } + if !strings.Contains(err.Error(), unsupportedSDKHooksMessage) { + t.Fatalf("expected unsupported hooks error, got %v", err) + } +} + func TestHooksExtendedE2E(t *testing.T) { ctx := testharness.NewTestContext(t) client := ctx.NewClient() t.Cleanup(func() { client.ForceStop() }) - t.Run("should invoke userPromptSubmitted hook and modify prompt", func(t *testing.T) { - ctx.ConfigureForTest(t) - - var ( - mu sync.Mutex - inputs []copilot.UserPromptSubmittedHookInput - ) - - session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ - OnPermissionRequest: copilot.PermissionHandler.ApproveAll, - Hooks: &copilot.SessionHooks{ - OnUserPromptSubmitted: func(input copilot.UserPromptSubmittedHookInput, invocation copilot.HookInvocation) (*copilot.UserPromptSubmittedHookOutput, error) { - mu.Lock() - inputs = append(inputs, input) - mu.Unlock() - if invocation.SessionID == "" { - t.Error("Expected non-empty session ID in invocation") - } - return &copilot.UserPromptSubmittedHookOutput{ - ModifiedPrompt: "Reply with exactly: HOOKED_PROMPT", - }, nil - }, + cases := map[string]*copilot.SessionHooks{ + "userPromptSubmitted": { + OnUserPromptSubmitted: func(copilot.UserPromptSubmittedHookInput, copilot.HookInvocation) (*copilot.UserPromptSubmittedHookOutput, error) { + return &copilot.UserPromptSubmittedHookOutput{ModifiedPrompt: "not used"}, nil }, - }) - if err != nil { - t.Fatalf("Failed to create session: %v", err) - } - - response, err := session.SendAndWait(t.Context(), copilot.MessageOptions{Prompt: "Say something else"}) - if err != nil { - t.Fatalf("Failed to send message: %v", err) - } - - mu.Lock() - defer mu.Unlock() - if len(inputs) == 0 { - t.Fatal("Expected at least one userPromptSubmitted hook invocation") - } - if !strings.Contains(inputs[0].Prompt, "Say something else") { - t.Errorf("Expected hook input prompt to contain original prompt, got %q", inputs[0].Prompt) - } - - assistantMessage, ok := response.Data.(*copilot.AssistantMessageData) - if !ok || !strings.Contains(assistantMessage.Content, "HOOKED_PROMPT") { - t.Errorf("Expected response to contain 'HOOKED_PROMPT', got %v", response.Data) - } - }) - - t.Run("should invoke sessionStart hook", func(t *testing.T) { - ctx.ConfigureForTest(t) - - var ( - mu sync.Mutex - inputs []copilot.SessionStartHookInput - ) - - session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ - OnPermissionRequest: copilot.PermissionHandler.ApproveAll, - Hooks: &copilot.SessionHooks{ - OnSessionStart: func(input copilot.SessionStartHookInput, invocation copilot.HookInvocation) (*copilot.SessionStartHookOutput, error) { - mu.Lock() - inputs = append(inputs, input) - mu.Unlock() - if invocation.SessionID == "" { - t.Error("Expected non-empty session ID in invocation") - } - return &copilot.SessionStartHookOutput{ - AdditionalContext: "Session start hook context.", - }, nil - }, + }, + "sessionStart": { + OnSessionStart: func(copilot.SessionStartHookInput, copilot.HookInvocation) (*copilot.SessionStartHookOutput, error) { + return &copilot.SessionStartHookOutput{AdditionalContext: "not used"}, nil }, - }) - if err != nil { - t.Fatalf("Failed to create session: %v", err) - } - - if _, err := session.SendAndWait(t.Context(), copilot.MessageOptions{Prompt: "Say hi"}); err != nil { - t.Fatalf("Failed to send message: %v", err) - } - - mu.Lock() - defer mu.Unlock() - if len(inputs) == 0 { - t.Fatal("Expected sessionStart hook to be invoked at least once") - } - if inputs[0].Source != "new" { - t.Errorf("Expected source 'new', got %q", inputs[0].Source) - } - if inputs[0].WorkingDirectory == "" { - t.Error("Expected non-empty cwd in sessionStart hook input") - } - }) - - t.Run("should invoke sessionEnd hook", func(t *testing.T) { - ctx.ConfigureForTest(t) - - var ( - mu sync.Mutex - inputs []copilot.SessionEndHookInput - invocations = make(chan copilot.SessionEndHookInput, 4) - ) - - session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ - OnPermissionRequest: copilot.PermissionHandler.ApproveAll, - Hooks: &copilot.SessionHooks{ - OnSessionEnd: func(input copilot.SessionEndHookInput, invocation copilot.HookInvocation) (*copilot.SessionEndHookOutput, error) { - mu.Lock() - inputs = append(inputs, input) - mu.Unlock() - if invocation.SessionID == "" { - t.Error("Expected non-empty session ID in invocation") - } - select { - case invocations <- input: - default: - } - return &copilot.SessionEndHookOutput{ - SessionSummary: "session ended", - }, nil - }, + }, + "sessionEnd": { + OnSessionEnd: func(copilot.SessionEndHookInput, copilot.HookInvocation) (*copilot.SessionEndHookOutput, error) { + return &copilot.SessionEndHookOutput{SessionSummary: "not used"}, nil }, - }) - if err != nil { - t.Fatalf("Failed to create session: %v", err) - } - - if _, err := session.SendAndWait(t.Context(), copilot.MessageOptions{Prompt: "Say bye"}); err != nil { - t.Fatalf("Failed to send message: %v", err) - } - if err := session.Disconnect(); err != nil { - t.Fatalf("Failed to disconnect session: %v", err) - } - - select { - case <-invocations: - case <-time.After(10 * time.Second): - t.Fatal("Timed out waiting for sessionEnd hook invocation") - } - - mu.Lock() - defer mu.Unlock() - if len(inputs) == 0 { - t.Fatal("Expected sessionEnd hook to be invoked at least once") - } - }) - - t.Run("should register errorOccurred hook", func(t *testing.T) { - ctx.ConfigureForTest(t) - - var ( - mu sync.Mutex - inputs []copilot.ErrorOccurredHookInput - ) - - session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ - OnPermissionRequest: copilot.PermissionHandler.ApproveAll, - Hooks: &copilot.SessionHooks{ - OnErrorOccurred: func(input copilot.ErrorOccurredHookInput, invocation copilot.HookInvocation) (*copilot.ErrorOccurredHookOutput, error) { - mu.Lock() - inputs = append(inputs, input) - mu.Unlock() - if invocation.SessionID == "" { - t.Error("Expected non-empty session ID in invocation") - } - return &copilot.ErrorOccurredHookOutput{ErrorHandling: "skip"}, nil - }, + }, + "errorOccurred": { + OnErrorOccurred: func(copilot.ErrorOccurredHookInput, copilot.HookInvocation) (*copilot.ErrorOccurredHookOutput, error) { + return &copilot.ErrorOccurredHookOutput{ErrorHandling: "skip"}, nil }, - }) - if err != nil { - t.Fatalf("Failed to create session: %v", err) - } - - if _, err := session.SendAndWait(t.Context(), copilot.MessageOptions{Prompt: "Say hi"}); err != nil { - t.Fatalf("Failed to send message: %v", err) - } - - // OnErrorOccurred is dispatched only by genuine runtime errors (e.g. provider - // failures, internal exceptions). A normal turn cannot deterministically trigger - // one, so this is a registration-only test: the SDK must accept the hook and not - // invoke it inappropriately during a healthy turn. - mu.Lock() - got := len(inputs) - mu.Unlock() - if got != 0 { - t.Errorf("Expected errorOccurred hook to not fire on a healthy turn, got %d invocations", got) - } - if session.SessionID == "" { - t.Error("Expected session id to be set") - } - }) - - t.Run("should allow preToolUse to return modifiedArgs and suppressOutput", func(t *testing.T) { - ctx.ConfigureForTest(t) - - type EchoParams struct { - Value string `json:"value" jsonschema:"Value to echo"` - } - echoTool := copilot.DefineTool("echo_value", "Echoes the supplied value", - func(params EchoParams, inv copilot.ToolInvocation) (string, error) { - return params.Value, nil - }) - - var ( - mu sync.Mutex - inputs []copilot.PreToolUseHookInput - ) - - session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ - OnPermissionRequest: copilot.PermissionHandler.ApproveAll, - Tools: []copilot.Tool{echoTool}, - Hooks: &copilot.SessionHooks{ - OnPreToolUse: func(input copilot.PreToolUseHookInput, invocation copilot.HookInvocation) (*copilot.PreToolUseHookOutput, error) { - mu.Lock() - inputs = append(inputs, input) - mu.Unlock() - if input.ToolName != "echo_value" { - return &copilot.PreToolUseHookOutput{PermissionDecision: "allow"}, nil - } - return &copilot.PreToolUseHookOutput{ - PermissionDecision: "allow", - ModifiedArgs: map[string]any{"value": "modified by hook"}, - SuppressOutput: false, - }, nil - }, + }, + "preToolUse output": { + OnPreToolUse: func(copilot.PreToolUseHookInput, copilot.HookInvocation) (*copilot.PreToolUseHookOutput, error) { + return &copilot.PreToolUseHookOutput{PermissionDecision: "allow"}, nil }, - }) - if err != nil { - t.Fatalf("Failed to create session: %v", err) - } - - response, err := session.SendAndWait(t.Context(), copilot.MessageOptions{ - Prompt: "Call echo_value with value 'original', then reply with the result.", - }) - if err != nil { - t.Fatalf("Failed to send message: %v", err) - } - - mu.Lock() - defer mu.Unlock() - if len(inputs) == 0 { - t.Fatal("Expected preToolUse hook to be invoked at least once") - } - hadEchoInput := false - for _, input := range inputs { - if input.ToolName == "echo_value" { - hadEchoInput = true - break - } - } - if !hadEchoInput { - t.Errorf("Expected at least one preToolUse invocation for echo_value, got %+v", inputs) - } - - assistantMessage, ok := response.Data.(*copilot.AssistantMessageData) - if !ok || !strings.Contains(assistantMessage.Content, "modified by hook") { - t.Errorf("Expected response to contain 'modified by hook', got %v", response.Data) - } - }) - - t.Run("should allow postToolUse to return modifiedResult", func(t *testing.T) { - ctx.ConfigureForTest(t) - - var ( - mu sync.Mutex - inputs []copilot.PostToolUseHookInput - ) - - session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ - OnPermissionRequest: copilot.PermissionHandler.ApproveAll, - Hooks: &copilot.SessionHooks{ - OnPostToolUse: func(input copilot.PostToolUseHookInput, invocation copilot.HookInvocation) (*copilot.PostToolUseHookOutput, error) { - mu.Lock() - inputs = append(inputs, input) - mu.Unlock() - if input.ToolName != "view" { - return nil, nil - } - return &copilot.PostToolUseHookOutput{ - ModifiedResult: copilot.ToolResult{ - TextResultForLLM: "modified by post hook", - ResultType: "success", - ToolTelemetry: map[string]any{}, - }, - SuppressOutput: false, - }, nil - }, + }, + "postToolUse output": { + OnPostToolUse: func(copilot.PostToolUseHookInput, copilot.HookInvocation) (*copilot.PostToolUseHookOutput, error) { + return &copilot.PostToolUseHookOutput{SuppressOutput: false}, nil }, - }) - if err != nil { - t.Fatalf("Failed to create session: %v", err) - } - - response, err := session.SendAndWait(t.Context(), copilot.MessageOptions{ - Prompt: "Call the view tool to read the current directory, then reply done.", - }) - if err != nil { - t.Fatalf("Failed to send message: %v", err) - } - - mu.Lock() - defer mu.Unlock() - hadView := false - for _, input := range inputs { - if input.ToolName == "view" { - hadView = true - break - } - } - if !hadView { - t.Errorf("Expected at least one postToolUse invocation for view, got %+v", inputs) - } - - assistantMessage, ok := response.Data.(*copilot.AssistantMessageData) - if !ok || !strings.Contains(strings.ToLower(assistantMessage.Content), "done") { - t.Errorf("Expected response content to contain 'done', got %v", response.Data) - } - }) - - t.Run("should invoke postToolUseFailure hook for failed tool result", func(t *testing.T) { - t.Skip("Fails with 1.0.64-0 runtime: built-in tools are not available when " + - "hooks restrict availableTools, so the failure path cannot be exercised. " + - "Follow up with runtime team.") - ctx.ConfigureForTest(t) - - var ( - mu sync.Mutex - failureInputs []copilot.PostToolUseFailureHookInput - postToolUseInputs []copilot.PostToolUseHookInput - ) - - session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ - OnPermissionRequest: copilot.PermissionHandler.ApproveAll, - AvailableTools: []string{"report_intent"}, - Hooks: &copilot.SessionHooks{ - OnPostToolUse: func(input copilot.PostToolUseHookInput, invocation copilot.HookInvocation) (*copilot.PostToolUseHookOutput, error) { - mu.Lock() - postToolUseInputs = append(postToolUseInputs, input) - mu.Unlock() - return nil, nil - }, - OnPostToolUseFailure: func(input copilot.PostToolUseFailureHookInput, invocation copilot.HookInvocation) (*copilot.PostToolUseFailureHookOutput, error) { - mu.Lock() - failureInputs = append(failureInputs, input) - mu.Unlock() - if invocation.SessionID == "" { - t.Error("Expected non-empty session ID in invocation") - } - return &copilot.PostToolUseFailureHookOutput{ - AdditionalContext: "HOOK_FAILURE_GUIDANCE_APPLIED", - }, nil - }, + }, + "postToolUseFailure output": { + OnPostToolUse: func(copilot.PostToolUseHookInput, copilot.HookInvocation) (*copilot.PostToolUseHookOutput, error) { + return nil, nil }, - }) - if err != nil { - t.Fatalf("Failed to create session: %v", err) - } + OnPostToolUseFailure: func(copilot.PostToolUseFailureHookInput, copilot.HookInvocation) (*copilot.PostToolUseFailureHookOutput, error) { + return &copilot.PostToolUseFailureHookOutput{AdditionalContext: "not used"}, nil + }, + }, + } - response, err := session.SendAndWait(t.Context(), copilot.MessageOptions{ - Prompt: "Call the view tool with path 'missing.txt'. If it fails, use the hook guidance to answer.", + for name, hooks := range cases { + t.Run("rejects SDK callback hook "+name, func(t *testing.T) { + ctx.ConfigureForTest(t) + assertUnsupportedExtendedHooks(t, client, hooks) }) - if err != nil { - t.Fatalf("Failed to send message: %v", err) - } - - mu.Lock() - defer mu.Unlock() - if len(postToolUseInputs) != 0 { - t.Fatalf("Expected postToolUse not to fire for failed result, got %+v", postToolUseInputs) - } - if len(failureInputs) != 1 { - t.Fatalf("Expected one postToolUseFailure input, got %+v", failureInputs) - } - input := failureInputs[0] - if input.ToolName != "view" { - t.Errorf("Expected tool name view, got %q", input.ToolName) - } - if !strings.Contains(input.Error, "does not exist") { - t.Errorf("Expected missing-tool error, got %q", input.Error) - } - if !strings.Contains(fmt.Sprint(input.ToolArgs), "missing.txt") { - t.Errorf("Expected tool args to contain missing.txt, got %+v", input.ToolArgs) - } - if input.WorkingDirectory == "" { - t.Error("Expected working directory to be populated") - } - if input.Timestamp.IsZero() { - t.Error("Expected timestamp to be populated") - } - if assistantMessage, ok := response.Data.(*copilot.AssistantMessageData); !ok || !strings.Contains(assistantMessage.Content, "HOOK_FAILURE_GUIDANCE_APPLIED") { - t.Errorf("Expected response to contain hook guidance, got %v", response.Data) - } - }) + } } diff --git a/go/internal/e2e/pre_mcp_tool_call_hook_e2e_test.go b/go/internal/e2e/pre_mcp_tool_call_hook_e2e_test.go index 1847270922..d432d2aad3 100644 --- a/go/internal/e2e/pre_mcp_tool_call_hook_e2e_test.go +++ b/go/internal/e2e/pre_mcp_tool_call_hook_e2e_test.go @@ -1,9 +1,7 @@ package e2e import ( - "path/filepath" "strings" - "sync" "testing" copilot "github.com/github/copilot-sdk/go" @@ -15,193 +13,25 @@ func TestPreMCPToolCallHookE2E(t *testing.T) { client := ctx.NewClient() t.Cleanup(func() { client.ForceStop() }) - testHarnessDir, _ := filepath.Abs("../../../test/harness") - metaEchoServer := filepath.Join(testHarnessDir, "test-mcp-meta-echo-server.mjs") - - metaEchoConfig := func() map[string]copilot.MCPServerConfig { - return map[string]copilot.MCPServerConfig{ - "meta-echo": copilot.MCPStdioServerConfig{ - Command: "node", - Args: []string{metaEchoServer}, - WorkingDirectory: testHarnessDir, - Tools: []string{"*"}, - }, - } - } - - t.Run("should set meta via preMcpToolCall hook", func(t *testing.T) { - ctx.ConfigureForTest(t) - - var ( - mu sync.Mutex - inputs []copilot.PreMCPToolCallHookInput - ) - - session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ - OnPermissionRequest: copilot.PermissionHandler.ApproveAll, - MCPServers: metaEchoConfig(), - Hooks: &copilot.SessionHooks{ - OnPreMCPToolCall: func(input copilot.PreMCPToolCallHookInput, invocation copilot.HookInvocation) (*copilot.PreMCPToolCallHookOutput, error) { - mu.Lock() - inputs = append(inputs, input) - mu.Unlock() - return &copilot.PreMCPToolCallHookOutput{ - MetaToUse: map[string]any{ - "injected": "by-hook", - "source": "test", - }, - }, nil - }, - }, - }) - if err != nil { - t.Fatalf("Failed to create session: %v", err) - } - - response, err := session.SendAndWait(t.Context(), copilot.MessageOptions{ - Prompt: "Use the meta-echo/echo_meta tool with value 'test-set'. Reply with just the raw tool result.", - }) - if err != nil { - t.Fatalf("Failed to send message: %v", err) - } - - assistantMessage, ok := response.Data.(*copilot.AssistantMessageData) - if !ok { - t.Fatalf("Expected assistant message data, got %T", response.Data) - } - if !strings.Contains(assistantMessage.Content, "injected") || !strings.Contains(assistantMessage.Content, "by-hook") { - t.Errorf("Expected response to contain 'injected' and 'by-hook', got %q", assistantMessage.Content) - } - - mu.Lock() - defer mu.Unlock() - if len(inputs) == 0 { - t.Fatal("Expected at least one preMcpToolCall hook invocation") - } - if inputs[0].ServerName != "meta-echo" { - t.Errorf("Expected serverName 'meta-echo', got %q", inputs[0].ServerName) - } - if inputs[0].ToolName != "echo_meta" { - t.Errorf("Expected toolName 'echo_meta', got %q", inputs[0].ToolName) - } - if inputs[0].WorkingDirectory == "" { - t.Error("Expected non-empty workingDirectory") - } - if inputs[0].Timestamp.IsZero() { - t.Error("Expected non-zero timestamp") - } - }) - - t.Run("should replace meta via preMcpToolCall hook", func(t *testing.T) { - ctx.ConfigureForTest(t) - - var ( - mu sync.Mutex - inputs []copilot.PreMCPToolCallHookInput - ) - - session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ - OnPermissionRequest: copilot.PermissionHandler.ApproveAll, - MCPServers: metaEchoConfig(), - Hooks: &copilot.SessionHooks{ - OnPreMCPToolCall: func(input copilot.PreMCPToolCallHookInput, invocation copilot.HookInvocation) (*copilot.PreMCPToolCallHookOutput, error) { - mu.Lock() - inputs = append(inputs, input) - mu.Unlock() - return &copilot.PreMCPToolCallHookOutput{ - MetaToUse: map[string]any{ - "completely": "replaced", - }, - }, nil - }, - }, - }) - if err != nil { - t.Fatalf("Failed to create session: %v", err) - } - - response, err := session.SendAndWait(t.Context(), copilot.MessageOptions{ - Prompt: "Use the meta-echo/echo_meta tool with value 'test-replace'. Reply with just the raw tool result.", - }) - if err != nil { - t.Fatalf("Failed to send message: %v", err) - } - - assistantMessage, ok := response.Data.(*copilot.AssistantMessageData) - if !ok { - t.Fatalf("Expected assistant message data, got %T", response.Data) - } - if !strings.Contains(assistantMessage.Content, "completely") || !strings.Contains(assistantMessage.Content, "replaced") { - t.Errorf("Expected response to contain 'completely' and 'replaced', got %q", assistantMessage.Content) - } - - mu.Lock() - defer mu.Unlock() - if len(inputs) == 0 { - t.Fatal("Expected at least one preMcpToolCall hook invocation") - } - if inputs[0].ServerName != "meta-echo" { - t.Errorf("Expected serverName 'meta-echo', got %q", inputs[0].ServerName) - } - if inputs[0].ToolName != "echo_meta" { - t.Errorf("Expected toolName 'echo_meta', got %q", inputs[0].ToolName) - } - }) - - t.Run("should remove meta via preMcpToolCall hook", func(t *testing.T) { + t.Run("rejects SDK preMcpToolCall callback hooks", func(t *testing.T) { ctx.ConfigureForTest(t) - var ( - mu sync.Mutex - inputs []copilot.PreMCPToolCallHookInput - ) - session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ OnPermissionRequest: copilot.PermissionHandler.ApproveAll, - MCPServers: metaEchoConfig(), Hooks: &copilot.SessionHooks{ - OnPreMCPToolCall: func(input copilot.PreMCPToolCallHookInput, invocation copilot.HookInvocation) (*copilot.PreMCPToolCallHookOutput, error) { - mu.Lock() - inputs = append(inputs, input) - mu.Unlock() - return &copilot.PreMCPToolCallHookOutput{ - MetaToUse: nil, - }, nil + OnPreMCPToolCall: func(copilot.PreMCPToolCallHookInput, copilot.HookInvocation) (*copilot.PreMCPToolCallHookOutput, error) { + return &copilot.PreMCPToolCallHookOutput{MetaToUse: map[string]any{"injected": "by-hook"}}, nil }, }, }) - if err != nil { - t.Fatalf("Failed to create session: %v", err) - } - - response, err := session.SendAndWait(t.Context(), copilot.MessageOptions{ - Prompt: "Use the meta-echo/echo_meta tool with value 'test-remove'. Reply with just the raw tool result.", - }) - if err != nil { - t.Fatalf("Failed to send message: %v", err) - } - - assistantMessage, ok := response.Data.(*copilot.AssistantMessageData) - if !ok { - t.Fatalf("Expected assistant message data, got %T", response.Data) - } - if !strings.Contains(assistantMessage.Content, `"meta":null`) { - t.Errorf("Expected response to contain '\"meta\":null', got %q", assistantMessage.Content) - } - if !strings.Contains(assistantMessage.Content, "test-remove") { - t.Errorf("Expected response to contain 'test-remove', got %q", assistantMessage.Content) - } - - mu.Lock() - defer mu.Unlock() - if len(inputs) == 0 { - t.Fatal("Expected at least one preMcpToolCall hook invocation") - } - if inputs[0].ServerName != "meta-echo" { - t.Errorf("Expected serverName 'meta-echo', got %q", inputs[0].ServerName) - } - if inputs[0].ToolName != "echo_meta" { - t.Errorf("Expected toolName 'echo_meta', got %q", inputs[0].ToolName) + if err == nil { + if session != nil { + _ = session.Disconnect() + } + t.Fatal("expected SDK callback hooks to be rejected") + } + if !strings.Contains(err.Error(), unsupportedSDKHooksMessage) { + t.Fatalf("expected unsupported hooks error, got %v", err) } }) } diff --git a/go/internal/e2e/subagent_hooks_e2e_test.go b/go/internal/e2e/subagent_hooks_e2e_test.go index c632b1e606..09f2b8f35d 100644 --- a/go/internal/e2e/subagent_hooks_e2e_test.go +++ b/go/internal/e2e/subagent_hooks_e2e_test.go @@ -1,9 +1,7 @@ package e2e import ( - "os" - "path/filepath" - "sync" + "strings" "testing" copilot "github.com/github/copilot-sdk/go" @@ -12,93 +10,31 @@ import ( func TestSubagentHooksE2E(t *testing.T) { ctx := testharness.NewTestContext(t) - client := ctx.NewClient(func(o *copilot.ClientOptions) { - o.Env = append(o.Env, "COPILOT_EXP_COPILOT_CLI_SESSION_BASED_SUBAGENTS=true") - }) + client := ctx.NewClient() t.Cleanup(func() { client.ForceStop() }) - t.Run("should invoke preToolUse and postToolUse hooks for sub-agent tool calls", func(t *testing.T) { + t.Run("rejects SDK callback hooks for sub-agent hook propagation", func(t *testing.T) { ctx.ConfigureForTest(t) - type hookEntry struct { - kind string - toolName string - sessionID string - } - var hookLog []hookEntry - var mu sync.Mutex - session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ OnPermissionRequest: copilot.PermissionHandler.ApproveAll, Hooks: &copilot.SessionHooks{ - OnPreToolUse: func(input copilot.PreToolUseHookInput, invocation copilot.HookInvocation) (*copilot.PreToolUseHookOutput, error) { - mu.Lock() - hookLog = append(hookLog, hookEntry{kind: "pre", toolName: input.ToolName, sessionID: input.SessionID}) - mu.Unlock() + OnPreToolUse: func(copilot.PreToolUseHookInput, copilot.HookInvocation) (*copilot.PreToolUseHookOutput, error) { return &copilot.PreToolUseHookOutput{PermissionDecision: "allow"}, nil }, - OnPostToolUse: func(input copilot.PostToolUseHookInput, invocation copilot.HookInvocation) (*copilot.PostToolUseHookOutput, error) { - mu.Lock() - hookLog = append(hookLog, hookEntry{kind: "post", toolName: input.ToolName, sessionID: input.SessionID}) - mu.Unlock() + OnPostToolUse: func(copilot.PostToolUseHookInput, copilot.HookInvocation) (*copilot.PostToolUseHookOutput, error) { return nil, nil }, }, }) - if err != nil { - t.Fatalf("Failed to create session: %v", err) - } - - // Create a file for the sub-agent to read - testFile := filepath.Join(ctx.WorkDir, "subagent-test.txt") - if err := os.WriteFile(testFile, []byte("Hello from subagent test!"), 0644); err != nil { - t.Fatalf("Failed to write test file: %v", err) - } - - _, err = session.SendAndWait(t.Context(), copilot.MessageOptions{ - Prompt: "Use the task tool to spawn an explore agent that reads the file subagent-test.txt in the current directory and reports its contents. You must use the task tool.", - }) - if err != nil { - t.Fatalf("Failed to send message: %v", err) - } - - mu.Lock() - defer mu.Unlock() - - // Parent tool hooks fire for "task" - var taskPre *hookEntry - for i := range hookLog { - if hookLog[i].kind == "pre" && hookLog[i].toolName == "task" { - taskPre = &hookLog[i] - break + if err == nil { + if session != nil { + _ = session.Disconnect() } + t.Fatal("expected SDK callback hooks to be rejected") } - if taskPre == nil { - t.Fatal("preToolUse should fire for the parent's 'task' tool call") - return - } - - // Sub-agent tool hooks fire for "view" - var viewPre, viewPost []hookEntry - for _, h := range hookLog { - if h.toolName == "view" { - if h.kind == "pre" { - viewPre = append(viewPre, h) - } else { - viewPost = append(viewPost, h) - } - } - } - if len(viewPre) == 0 { - t.Fatal("preToolUse should fire for the sub-agent's 'view' tool call") - } - if len(viewPost) == 0 { - t.Fatal("postToolUse should fire for the sub-agent's 'view' tool call") - } - - // input.SessionID distinguishes parent from sub-agent - if viewPre[0].sessionID == taskPre.sessionID { - t.Error("Sub-agent tool hooks should have a different sessionId than parent tool hooks") + if !strings.Contains(err.Error(), unsupportedSDKHooksMessage) { + t.Fatalf("expected unsupported hooks error, got %v", err) } }) } diff --git a/java/src/test/java/com/github/copilot/HooksTest.java b/java/src/test/java/com/github/copilot/HooksTest.java index 4608848f19..c47e8d84f8 100644 --- a/java/src/test/java/com/github/copilot/HooksTest.java +++ b/java/src/test/java/com/github/copilot/HooksTest.java @@ -4,44 +4,28 @@ package com.github.copilot; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Set; +import java.util.List; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; +import java.util.concurrent.ExecutionException; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import com.github.copilot.rpc.MessageOptions; import com.github.copilot.rpc.PermissionHandler; -import com.github.copilot.rpc.PostToolUseHookInput; -import com.github.copilot.rpc.PreToolUseHookInput; +import com.github.copilot.rpc.PostToolUseHookOutput; import com.github.copilot.rpc.PreToolUseHookOutput; import com.github.copilot.rpc.SessionConfig; import com.github.copilot.rpc.SessionHooks; -/** - * Tests for hooks functionality (pre-tool-use and post-tool-use hooks). - * - *

- * These tests use the shared CapiProxy infrastructure for deterministic API - * response replay. Snapshots are stored in test/snapshots/hooks/. - *

- * - *

- * Note: Tests for userPromptSubmitted, sessionStart, and sessionEnd hooks are - * not included as they are not tested in the reference implementation .NET or - * Node.js SDKs and require test harness updates to properly invoke these hooks. - *

- */ +/** Tests for SDK callback hook behavior with the native runtime. */ public class HooksTest { + private static final String UNSUPPORTED_SDK_HOOKS_MESSAGE = "SDK hook callbacks are no longer supported"; + private static E2ETestContext ctx; @BeforeAll @@ -56,171 +40,31 @@ static void teardown() throws Exception { } } - /** - * Verifies that pre-tool-use hook is invoked when model runs a tool. - * - * @see Snapshot: hooks/invoke_pre_tool_use_hook_when_model_runs_a_tool - */ - @Test - void testInvokePreToolUseHookWhenModelRunsATool() throws Exception { - ctx.configureForTest("hooks", "invoke_pre_tool_use_hook_when_model_runs_a_tool"); - - var preToolUseInputs = new ArrayList(); - final String[] sessionIdHolder = new String[1]; - - var config = new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL) - .setHooks(new SessionHooks().setOnPreToolUse((input, invocation) -> { - preToolUseInputs.add(input); - assertEquals(sessionIdHolder[0], invocation.getSessionId()); - return CompletableFuture.completedFuture(PreToolUseHookOutput.allow()); - })); - - try (CopilotClient client = ctx.createClient()) { - CopilotSession session = client.createSession(config).get(); - sessionIdHolder[0] = session.getSessionId(); - - // Create a file for the model to read - Path testFile = ctx.getWorkDir().resolve("hello.txt"); - Files.writeString(testFile, "Hello from the test!"); - - session.sendAndWait( - new MessageOptions().setPrompt("Read the contents of hello.txt and tell me what it says")) - .get(60, TimeUnit.SECONDS); - - // Should have received at least one preToolUse hook call - assertFalse(preToolUseInputs.isEmpty(), "Should have received preToolUse hook calls"); - - // Should have received the tool name - assertTrue(preToolUseInputs.stream().anyMatch(i -> i.getToolName() != null && !i.getToolName().isEmpty()), - "Should have received tool name in preToolUse hook"); - } - } - - /** - * Verifies that post-tool-use hook is invoked after model runs a tool. - * - * @see Snapshot: hooks/invoke_post_tool_use_hook_after_model_runs_a_tool - */ @Test - void testInvokePostToolUseHookAfterModelRunsATool() throws Exception { - ctx.configureForTest("hooks", "invoke_post_tool_use_hook_after_model_runs_a_tool"); - - var postToolUseInputs = new ArrayList(); - final String[] sessionIdHolder = new String[1]; - - var config = new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL) - .setHooks(new SessionHooks().setOnPostToolUse((input, invocation) -> { - postToolUseInputs.add(input); - assertEquals(sessionIdHolder[0], invocation.getSessionId()); - return CompletableFuture.completedFuture(null); - })); + void testRejectsSdkCallbackHooks() throws Exception { + ctx.initializeProxy(); + + var hookCases = List.of( + new SessionHooks().setOnPreToolUse( + (input, invocation) -> CompletableFuture.completedFuture(PreToolUseHookOutput.allow())), + new SessionHooks().setOnPostToolUse( + (input, invocation) -> CompletableFuture.completedFuture((PostToolUseHookOutput) null)), + new SessionHooks().setOnPreToolUse( + (input, invocation) -> CompletableFuture.completedFuture(PreToolUseHookOutput.deny())), + new SessionHooks() + .setOnPreToolUse((input, invocation) -> CompletableFuture + .completedFuture(PreToolUseHookOutput.allow())) + .setOnPostToolUse((input, invocation) -> CompletableFuture + .completedFuture((PostToolUseHookOutput) null))); try (CopilotClient client = ctx.createClient()) { - CopilotSession session = client.createSession(config).get(); - sessionIdHolder[0] = session.getSessionId(); - - // Create a file for the model to read - Path testFile = ctx.getWorkDir().resolve("world.txt"); - Files.writeString(testFile, "World from the test!"); - - session.sendAndWait( - new MessageOptions().setPrompt("Read the contents of world.txt and tell me what it says")) - .get(60, TimeUnit.SECONDS); - - // Should have received at least one postToolUse hook call - assertFalse(postToolUseInputs.isEmpty(), "Should have received postToolUse hook calls"); - - // Should have received the tool name and result - assertTrue(postToolUseInputs.stream().anyMatch(i -> i.getToolName() != null && !i.getToolName().isEmpty()), - "Should have received tool name in postToolUse hook"); - assertTrue(postToolUseInputs.stream().anyMatch(i -> i.getToolResult() != null), - "Should have received tool result in postToolUse hook"); - } - } - - /** - * Verifies that both hooks are invoked for a single tool call. - * - * @see Snapshot: hooks/invoke_both_hooks_for_single_tool_call - */ - @Test - void testInvokeBothHooksForSingleToolCall() throws Exception { - ctx.configureForTest("hooks", "invoke_both_hooks_for_single_tool_call"); - - var preToolUseInputs = new ArrayList(); - var postToolUseInputs = new ArrayList(); - - var config = new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL) - .setHooks(new SessionHooks().setOnPreToolUse((input, invocation) -> { - preToolUseInputs.add(input); - return CompletableFuture.completedFuture(PreToolUseHookOutput.allow()); - }).setOnPostToolUse((input, invocation) -> { - postToolUseInputs.add(input); - return CompletableFuture.completedFuture(null); - })); - - try (CopilotClient client = ctx.createClient()) { - CopilotSession session = client.createSession(config).get(); - - // Create a file for the model to read - Path testFile = ctx.getWorkDir().resolve("both.txt"); - Files.writeString(testFile, "Testing both hooks!"); - - session.sendAndWait(new MessageOptions().setPrompt("Read the contents of both.txt")).get(60, - TimeUnit.SECONDS); - - // Both hooks should have been called - assertFalse(preToolUseInputs.isEmpty(), "Should have received preToolUse hook calls"); - assertFalse(postToolUseInputs.isEmpty(), "Should have received postToolUse hook calls"); - - // The same tool should appear in both - Set preToolNames = preToolUseInputs.stream().map(PreToolUseHookInput::getToolName) - .filter(n -> n != null && !n.isEmpty()).collect(Collectors.toSet()); - Set postToolNames = postToolUseInputs.stream().map(PostToolUseHookInput::getToolName) - .filter(n -> n != null && !n.isEmpty()).collect(Collectors.toSet()); - - // Check if there's any overlap - boolean hasOverlap = preToolNames.stream().anyMatch(postToolNames::contains); - assertTrue(hasOverlap, "Expected the same tool to appear in both pre and post hooks"); - } - } - - /** - * Verifies that tool execution is denied when pre-tool-use returns deny. - * - * @see Snapshot: hooks/deny_tool_execution_when_pre_tool_use_returns_deny - */ - @Test - void testDenyToolExecutionWhenPreToolUseReturnsDeny() throws Exception { - ctx.configureForTest("hooks", "deny_tool_execution_when_pre_tool_use_returns_deny"); - - var preToolUseInputs = new ArrayList(); - - var config = new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL) - .setHooks(new SessionHooks().setOnPreToolUse((input, invocation) -> { - preToolUseInputs.add(input); - // Deny all tool calls - return CompletableFuture.completedFuture(PreToolUseHookOutput.deny()); - })); - - try (CopilotClient client = ctx.createClient()) { - CopilotSession session = client.createSession(config).get(); - - // Create a file - Path testFile = ctx.getWorkDir().resolve("protected.txt"); - String originalContent = "Original content that should not be modified"; - Files.writeString(testFile, originalContent); - - var response = session - .sendAndWait( - new MessageOptions().setPrompt("Edit protected.txt and replace 'Original' with 'Modified'")) - .get(60, TimeUnit.SECONDS); - - // The hook should have been called - assertFalse(preToolUseInputs.isEmpty(), "Should have received preToolUse hook calls"); + for (SessionHooks hooks : hookCases) { + var config = new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL).setHooks(hooks); - // The response should be defined - assertNotNull(response, "Response should not be null"); + var ex = assertThrows(ExecutionException.class, () -> client.createSession(config).get()); + assertTrue(ex.toString().contains(UNSUPPORTED_SDK_HOOKS_MESSAGE), + () -> "Expected unsupported hooks error, got: " + ex); + } } } } diff --git a/java/src/test/java/com/github/copilot/PreMcpToolCallHookTest.java b/java/src/test/java/com/github/copilot/PreMcpToolCallHookTest.java index 5da0d2002f..1da88a82ae 100644 --- a/java/src/test/java/com/github/copilot/PreMcpToolCallHookTest.java +++ b/java/src/test/java/com/github/copilot/PreMcpToolCallHookTest.java @@ -4,43 +4,26 @@ package com.github.copilot; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; -import java.util.HashMap; -import java.util.List; -import java.util.Map; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.ExecutionException; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.copilot.generated.AssistantMessageEvent; -import com.github.copilot.rpc.McpServerConfig; -import com.github.copilot.rpc.McpStdioServerConfig; -import com.github.copilot.rpc.MessageOptions; import com.github.copilot.rpc.PermissionHandler; -import com.github.copilot.rpc.PreMcpToolCallHookInput; import com.github.copilot.rpc.PreMcpToolCallHookOutput; import com.github.copilot.rpc.SessionConfig; import com.github.copilot.rpc.SessionHooks; -/** - * Tests for preMcpToolCall hook functionality. - * - *

- * These tests use the shared CapiProxy infrastructure for deterministic API - * response replay. Snapshots are stored in - * test/snapshots/pre_mcp_tool_call_hook/. - *

- */ +/** Tests for SDK preMcpToolCall callback hook behavior with the native runtime. */ public class PreMcpToolCallHookTest { - private static final ObjectMapper MAPPER = JsonRpcClient.getObjectMapper(); + private static final String UNSUPPORTED_SDK_HOOKS_MESSAGE = "SDK hook callbacks are no longer supported"; + private static E2ETestContext ctx; @BeforeAll @@ -55,123 +38,19 @@ static void teardown() throws Exception { } } - /** - * Verifies that preMcpToolCall hook can set metadata on the MCP request. - * - * @see Snapshot: pre_mcp_tool_call_hook/should_set_meta_via_premcptoolcall_hook - */ - @Disabled("Requires snapshot: pre_mcp_tool_call_hook/should_set_meta_via_premcptoolcall_hook") - @Test - void testShouldSetMetaViaPreMcpToolCallHook() throws Exception { - ctx.configureForTest("pre_mcp_tool_call_hook", "should_set_meta_via_premcptoolcall_hook"); - - var hookInputs = new java.util.ArrayList(); - - var mcpServers = new HashMap(); - mcpServers.put("meta-echo", new McpStdioServerConfig().setCommand("npx").setArgs(List.of("-y", "mcp-meta-echo")) - .setTools(List.of("*")).setWorkingDirectory(ctx.getWorkDir().toString())); - - var hooks = new SessionHooks().setOnPreMcpToolCall((input, invocation) -> { - hookInputs.add(input); - JsonNode metaNode = MAPPER.valueToTree(Map.of("injected", "by-hook", "source", "test")); - return CompletableFuture.completedFuture(PreMcpToolCallHookOutput.withMeta(metaNode)); - }); - - try (CopilotClient client = ctx.createClient()) { - CopilotSession session = client.createSession(new SessionConfig().setMcpServers(mcpServers).setHooks(hooks) - .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get(); - - AssistantMessageEvent response = session.sendAndWait(new MessageOptions().setPrompt( - "Use the meta-echo/echo_meta tool with value 'test-set'. Reply with just the raw tool result.")) - .get(60, TimeUnit.SECONDS); - - assertNotNull(response); - assertFalse(hookInputs.isEmpty(), "Should have received preMcpToolCall hook calls"); - - // Verify hook input fields - PreMcpToolCallHookInput hookInput = hookInputs.get(0); - assertEquals("meta-echo", hookInput.getServerName()); - assertNotNull(hookInput.getToolName()); - assertNotNull(hookInput.getCwd()); - assertTrue(hookInput.getTimestamp() > 0); - - // Verify the response contains the injected metadata - String content = response.getData().content(); - assertTrue(content.contains("by-hook"), "Response should contain injected metadata: " + content); - - session.close(); - } - } - - /** - * Verifies that preMcpToolCall hook can replace existing metadata. - * - * @see Snapshot: - * pre_mcp_tool_call_hook/should_replace_meta_via_premcptoolcall_hook - */ - @Disabled("Requires snapshot: pre_mcp_tool_call_hook/should_replace_meta_via_premcptoolcall_hook") @Test - void testShouldReplaceMetaViaPreMcpToolCallHook() throws Exception { - ctx.configureForTest("pre_mcp_tool_call_hook", "should_replace_meta_via_premcptoolcall_hook"); + void testRejectsSdkPreMcpToolCallCallbackHooks() throws Exception { + ctx.initializeProxy(); - var mcpServers = new HashMap(); - mcpServers.put("meta-echo", new McpStdioServerConfig().setCommand("npx").setArgs(List.of("-y", "mcp-meta-echo")) - .setTools(List.of("*")).setWorkingDirectory(ctx.getWorkDir().toString())); - - var hooks = new SessionHooks().setOnPreMcpToolCall((input, invocation) -> { - JsonNode metaNode = MAPPER.valueToTree(Map.of("replaced", "true", "original", "gone")); - return CompletableFuture.completedFuture(PreMcpToolCallHookOutput.withMeta(metaNode)); - }); + var hooks = new SessionHooks().setOnPreMcpToolCall( + (input, invocation) -> CompletableFuture.completedFuture(PreMcpToolCallHookOutput.removeMeta())); try (CopilotClient client = ctx.createClient()) { - CopilotSession session = client.createSession(new SessionConfig().setMcpServers(mcpServers).setHooks(hooks) - .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get(); - - AssistantMessageEvent response = session.sendAndWait(new MessageOptions().setPrompt( - "Use the meta-echo/echo_meta tool with value 'test-replace'. Reply with just the raw tool result.")) - .get(60, TimeUnit.SECONDS); - - assertNotNull(response); - - // Verify the response contains the replaced metadata - String content = response.getData().content(); - assertTrue(content.contains("replaced"), "Response should contain replaced metadata: " + content); - - session.close(); - } - } - - /** - * Verifies that preMcpToolCall hook can remove metadata from the MCP request. - * - * @see Snapshot: - * pre_mcp_tool_call_hook/should_remove_meta_via_premcptoolcall_hook - */ - @Disabled("Requires snapshot: pre_mcp_tool_call_hook/should_remove_meta_via_premcptoolcall_hook") - @Test - void testShouldRemoveMetaViaPreMcpToolCallHook() throws Exception { - ctx.configureForTest("pre_mcp_tool_call_hook", "should_remove_meta_via_premcptoolcall_hook"); - - var mcpServers = new HashMap(); - mcpServers.put("meta-echo", new McpStdioServerConfig().setCommand("npx").setArgs(List.of("-y", "mcp-meta-echo")) - .setTools(List.of("*")).setWorkingDirectory(ctx.getWorkDir().toString())); - - var hooks = new SessionHooks().setOnPreMcpToolCall((input, invocation) -> { - // Return output with null metaToUse to remove metadata - return CompletableFuture.completedFuture(PreMcpToolCallHookOutput.removeMeta()); - }); - - try (CopilotClient client = ctx.createClient()) { - CopilotSession session = client.createSession(new SessionConfig().setMcpServers(mcpServers).setHooks(hooks) - .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get(); - - AssistantMessageEvent response = session.sendAndWait(new MessageOptions().setPrompt( - "Use the meta-echo/echo_meta tool with value 'test-remove'. Reply with just the raw tool result.")) - .get(60, TimeUnit.SECONDS); - - assertNotNull(response); + var config = new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL).setHooks(hooks); - session.close(); + var ex = assertThrows(ExecutionException.class, () -> client.createSession(config).get()); + assertTrue(ex.toString().contains(UNSUPPORTED_SDK_HOOKS_MESSAGE), + () -> "Expected unsupported hooks error, got: " + ex); } } } diff --git a/nodejs/test/e2e/hooks.e2e.test.ts b/nodejs/test/e2e/hooks.e2e.test.ts index 895097adbc..ae9e265e3f 100644 --- a/nodejs/test/e2e/hooks.e2e.test.ts +++ b/nodejs/test/e2e/hooks.e2e.test.ts @@ -2,155 +2,39 @@ * Copyright (c) Microsoft Corporation. All rights reserved. *--------------------------------------------------------------------------------------------*/ -import { readFile, writeFile } from "fs/promises"; -import { join } from "path"; import { describe, expect, it } from "vitest"; -import type { - PreToolUseHookInput, - PreToolUseHookOutput, - PostToolUseHookInput, - PostToolUseHookOutput, -} from "../../src/index.js"; import { approveAll } from "../../src/index.js"; +import type { SessionHooks } from "../../src/types.js"; import { createSdkTestContext } from "./harness/sdkTestContext.js"; -describe("Session hooks", async () => { - const { copilotClient: client, workDir } = await createSdkTestContext(); - - it("should invoke preToolUse hook when model runs a tool", async () => { - const preToolUseInputs: PreToolUseHookInput[] = []; - - const session = await client.createSession({ - onPermissionRequest: approveAll, - hooks: { - onPreToolUse: async (input, invocation) => { - preToolUseInputs.push(input); - expect(invocation.sessionId).toBe(session.sessionId); - // Allow the tool to run - return { permissionDecision: "allow" } as PreToolUseHookOutput; - }, - }, - }); - - // Create a file for the model to read - await writeFile(join(workDir, "hello.txt"), "Hello from the test!"); - - await session.sendAndWait({ - prompt: "Read the contents of hello.txt and tell me what it says", - }); - - // Should have received at least one preToolUse hook call - expect(preToolUseInputs.length).toBeGreaterThan(0); - - // Should have received the tool name - expect(preToolUseInputs.some((input) => input.toolName)).toBe(true); - - await session.disconnect(); - }); - - it("should invoke postToolUse hook after model runs a tool", async () => { - const postToolUseInputs: PostToolUseHookInput[] = []; - - const session = await client.createSession({ - onPermissionRequest: approveAll, - hooks: { - onPostToolUse: async (input, invocation) => { - postToolUseInputs.push(input); - expect(invocation.sessionId).toBe(session.sessionId); - return null as PostToolUseHookOutput; - }, - }, - }); - - // Create a file for the model to read - await writeFile(join(workDir, "world.txt"), "World from the test!"); +const UNSUPPORTED_SDK_HOOKS_MESSAGE = "SDK hook callbacks are no longer supported"; - await session.sendAndWait({ - prompt: "Read the contents of world.txt and tell me what it says", - }); - - // Should have received at least one postToolUse hook call - expect(postToolUseInputs.length).toBeGreaterThan(0); - - // Should have received the tool name and result - expect(postToolUseInputs.some((input) => input.toolName)).toBe(true); - expect(postToolUseInputs.some((input) => input.toolResult !== undefined)).toBe(true); - - await session.disconnect(); - }); - - it("should invoke both preToolUse and postToolUse hooks for a single tool call", async () => { - const preToolUseInputs: PreToolUseHookInput[] = []; - const postToolUseInputs: PostToolUseHookInput[] = []; - - const session = await client.createSession({ - onPermissionRequest: approveAll, - hooks: { - onPreToolUse: async (input) => { - preToolUseInputs.push(input); - return { permissionDecision: "allow" } as PreToolUseHookOutput; - }, - onPostToolUse: async (input) => { - postToolUseInputs.push(input); - return null as PostToolUseHookOutput; - }, - }, - }); - - await writeFile(join(workDir, "both.txt"), "Testing both hooks!"); - - await session.sendAndWait({ - prompt: "Read the contents of both.txt", - }); - - // Both hooks should have been called - expect(preToolUseInputs.length).toBeGreaterThan(0); - expect(postToolUseInputs.length).toBeGreaterThan(0); - - // The same tool should appear in both - const preToolNames = preToolUseInputs.map((i) => i.toolName); - const postToolNames = postToolUseInputs.map((i) => i.toolName); - const commonTool = preToolNames.find((name) => postToolNames.includes(name)); - expect(commonTool).toBeDefined(); - - await session.disconnect(); - }); - - it("should deny tool execution when preToolUse returns deny", async () => { - const preToolUseInputs: PreToolUseHookInput[] = []; - - const session = await client.createSession({ - onPermissionRequest: approveAll, - hooks: { - onPreToolUse: async (input) => { - preToolUseInputs.push(input); - // Deny all tool calls - return { permissionDecision: "deny" } as PreToolUseHookOutput; - }, +describe("Session hooks", async () => { + const { copilotClient: client } = await createSdkTestContext(); + + async function expectUnsupportedHooks(hooks: SessionHooks) { + await expect( + client.createSession({ + onPermissionRequest: approveAll, + hooks, + }) + ).rejects.toThrow(UNSUPPORTED_SDK_HOOKS_MESSAGE); + } + + const hookCases: Array<[string, SessionHooks]> = [ + ["preToolUse", { onPreToolUse: () => ({ permissionDecision: "allow" }) }], + ["postToolUse", { onPostToolUse: () => undefined }], + ["preToolUse denial", { onPreToolUse: () => ({ permissionDecision: "deny" }) }], + [ + "combined preToolUse and postToolUse", + { + onPreToolUse: () => ({ permissionDecision: "allow" }), + onPostToolUse: () => undefined, }, - }); - - // Create a file - const originalContent = "Original content that should not be modified"; - await writeFile(join(workDir, "protected.txt"), originalContent); - - const response = await session.sendAndWait({ - prompt: "Edit protected.txt and replace 'Original' with 'Modified'", - }); - - // The hook should have been called - expect(preToolUseInputs.length).toBeGreaterThan(0); - - // The response should indicate the tool was denied (behavior may vary) - // At minimum, we verify the hook was invoked - expect(response).toBeDefined(); - - // Strengthen: verify the actual deny behavior — the protected file was NOT - // modified by the runtime even though the LLM tried to edit it. The - // pre-tool-use hook denial blocks tool execution before it can mutate state. - const actualContent = await readFile(join(workDir, "protected.txt"), "utf-8"); - expect(actualContent).toBe(originalContent); + ], + ]; - await session.disconnect(); + it.each(hookCases)("rejects SDK callback hook %s", async (_name, hooks) => { + await expectUnsupportedHooks(hooks); }); }); diff --git a/nodejs/test/e2e/hooks_extended.e2e.test.ts b/nodejs/test/e2e/hooks_extended.e2e.test.ts index caa69399ed..61e6b0ed24 100644 --- a/nodejs/test/e2e/hooks_extended.e2e.test.ts +++ b/nodejs/test/e2e/hooks_extended.e2e.test.ts @@ -3,337 +3,41 @@ *--------------------------------------------------------------------------------------------*/ import { describe, expect, it } from "vitest"; -import { z } from "zod"; -import { approveAll, defineTool } from "../../src/index.js"; -import type { - ErrorOccurredHookInput, - PostToolUseFailureHookInput, - PostToolUseHookInput, - PreToolUseHookInput, - SessionEndHookInput, - SessionStartHookInput, - UserPromptSubmittedHookInput, -} from "../../src/types.js"; +import { approveAll } from "../../src/index.js"; +import type { SessionHooks } from "../../src/types.js"; import { createSdkTestContext } from "./harness/sdkTestContext.js"; +const UNSUPPORTED_SDK_HOOKS_MESSAGE = "SDK hook callbacks are no longer supported"; + describe("Extended session hooks", async () => { const { copilotClient: client } = await createSdkTestContext(); - it("should invoke onSessionStart hook on new session", async () => { - const sessionStartInputs: SessionStartHookInput[] = []; - - const session = await client.createSession({ - onPermissionRequest: approveAll, - hooks: { - onSessionStart: async (input, invocation) => { - sessionStartInputs.push(input); - expect(invocation.sessionId).toBe(session.sessionId); - }, - }, - }); - - await session.sendAndWait({ - prompt: "Say hi", - }); - - expect(sessionStartInputs.length).toBeGreaterThan(0); - expect(sessionStartInputs[0].source).toBe("new"); - expect(sessionStartInputs[0].timestamp).toBeInstanceOf(Date); - expect(sessionStartInputs[0].workingDirectory).toBeDefined(); - - await session.disconnect(); - }); - - it("should invoke onUserPromptSubmitted hook when sending a message", async () => { - const userPromptInputs: UserPromptSubmittedHookInput[] = []; - - const session = await client.createSession({ - onPermissionRequest: approveAll, - hooks: { - onUserPromptSubmitted: async (input, invocation) => { - userPromptInputs.push(input); - expect(invocation.sessionId).toBe(session.sessionId); - }, - }, - }); - - await session.sendAndWait({ - prompt: "Say hello", - }); - - expect(userPromptInputs.length).toBeGreaterThan(0); - expect(userPromptInputs[0].prompt).toContain("Say hello"); - expect(userPromptInputs[0].timestamp).toBeInstanceOf(Date); - expect(userPromptInputs[0].workingDirectory).toBeDefined(); - - await session.disconnect(); - }); - - it("should invoke onSessionEnd hook when session is disconnected", async () => { - const sessionEndInputs: SessionEndHookInput[] = []; - - const session = await client.createSession({ - onPermissionRequest: approveAll, - hooks: { - onSessionEnd: async (input, invocation) => { - sessionEndInputs.push(input); - expect(invocation.sessionId).toBe(session.sessionId); - }, - }, - }); - - await session.sendAndWait({ - prompt: "Say hi", - }); - - await session.disconnect(); - - // Wait briefly for async hook - await new Promise((resolve) => setTimeout(resolve, 100)); - - expect(sessionEndInputs.length).toBeGreaterThan(0); - }); - - it("should invoke onErrorOccurred hook when error occurs", async () => { - const errorInputs: ErrorOccurredHookInput[] = []; - - const session = await client.createSession({ - onPermissionRequest: approveAll, - hooks: { - onErrorOccurred: async (input, invocation) => { - errorInputs.push(input); - expect(invocation.sessionId).toBe(session.sessionId); - expect(input.timestamp).toBeInstanceOf(Date); - expect(input.workingDirectory).toBeDefined(); - expect(input.error).toBeDefined(); - expect(["model_call", "tool_execution", "system", "user_input"]).toContain( - input.errorContext - ); - expect(typeof input.recoverable).toBe("boolean"); - }, - }, - }); - - await session.sendAndWait({ - prompt: "Say hi", - }); - - // onErrorOccurred is dispatched by the runtime for actual errors (model failures, system errors). - // In a normal session it may not fire. Verify the hook is properly wired by checking - // that the session works correctly with the hook registered. - // If the hook did fire, the assertions inside it would have run. - expect(session.sessionId).toBeDefined(); - - await session.disconnect(); - }); - - it("should invoke userPromptSubmitted hook and modify prompt", async () => { - const inputs: UserPromptSubmittedHookInput[] = []; - const session = await client.createSession({ - onPermissionRequest: approveAll, - hooks: { - onUserPromptSubmitted: async (input, invocation) => { - inputs.push(input); - expect(invocation.sessionId).toBeTruthy(); - return { modifiedPrompt: "Reply with exactly: HOOKED_PROMPT" }; - }, - }, - }); - - const response = await session.sendAndWait({ prompt: "Say something else" }); - - expect(inputs.length).toBeGreaterThan(0); - expect(inputs[0].prompt).toContain("Say something else"); - expect(response?.data.content ?? "").toContain("HOOKED_PROMPT"); - - await session.disconnect(); - }); - - it("should invoke sessionStart hook", async () => { - const inputs: SessionStartHookInput[] = []; - const session = await client.createSession({ - onPermissionRequest: approveAll, - hooks: { - onSessionStart: async (input, invocation) => { - inputs.push(input); - expect(invocation.sessionId).toBeTruthy(); - return { additionalContext: "Session start hook context." }; - }, - }, - }); - - await session.sendAndWait({ prompt: "Say hi" }); - - expect(inputs.length).toBeGreaterThan(0); - expect(inputs[0].source).toBe("new"); - expect(inputs[0].workingDirectory).toBeTruthy(); - - await session.disconnect(); - }); - - it("should invoke sessionEnd hook", async () => { - const inputs: SessionEndHookInput[] = []; - let resolveHook!: (value: SessionEndHookInput) => void; - const hookInvoked = new Promise((resolve) => { - resolveHook = resolve; - }); - - const session = await client.createSession({ - onPermissionRequest: approveAll, - hooks: { - onSessionEnd: async (input, invocation) => { - inputs.push(input); - expect(invocation.sessionId).toBeTruthy(); - resolveHook(input); - return { sessionSummary: "session ended" }; - }, - }, - }); - - await session.sendAndWait({ prompt: "Say bye" }); - await session.disconnect(); - - let timer: NodeJS.Timeout | undefined; - try { - await Promise.race([ - hookInvoked, - new Promise((_, reject) => { - timer = setTimeout(() => reject(new Error("Timeout: onSessionEnd")), 10_000); - }), - ]); - } finally { - if (timer) clearTimeout(timer); - } - - expect(inputs.length).toBeGreaterThan(0); - }); - - it("should register erroroccurred hook", async () => { - const inputs: ErrorOccurredHookInput[] = []; - const session = await client.createSession({ - onPermissionRequest: approveAll, - hooks: { - onErrorOccurred: async (input, invocation) => { - inputs.push(input); - expect(invocation.sessionId).toBeTruthy(); - return { errorHandling: "skip" }; - }, - }, - }); - - await session.sendAndWait({ prompt: "Say hi" }); - - // OnErrorOccurred is dispatched only by genuine runtime errors. A normal turn - // cannot deterministically trigger one; this test is registration-only. - expect(inputs.length).toBe(0); - expect(session.sessionId).toBeTruthy(); - - await session.disconnect(); - }); - - it("should allow preToolUse to return modifiedArgs and suppressOutput", async () => { - const inputs: PreToolUseHookInput[] = []; - const session = await client.createSession({ - onPermissionRequest: approveAll, - tools: [ - defineTool("echo_value", { - description: "Echoes the supplied value", - parameters: z.object({ value: z.string() }), - handler: ({ value }) => value, - }), - ], - hooks: { - onPreToolUse: async (input) => { - inputs.push(input); - if (input.toolName !== "echo_value") { - return { permissionDecision: "allow" }; - } - return { - permissionDecision: "allow", - modifiedArgs: { value: "modified by hook" }, - suppressOutput: false, - }; - }, - }, - }); - - const response = await session.sendAndWait({ - prompt: "Call echo_value with value 'original', then reply with the result.", - }); - - expect(inputs.length).toBeGreaterThan(0); - expect(inputs.some((input) => input.toolName === "echo_value")).toBe(true); - expect(response?.data.content ?? "").toContain("modified by hook"); - - await session.disconnect(); - }); - - it("should allow postToolUse to return modifiedResult", async () => { - const inputs: PostToolUseHookInput[] = []; - const session = await client.createSession({ - onPermissionRequest: approveAll, - hooks: { - onPostToolUse: async (input) => { - inputs.push(input); - if (input.toolName !== "view") { - return undefined; - } - return { - modifiedResult: { - textResultForLlm: "modified by post hook", - resultType: "success", - toolTelemetry: {}, - }, - suppressOutput: false, - }; - }, - }, - }); - - const response = await session.sendAndWait({ - prompt: "Call the view tool to read the current directory, then reply done.", - }); - - expect(inputs.some((input) => input.toolName === "view")).toBe(true); - expect(response?.data.content?.toLowerCase()).toContain("done"); - - await session.disconnect(); - }); - - it.skip("should invoke postToolUseFailure hook for failed tool result", async () => { - // TODO: This test fails with 1.0.64-0 runtime due to built-in tools not being - // available when hooks are configured. Runtime returns "Tool 'view' does not exist. - // Available tools: report_intent" even though view is a built-in and availableTools - // wasn't specified. Follow up with runtime team. - const failureInputs: PostToolUseFailureHookInput[] = []; - const postToolUseInputs: PostToolUseHookInput[] = []; - const session = await client.createSession({ - onPermissionRequest: approveAll, - hooks: { - onPostToolUse: async (input) => { - postToolUseInputs.push(input); - }, - onPostToolUseFailure: async (input, invocation) => { - failureInputs.push(input); - expect(invocation.sessionId).toBe(session.sessionId); - return { additionalContext: "HOOK_FAILURE_GUIDANCE_APPLIED" }; - }, - }, - }); - - const response = await session.sendAndWait({ - prompt: "Call the view tool with path 'missing.txt'. If it fails, use the hook guidance to answer.", - }); - - expect(postToolUseInputs).toHaveLength(0); - expect(failureInputs).toHaveLength(1); - expect(failureInputs[0].toolName).toBe("view"); - expect(failureInputs[0].error).toContain("does not exist"); - expect((failureInputs[0].toolArgs as { path?: string }).path).toContain("missing.txt"); - expect(failureInputs[0].timestamp).toBeInstanceOf(Date); - expect(failureInputs[0].workingDirectory).toBeTruthy(); - expect(response?.data.content ?? "").toContain("HOOK_FAILURE_GUIDANCE_APPLIED"); - - await session.disconnect(); + async function expectUnsupportedHooks(hooks: SessionHooks) { + await expect( + client.createSession({ + onPermissionRequest: approveAll, + hooks, + }) + ).rejects.toThrow(UNSUPPORTED_SDK_HOOKS_MESSAGE); + } + + const hookCases: Array<[string, SessionHooks]> = [ + ["userPromptSubmitted", { onUserPromptSubmitted: () => undefined }], + ["sessionStart", { onSessionStart: () => undefined }], + ["sessionEnd", { onSessionEnd: () => undefined }], + ["errorOccurred", { onErrorOccurred: () => undefined }], + ["preToolUse output", { onPreToolUse: () => ({ permissionDecision: "allow" }) }], + ["postToolUse output", { onPostToolUse: () => ({ suppressOutput: false }) }], + [ + "postToolUseFailure output", + { + onPostToolUse: () => undefined, + onPostToolUseFailure: () => ({ additionalContext: "not used" }), + }, + ], + ]; + + it.each(hookCases)("rejects SDK callback hook %s", async (_name, hooks) => { + await expectUnsupportedHooks(hooks); }); }); diff --git a/nodejs/test/e2e/pre_mcp_tool_call_hook.e2e.test.ts b/nodejs/test/e2e/pre_mcp_tool_call_hook.e2e.test.ts index 5711132397..2ca34b716a 100644 --- a/nodejs/test/e2e/pre_mcp_tool_call_hook.e2e.test.ts +++ b/nodejs/test/e2e/pre_mcp_tool_call_hook.e2e.test.ts @@ -2,131 +2,26 @@ * Copyright (c) Microsoft Corporation. All rights reserved. *--------------------------------------------------------------------------------------------*/ -import { dirname, resolve } from "path"; -import { fileURLToPath } from "url"; import { describe, expect, it } from "vitest"; import { approveAll } from "../../src/index.js"; -import type { MCPStdioServerConfig, PreMcpToolCallHookInput } from "../../src/types.js"; +import type { SessionHooks } from "../../src/types.js"; import { createSdkTestContext } from "./harness/sdkTestContext.js"; -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); -const TEST_MCP_META_ECHO_SERVER = resolve( - __dirname, - "../../../test/harness/test-mcp-meta-echo-server.mjs" -); -const TEST_HARNESS_DIR = dirname(TEST_MCP_META_ECHO_SERVER); +const UNSUPPORTED_SDK_HOOKS_MESSAGE = "SDK hook callbacks are no longer supported"; describe("pre_mcp_tool_call_hook", async () => { const { copilotClient: client } = await createSdkTestContext(); - it("should set meta via preMcpToolCall hook", async () => { - const hookInputs: PreMcpToolCallHookInput[] = []; - - const session = await client.createSession({ - onPermissionRequest: approveAll, - mcpServers: { - "meta-echo": { - command: "node", - args: [TEST_MCP_META_ECHO_SERVER], - workingDirectory: TEST_HARNESS_DIR, - tools: ["*"], - } as MCPStdioServerConfig, - }, - hooks: { - onPreMcpToolCall: async (input, _invocation) => { - hookInputs.push(input); - return { metaToUse: { injected: "by-hook", source: "test" } }; - }, - }, - }); - - const message = await session.sendAndWait({ - prompt: "Use the meta-echo/echo_meta tool with value 'test-set'. Reply with just the raw tool result.", - }); - - expect(message).not.toBeNull(); - expect(message!.data.content).toContain("injected"); - expect(message!.data.content).toContain("by-hook"); - - expect(hookInputs.length).toBeGreaterThan(0); - expect(hookInputs[0].serverName).toBe("meta-echo"); - expect(hookInputs[0].toolName).toBe("echo_meta"); - expect(hookInputs[0].workingDirectory).toBeDefined(); - expect(hookInputs[0].timestamp).toBeInstanceOf(Date); - - await session.disconnect(); - }); - - it("should replace meta via preMcpToolCall hook", async () => { - const hookInputs: PreMcpToolCallHookInput[] = []; - - const session = await client.createSession({ - onPermissionRequest: approveAll, - mcpServers: { - "meta-echo": { - command: "node", - args: [TEST_MCP_META_ECHO_SERVER], - workingDirectory: TEST_HARNESS_DIR, - tools: ["*"], - } as MCPStdioServerConfig, - }, - hooks: { - onPreMcpToolCall: async (input, _invocation) => { - hookInputs.push(input); - return { metaToUse: { completely: "replaced" } }; - }, - }, - }); - - const message = await session.sendAndWait({ - prompt: "Use the meta-echo/echo_meta tool with value 'test-replace'. Reply with just the raw tool result.", - }); - - expect(message).not.toBeNull(); - expect(message!.data.content).toContain("completely"); - expect(message!.data.content).toContain("replaced"); - - expect(hookInputs.length).toBeGreaterThan(0); - expect(hookInputs[0].serverName).toBe("meta-echo"); - expect(hookInputs[0].toolName).toBe("echo_meta"); - - await session.disconnect(); - }); - - it("should remove meta via preMcpToolCall hook", async () => { - const hookInputs: PreMcpToolCallHookInput[] = []; - - const session = await client.createSession({ - onPermissionRequest: approveAll, - mcpServers: { - "meta-echo": { - command: "node", - args: [TEST_MCP_META_ECHO_SERVER], - workingDirectory: TEST_HARNESS_DIR, - tools: ["*"], - } as MCPStdioServerConfig, - }, - hooks: { - onPreMcpToolCall: async (input, _invocation) => { - hookInputs.push(input); - return { metaToUse: null }; - }, - }, - }); - - const message = await session.sendAndWait({ - prompt: "Use the meta-echo/echo_meta tool with value 'test-remove'. Reply with just the raw tool result.", - }); - - expect(message).not.toBeNull(); - expect(message!.data.content).toContain('"meta":null'); - expect(message!.data.content).toContain("test-remove"); - - expect(hookInputs.length).toBeGreaterThan(0); - expect(hookInputs[0].serverName).toBe("meta-echo"); - expect(hookInputs[0].toolName).toBe("echo_meta"); - - await session.disconnect(); + it("rejects SDK preMcpToolCall callback hooks", async () => { + const hooks: SessionHooks = { + onPreMcpToolCall: () => ({ metaToUse: { injected: "by-hook" } }), + }; + + await expect( + client.createSession({ + onPermissionRequest: approveAll, + hooks, + }) + ).rejects.toThrow(UNSUPPORTED_SDK_HOOKS_MESSAGE); }); }); diff --git a/nodejs/test/e2e/subagent_hooks.e2e.test.ts b/nodejs/test/e2e/subagent_hooks.e2e.test.ts index 0e6c2e95e0..bfb7a4339b 100644 --- a/nodejs/test/e2e/subagent_hooks.e2e.test.ts +++ b/nodejs/test/e2e/subagent_hooks.e2e.test.ts @@ -2,85 +2,27 @@ * Copyright (c) Microsoft Corporation. All rights reserved. *--------------------------------------------------------------------------------------------*/ -import { writeFile } from "fs/promises"; -import { join } from "path"; import { describe, expect, it } from "vitest"; -import type { - PreToolUseHookInput, - PreToolUseHookOutput, - PostToolUseHookInput, - PostToolUseHookOutput, -} from "../../src/index.js"; import { approveAll } from "../../src/index.js"; -import { createSdkTestContext, isCI } from "./harness/sdkTestContext.js"; +import type { SessionHooks } from "../../src/types.js"; +import { createSdkTestContext } from "./harness/sdkTestContext.js"; + +const UNSUPPORTED_SDK_HOOKS_MESSAGE = "SDK hook callbacks are no longer supported"; describe("Subagent hooks", async () => { - // For snapshot recording (non-CI), use RECORD_GH_TOKEN if available - const recordToken = !isCI ? process.env.RECORD_GH_TOKEN : undefined; - const { - copilotClient: client, - workDir, - env, - } = await createSdkTestContext({ - ...(recordToken ? { copilotClientOptions: { gitHubToken: recordToken } } : {}), + const { copilotClient: client } = await createSdkTestContext(); + + it("rejects SDK callback hooks for sub-agent hook propagation", async () => { + const hooks: SessionHooks = { + onPreToolUse: () => ({ permissionDecision: "allow" }), + onPostToolUse: () => undefined, + }; + + await expect( + client.createSession({ + onPermissionRequest: approveAll, + hooks, + }) + ).rejects.toThrow(UNSUPPORTED_SDK_HOOKS_MESSAGE); }); - // Sub-agent hook propagation requires the session-based subagents feature flag. - // Without this flag, the legacy callback-bridge path is used, which does not - // support SDK preToolUse/postToolUse hooks for sub-agent tool calls. - env.COPILOT_EXP_COPILOT_CLI_SESSION_BASED_SUBAGENTS = "true"; - - it("should invoke preToolUse and postToolUse hooks for sub-agent tool calls", async () => { - const hookLog: { kind: "pre" | "post"; toolName: string; sessionId: string }[] = []; - - const session = await client.createSession({ - onPermissionRequest: approveAll, - hooks: { - onPreToolUse: async (input: PreToolUseHookInput) => { - hookLog.push({ - kind: "pre", - toolName: input.toolName, - sessionId: input.sessionId, - }); - return { permissionDecision: "allow" } as PreToolUseHookOutput; - }, - onPostToolUse: async (input: PostToolUseHookInput) => { - hookLog.push({ - kind: "post", - toolName: input.toolName, - sessionId: input.sessionId, - }); - return null as PostToolUseHookOutput; - }, - }, - }); - - // Create a file for the sub-agent to read - await writeFile(join(workDir, "subagent-test.txt"), "Hello from subagent test!"); - - await session.sendAndWait({ - prompt: "Use the task tool to spawn an explore agent that reads the file subagent-test.txt in the current directory and reports its contents. You must use the task tool.", - }); - - // Parent tool hooks fire for "task" - const taskPre = hookLog.find((h) => h.kind === "pre" && h.toolName === "task"); - expect(taskPre, "preToolUse should fire for the parent's 'task' tool call").toBeDefined(); - - // Sub-agent tool hooks fire for "view" - const viewPre = hookLog.filter((h) => h.kind === "pre" && h.toolName === "view"); - const viewPost = hookLog.filter((h) => h.kind === "post" && h.toolName === "view"); - expect( - viewPre.length, - "preToolUse should fire for the sub-agent's 'view' tool call" - ).toBeGreaterThan(0); - expect( - viewPost.length, - "postToolUse should fire for the sub-agent's 'view' tool call" - ).toBeGreaterThan(0); - - // input.sessionId distinguishes parent from sub-agent: parent tools and - // sub-agent tools carry different sessionIds - expect(viewPre[0].sessionId).not.toBe(taskPre!.sessionId); - - await session.disconnect(); - }, 120_000); }); diff --git a/python/e2e/test_hooks_e2e.py b/python/e2e/test_hooks_e2e.py index 088379d4c7..b9fec80d7d 100644 --- a/python/e2e/test_hooks_e2e.py +++ b/python/e2e/test_hooks_e2e.py @@ -1,155 +1,45 @@ -""" -Tests for session hooks functionality -""" - -import os +"""E2E coverage for SDK callback hook rejection.""" import pytest from copilot.session import PermissionHandler from .testharness import E2ETestContext -from .testharness.helper import write_file pytestmark = pytest.mark.asyncio(loop_scope="module") +UNSUPPORTED_SDK_HOOKS_MESSAGE = "SDK hook callbacks are no longer supported" -class TestHooks: - async def test_should_invoke_pretooluse_hook_when_model_runs_a_tool(self, ctx: E2ETestContext): - """Test that preToolUse hook is invoked when model runs a tool""" - pre_tool_use_inputs = [] - - async def on_pre_tool_use(input_data, invocation): - pre_tool_use_inputs.append(input_data) - assert invocation["session_id"] == session.session_id - # Allow the tool to run - return {"permissionDecision": "allow"} - - session = await ctx.client.create_session( - on_permission_request=PermissionHandler.approve_all, - hooks={"on_pre_tool_use": on_pre_tool_use}, - ) - - # Create a file for the model to read - write_file(ctx.work_dir, "hello.txt", "Hello from the test!") - - await session.send_and_wait("Read the contents of hello.txt and tell me what it says") - - # Should have received at least one preToolUse hook call - assert len(pre_tool_use_inputs) > 0 - - # Should have received the tool name - assert any(inp.get("toolName") for inp in pre_tool_use_inputs) - - await session.disconnect() - - async def test_should_invoke_posttooluse_hook_after_model_runs_a_tool( - self, ctx: E2ETestContext - ): - """Test that postToolUse hook is invoked after model runs a tool""" - post_tool_use_inputs = [] - - async def on_post_tool_use(input_data, invocation): - post_tool_use_inputs.append(input_data) - assert invocation["session_id"] == session.session_id - return None - - session = await ctx.client.create_session( - on_permission_request=PermissionHandler.approve_all, - hooks={"on_post_tool_use": on_post_tool_use}, - ) - - # Create a file for the model to read - write_file(ctx.work_dir, "world.txt", "World from the test!") - - await session.send_and_wait("Read the contents of world.txt and tell me what it says") - # Should have received at least one postToolUse hook call - assert len(post_tool_use_inputs) > 0 +async def _allow(*_args): + return {"permissionDecision": "allow"} - # Should have received the tool name and result - assert any(inp.get("toolName") for inp in post_tool_use_inputs) - assert any(inp.get("toolResult") is not None for inp in post_tool_use_inputs) - await session.disconnect() +async def _deny(*_args): + return {"permissionDecision": "deny"} - async def test_should_invoke_both_pretooluse_and_posttooluse_hooks_for_a_single_tool_call( - self, ctx: E2ETestContext - ): - """Test that both preToolUse and postToolUse hooks fire for the same tool call""" - pre_tool_use_inputs = [] - post_tool_use_inputs = [] - async def on_pre_tool_use(input_data, invocation): - pre_tool_use_inputs.append(input_data) - return {"permissionDecision": "allow"} +async def _noop(*_args): + return None - async def on_post_tool_use(input_data, invocation): - post_tool_use_inputs.append(input_data) - return None - session = await ctx.client.create_session( +async def assert_unsupported_hooks(ctx: E2ETestContext, hooks: dict): + with pytest.raises(Exception, match=UNSUPPORTED_SDK_HOOKS_MESSAGE): + await ctx.client.create_session( on_permission_request=PermissionHandler.approve_all, - hooks={ - "on_pre_tool_use": on_pre_tool_use, - "on_post_tool_use": on_post_tool_use, - }, + hooks=hooks, ) - write_file(ctx.work_dir, "both.txt", "Testing both hooks!") - await session.send_and_wait("Read the contents of both.txt") - - # Both hooks should have been called - assert len(pre_tool_use_inputs) > 0 - assert len(post_tool_use_inputs) > 0 - - # The same tool should appear in both - pre_tool_names = [inp.get("toolName") for inp in pre_tool_use_inputs] - post_tool_names = [inp.get("toolName") for inp in post_tool_use_inputs] - common_tool = next((name for name in pre_tool_names if name in post_tool_names), None) - assert common_tool is not None - - await session.disconnect() - - async def test_should_deny_tool_execution_when_pretooluse_returns_deny( - self, ctx: E2ETestContext - ): - """Test that returning deny in preToolUse prevents tool execution""" - pre_tool_use_inputs = [] - - async def on_pre_tool_use(input_data, invocation): - pre_tool_use_inputs.append(input_data) - # Deny all tool calls - return {"permissionDecision": "deny"} - - session = await ctx.client.create_session( - on_permission_request=PermissionHandler.approve_all, - hooks={"on_pre_tool_use": on_pre_tool_use}, - ) - - # Create a file - original_content = "Original content that should not be modified" - write_file(ctx.work_dir, "protected.txt", original_content) - - response = await session.send_and_wait( - "Edit protected.txt and replace 'Original' with 'Modified'" - ) - - # The hook should have been called - assert len(pre_tool_use_inputs) > 0 - - # The response should indicate the tool was denied (behavior may vary) - # At minimum, we verify the hook was invoked - assert response is not None - - # Strengthen: verify the actual deny behavior — the protected file was NOT - # modified by the runtime even though the LLM tried to edit it. The - # pre-tool-use hook denial blocks tool execution before it can mutate state. - with open(os.path.join(ctx.work_dir, "protected.txt")) as f: - actual_content = f.read() - assert actual_content == original_content, ( - f"protected.txt should be unchanged after deny; got: {actual_content!r}" - ) - - await session.disconnect() +class TestHooks: + @pytest.mark.parametrize( + "hooks", + [ + {"on_pre_tool_use": _allow}, + {"on_post_tool_use": _noop}, + {"on_pre_tool_use": _deny}, + {"on_pre_tool_use": _allow, "on_post_tool_use": _noop}, + ], + ) + async def test_rejects_sdk_callback_hooks(self, ctx: E2ETestContext, hooks: dict): + await assert_unsupported_hooks(ctx, hooks) diff --git a/python/e2e/test_hooks_extended_e2e.py b/python/e2e/test_hooks_extended_e2e.py index 5ad0ffea43..4055bfa081 100644 --- a/python/e2e/test_hooks_extended_e2e.py +++ b/python/e2e/test_hooks_extended_e2e.py @@ -1,231 +1,52 @@ -""" -Extended hook lifecycle tests that mirror dotnet/test/HookLifecycleAndOutputTests.cs. - -E2E coverage for every handler exposed on ``SessionHooks``: -``on_pre_tool_use``, ``on_post_tool_use``, ``on_post_tool_use_failure``, -``on_user_prompt_submitted``, ``on_session_start``, ``on_session_end``, -``on_error_occurred``. Output-shape behavior (modifiedPrompt / -additionalContext / errorHandling / modifiedArgs / modifiedResult / -sessionSummary) is asserted alongside hook invocation. -""" - -from __future__ import annotations - -import asyncio +"""E2E coverage for SDK lifecycle callback hook rejection.""" import pytest from copilot.session import PermissionHandler -from copilot.tools import Tool, ToolInvocation, ToolResult from .testharness import E2ETestContext pytestmark = pytest.mark.asyncio(loop_scope="module") +UNSUPPORTED_SDK_HOOKS_MESSAGE = "SDK hook callbacks are no longer supported" -class TestHooksExtended: - async def test_should_invoke_userpromptsubmitted_hook_and_modify_prompt( - self, ctx: E2ETestContext - ): - inputs: list[dict] = [] - async def on_user_prompt_submitted(input_data, invocation): - inputs.append(input_data) - assert invocation["session_id"] - return {"modifiedPrompt": "Reply with exactly: HOOKED_PROMPT"} +async def _allow(*_args): + return {"permissionDecision": "allow"} - session = await ctx.client.create_session( - on_permission_request=PermissionHandler.approve_all, - hooks={"on_user_prompt_submitted": on_user_prompt_submitted}, - ) - try: - response = await session.send_and_wait("Say something else") - assert inputs - assert "Say something else" in inputs[0].get("prompt", "") - assert "HOOKED_PROMPT" in (response.data.content or "") - finally: - await session.disconnect() - - async def test_should_invoke_sessionstart_hook(self, ctx: E2ETestContext): - inputs: list[dict] = [] - - async def on_session_start(input_data, invocation): - inputs.append(input_data) - assert invocation["session_id"] - return {"additionalContext": "Session start hook context."} - - session = await ctx.client.create_session( - on_permission_request=PermissionHandler.approve_all, - hooks={"on_session_start": on_session_start}, - ) - try: - await session.send_and_wait("Say hi") - assert inputs - assert inputs[0].get("source") == "new" - assert inputs[0].get("workingDirectory") - finally: - await session.disconnect() - - async def test_should_invoke_sessionend_hook(self, ctx: E2ETestContext): - inputs: list[dict] = [] - hook_invoked: asyncio.Future = asyncio.get_event_loop().create_future() - - async def on_session_end(input_data, invocation): - inputs.append(input_data) - if not hook_invoked.done(): - hook_invoked.set_result(input_data) - assert invocation["session_id"] - return {"sessionSummary": "session ended"} - - session = await ctx.client.create_session( - on_permission_request=PermissionHandler.approve_all, - hooks={"on_session_end": on_session_end}, - ) - await session.send_and_wait("Say bye") - await session.disconnect() - await asyncio.wait_for(hook_invoked, 10.0) - assert inputs - async def test_should_register_erroroccurred_hook(self, ctx: E2ETestContext): - inputs: list[dict] = [] +async def _noop(*_args): + return None - async def on_error_occurred(input_data, invocation): - inputs.append(input_data) - assert invocation["session_id"] - return {"errorHandling": "skip"} - session = await ctx.client.create_session( - on_permission_request=PermissionHandler.approve_all, - hooks={"on_error_occurred": on_error_occurred}, - ) - try: - await session.send_and_wait("Say hi") - # Registration-only test: a healthy turn shouldn't fire OnErrorOccurred. - assert not inputs - assert session.session_id - finally: - await session.disconnect() - - async def test_should_allow_pretooluse_to_return_modifiedargs_and_suppressoutput( - self, ctx: E2ETestContext - ): - inputs: list[dict] = [] - - def echo_value(invocation: ToolInvocation) -> ToolResult: - args = invocation.arguments or {} - return ToolResult(text_result_for_llm=str(args.get("value", ""))) - - async def on_pre_tool_use(input_data, invocation): - inputs.append(input_data) - if input_data.get("toolName") != "echo_value": - return {"permissionDecision": "allow"} - return { - "permissionDecision": "allow", - "modifiedArgs": {"value": "modified by hook"}, - "suppressOutput": False, - } - - session = await ctx.client.create_session( - on_permission_request=PermissionHandler.approve_all, - tools=[ - Tool( - name="echo_value", - description="Echoes the supplied value", - parameters={ - "type": "object", - "properties": { - "value": { - "type": "string", - "description": "Value to echo", - } - }, - "required": ["value"], - }, - handler=echo_value, - ) - ], - hooks={"on_pre_tool_use": on_pre_tool_use}, - ) - try: - response = await session.send_and_wait( - "Call echo_value with value 'original', then reply with the result." - ) - assert inputs - assert any(inp.get("toolName") == "echo_value" for inp in inputs) - assert "modified by hook" in (response.data.content or "") - finally: - await session.disconnect() - - async def test_should_allow_posttooluse_to_return_modifiedresult(self, ctx: E2ETestContext): - inputs: list[dict] = [] - - async def on_post_tool_use(input_data, invocation): - inputs.append(input_data) - if input_data.get("toolName") != "view": - return None - return { - "modifiedResult": { - "textResultForLlm": "modified by post hook", - "resultType": "success", - "toolTelemetry": {}, - }, - "suppressOutput": False, - } - - session = await ctx.client.create_session( +async def _modified_prompt(*_args): + return {"modifiedPrompt": "not used"} + + +async def _post_failure(*_args): + return {"additionalContext": "not used"} + + +async def assert_unsupported_hooks(ctx: E2ETestContext, hooks: dict): + with pytest.raises(Exception, match=UNSUPPORTED_SDK_HOOKS_MESSAGE): + await ctx.client.create_session( on_permission_request=PermissionHandler.approve_all, - hooks={"on_post_tool_use": on_post_tool_use}, + hooks=hooks, ) - try: - response = await session.send_and_wait( - "Call the view tool to read the current directory, then reply done." - ) - assert any(inp.get("toolName") == "view" for inp in inputs) - assert "done" in (response.data.content or "").lower() - finally: - await session.disconnect() - - @pytest.mark.skip( - reason="Fails with 1.0.64-0 runtime: built-in tools are not available when hooks " - "restrict availableTools, so the failure path cannot be exercised. " - "Follow up with runtime team." + + +class TestHooksExtended: + @pytest.mark.parametrize( + "hooks", + [ + {"on_user_prompt_submitted": _modified_prompt}, + {"on_session_start": _noop}, + {"on_session_end": _noop}, + {"on_error_occurred": _noop}, + {"on_pre_tool_use": _allow}, + {"on_post_tool_use": _noop}, + {"on_post_tool_use": _noop, "on_post_tool_use_failure": _post_failure}, + ], ) - async def test_should_invoke_posttoolusefailure_hook_for_failed_tool_result( - self, ctx: E2ETestContext - ): - failure_inputs: list[dict] = [] - post_tool_use_inputs: list[dict] = [] - - async def on_post_tool_use(input_data, invocation): - post_tool_use_inputs.append(input_data) - return None - - async def on_post_tool_use_failure(input_data, invocation): - failure_inputs.append(input_data) - assert invocation["session_id"] == session.session_id - return {"additionalContext": "HOOK_FAILURE_GUIDANCE_APPLIED"} - - session = await ctx.client.create_session( - on_permission_request=PermissionHandler.approve_all, - available_tools=["report_intent"], - hooks={ - "on_post_tool_use": on_post_tool_use, - "on_post_tool_use_failure": on_post_tool_use_failure, - }, - ) - try: - response = await session.send_and_wait( - "Call the view tool with path 'missing.txt'. " - "If it fails, use the hook guidance to answer." - ) - assert not post_tool_use_inputs - assert len(failure_inputs) == 1 - failure_input = failure_inputs[0] - assert failure_input["toolName"] == "view" - assert "does not exist" in failure_input["error"] - assert "missing.txt" in failure_input["toolArgs"]["path"] - assert failure_input["timestamp"].timestamp() > 0 - assert failure_input["workingDirectory"] - assert "HOOK_FAILURE_GUIDANCE_APPLIED" in (response.data.content or "") - finally: - await session.disconnect() + async def test_rejects_sdk_callback_hooks(self, ctx: E2ETestContext, hooks: dict): + await assert_unsupported_hooks(ctx, hooks) diff --git a/python/e2e/test_pre_mcp_tool_call_hook_e2e.py b/python/e2e/test_pre_mcp_tool_call_hook_e2e.py index c59994437c..8706e89627 100644 --- a/python/e2e/test_pre_mcp_tool_call_hook_e2e.py +++ b/python/e2e/test_pre_mcp_tool_call_hook_e2e.py @@ -1,120 +1,24 @@ -""" -E2E tests for the preMcpToolCall hook, verifying meta manipulation scenarios: -setting meta, replacing meta, and removing meta. -""" - -from __future__ import annotations - -from datetime import datetime -from pathlib import Path +"""E2E coverage for SDK preMcpToolCall callback hook rejection.""" import pytest -from copilot.session import MCPServerConfig, PermissionHandler +from copilot.session import PermissionHandler from .testharness import E2ETestContext -TEST_MCP_META_ECHO_SERVER = str( - (Path(__file__).parents[2] / "test" / "harness" / "test-mcp-meta-echo-server.mjs").resolve() -) -TEST_HARNESS_DIR = str((Path(__file__).parents[2] / "test" / "harness").resolve()) - pytestmark = pytest.mark.asyncio(loop_scope="module") +UNSUPPORTED_SDK_HOOKS_MESSAGE = "SDK hook callbacks are no longer supported" -def meta_echo_mcp_config() -> dict[str, MCPServerConfig]: - return { - "meta-echo": { - "command": "node", - "args": [TEST_MCP_META_ECHO_SERVER], - "working_directory": TEST_HARNESS_DIR, - "tools": ["*"], - } - } - - -class TestPreMcpToolCallHook: - async def test_should_set_meta_via_premcptoolcall_hook(self, ctx: E2ETestContext): - inputs: list[dict] = [] - - async def on_pre_mcp_tool_call(input_data, invocation): - inputs.append(input_data) - return {"metaToUse": {"injected": "by-hook", "source": "test"}} - - session = await ctx.client.create_session( - on_permission_request=PermissionHandler.approve_all, - mcp_servers=meta_echo_mcp_config(), - hooks={"on_pre_mcp_tool_call": on_pre_mcp_tool_call}, - ) - try: - response = await session.send_and_wait( - "Use the meta-echo/echo_meta tool with value 'test-set'." - " Reply with just the raw tool result." - ) - assert response is not None - assert "injected" in (response.data.content or "") - assert "by-hook" in (response.data.content or "") - - assert inputs - assert inputs[0].get("serverName") == "meta-echo" - assert inputs[0].get("toolName") == "echo_meta" - assert inputs[0].get("workingDirectory") - assert isinstance(inputs[0].get("timestamp"), datetime) - finally: - await session.disconnect() - - async def test_should_replace_meta_via_premcptoolcall_hook(self, ctx: E2ETestContext): - inputs: list[dict] = [] - - async def on_pre_mcp_tool_call(input_data, invocation): - inputs.append(input_data) - return {"metaToUse": {"completely": "replaced"}} - - session = await ctx.client.create_session( - on_permission_request=PermissionHandler.approve_all, - mcp_servers=meta_echo_mcp_config(), - hooks={"on_pre_mcp_tool_call": on_pre_mcp_tool_call}, - ) - try: - response = await session.send_and_wait( - "Use the meta-echo/echo_meta tool with value 'test-replace'." - " Reply with just the raw tool result." - ) - assert response is not None - assert "completely" in (response.data.content or "") - assert "replaced" in (response.data.content or "") - assert inputs - assert inputs[0].get("serverName") == "meta-echo" - assert inputs[0].get("toolName") == "echo_meta" - finally: - await session.disconnect() +async def _pre_mcp_tool_call(*_args): + return {"metaToUse": {"injected": "by-hook"}} - async def test_should_remove_meta_via_premcptoolcall_hook(self, ctx: E2ETestContext): - inputs: list[dict] = [] - async def on_pre_mcp_tool_call(input_data, invocation): - inputs.append(input_data) - return {"metaToUse": None} - - session = await ctx.client.create_session( - on_permission_request=PermissionHandler.approve_all, - mcp_servers=meta_echo_mcp_config(), - hooks={"on_pre_mcp_tool_call": on_pre_mcp_tool_call}, - ) - try: - response = await session.send_and_wait( - "Use the meta-echo/echo_meta tool with value 'test-remove'." - " Reply with just the raw tool result." - ) - assert response is not None - assert '"meta":null' in (response.data.content or "") or '"meta": null' in ( - response.data.content or "" +class TestPreMcpToolCallHook: + async def test_rejects_sdk_premcptoolcall_callback_hook(self, ctx: E2ETestContext): + with pytest.raises(Exception, match=UNSUPPORTED_SDK_HOOKS_MESSAGE): + await ctx.client.create_session( + on_permission_request=PermissionHandler.approve_all, + hooks={"on_pre_mcp_tool_call": _pre_mcp_tool_call}, ) - assert "test-remove" in (response.data.content or "") - - assert inputs - assert inputs[0].get("serverName") == "meta-echo" - assert inputs[0].get("toolName") == "echo_meta" - finally: - await session.disconnect() diff --git a/python/e2e/test_subagent_hooks_e2e.py b/python/e2e/test_subagent_hooks_e2e.py index 1ca2a54c12..9206a6f2e5 100644 --- a/python/e2e/test_subagent_hooks_e2e.py +++ b/python/e2e/test_subagent_hooks_e2e.py @@ -1,92 +1,28 @@ -""" -Tests for sub-agent hooks functionality — verifies preToolUse/postToolUse hooks -fire for tool calls made by sub-agents spawned via the task tool. -""" - -import os +"""E2E coverage for SDK sub-agent callback hook rejection.""" import pytest -from copilot.client import CopilotClient, RuntimeConnection from copilot.session import PermissionHandler from .testharness import E2ETestContext -from .testharness.helper import write_file pytestmark = pytest.mark.asyncio(loop_scope="module") +UNSUPPORTED_SDK_HOOKS_MESSAGE = "SDK hook callbacks are no longer supported" -class TestSubagentHooks: - async def test_should_invoke_pretooluse_and_posttooluse_hooks_for_sub_agent_tool_calls( - self, ctx: E2ETestContext - ): - """Test that preToolUse/postToolUse hooks fire for sub-agent tool calls""" - hook_log = [] - - async def on_pre_tool_use(input_data, invocation): - hook_log.append( - { - "kind": "pre", - "toolName": input_data.get("toolName"), - "sessionId": input_data.get("sessionId"), - } - ) - return {"permissionDecision": "allow"} - - async def on_post_tool_use(input_data, invocation): - hook_log.append( - { - "kind": "post", - "toolName": input_data.get("toolName"), - "sessionId": input_data.get("sessionId"), - } - ) - return None - # Create a client with the session-based subagents feature flag - env = ctx.get_env() - env["COPILOT_EXP_COPILOT_CLI_SESSION_BASED_SUBAGENTS"] = "true" - github_token = ( - "fake-token-for-e2e-tests" if os.environ.get("GITHUB_ACTIONS") == "true" else None - ) - client = CopilotClient( - connection=RuntimeConnection.for_stdio(path=ctx.cli_path), - working_directory=ctx.work_dir, - env=env, - github_token=github_token, - ) +async def _allow(*_args): + return {"permissionDecision": "allow"} - session = await client.create_session( - on_permission_request=PermissionHandler.approve_all, - hooks={ - "on_pre_tool_use": on_pre_tool_use, - "on_post_tool_use": on_post_tool_use, - }, - ) - # Create a file for the sub-agent to read - write_file(ctx.work_dir, "subagent-test.txt", "Hello from subagent test!") +async def _noop(*_args): + return None - await session.send_and_wait( - "Use the task tool to spawn an explore agent that reads the file " - "subagent-test.txt in the current directory and reports its contents. " - "You must use the task tool." - ) - # Parent tool hooks fire for "task" - task_pre = [h for h in hook_log if h["kind"] == "pre" and h["toolName"] == "task"] - assert len(task_pre) >= 1, "preToolUse should fire for the parent's 'task' tool call" - - # Sub-agent tool hooks fire for "view" - view_pre = [h for h in hook_log if h["kind"] == "pre" and h["toolName"] == "view"] - view_post = [h for h in hook_log if h["kind"] == "post" and h["toolName"] == "view"] - assert len(view_pre) > 0, "preToolUse should fire for the sub-agent's 'view' tool call" - assert len(view_post) > 0, "postToolUse should fire for the sub-agent's 'view' tool call" - - # input.session_id distinguishes parent from sub-agent - assert view_pre[0]["sessionId"] != task_pre[0]["sessionId"], ( - "Sub-agent tool hooks should have a different sessionId than parent tool hooks" - ) - - await session.disconnect() - await client.stop() +class TestSubagentHooks: + async def test_rejects_sdk_callback_hooks_for_sub_agents(self, ctx: E2ETestContext): + with pytest.raises(Exception, match=UNSUPPORTED_SDK_HOOKS_MESSAGE): + await ctx.client.create_session( + on_permission_request=PermissionHandler.approve_all, + hooks={"on_pre_tool_use": _allow, "on_post_tool_use": _noop}, + ) diff --git a/rust/src/generated/api_types.rs b/rust/src/generated/api_types.rs index b95c907e3a..1e6caa15ea 100644 --- a/rust/src/generated/api_types.rs +++ b/rust/src/generated/api_types.rs @@ -1,6 +1,7 @@ //! Auto-generated from api.schema.json — do not edit manually. #![allow(clippy::large_enum_variant)] +#![allow(deprecated)] #![allow(dead_code)] #![allow(rustdoc::invalid_html_tags)] diff --git a/rust/src/generated/rpc.rs b/rust/src/generated/rpc.rs index 610db961c5..cb0df3490d 100644 --- a/rust/src/generated/rpc.rs +++ b/rust/src/generated/rpc.rs @@ -7,6 +7,7 @@ #![allow(missing_docs)] #![allow(clippy::too_many_arguments)] +#![allow(deprecated)] #![allow(dead_code)] use super::api_types::{rpc_methods, *}; diff --git a/rust/src/generated/session_events.rs b/rust/src/generated/session_events.rs index 49d8cf5feb..c5f005f3dd 100644 --- a/rust/src/generated/session_events.rs +++ b/rust/src/generated/session_events.rs @@ -1,5 +1,7 @@ //! Auto-generated from session-events.schema.json — do not edit manually. +#![allow(deprecated)] + use std::collections::HashMap; use serde::{Deserialize, Serialize}; diff --git a/rust/tests/e2e/hooks.rs b/rust/tests/e2e/hooks.rs index b4a211d878..08b67d3a11 100644 --- a/rust/tests/e2e/hooks.rs +++ b/rust/tests/e2e/hooks.rs @@ -1,228 +1,69 @@ -use std::collections::HashSet; use std::sync::Arc; use async_trait::async_trait; use github_copilot_sdk::hooks::{ - HookContext, PostToolUseInput, PreToolUseInput, PreToolUseOutput, SessionHooks, + HookContext, PostToolUseInput, PostToolUseOutput, PreToolUseInput, PreToolUseOutput, + SessionHooks, }; -use tokio::sync::mpsc; -use super::support::{recv_with_timeout, with_e2e_context}; +use super::support::with_e2e_context; -#[tokio::test] -async fn should_invoke_pretooluse_hook_when_model_runs_a_tool() { - with_e2e_context( - "hooks", - "should_invoke_pretooluse_hook_when_model_runs_a_tool", - |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - std::fs::write(ctx.work_dir().join("hello.txt"), "Hello from the test!") - .expect("write hello"); - let (pre_tx, mut pre_rx) = mpsc::unbounded_channel(); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config().with_hooks(Arc::new( - RecordingHooks { - pre_tx: Some(pre_tx), - post_tx: None, - deny: false, - }, - ))) - .await - .expect("create session"); - - session - .send_and_wait("Read the contents of hello.txt and tell me what it says") - .await - .expect("send"); - - let input = recv_with_timeout(&mut pre_rx, "preToolUse hook").await; - assert_eq!(input.0, *session.id()); - assert!(!input.1.is_empty()); - - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }, - ) - .await; -} - -#[tokio::test] -async fn should_invoke_posttooluse_hook_after_model_runs_a_tool() { - with_e2e_context( - "hooks", - "should_invoke_posttooluse_hook_after_model_runs_a_tool", - |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - std::fs::write(ctx.work_dir().join("world.txt"), "World from the test!") - .expect("write world"); - let (post_tx, mut post_rx) = mpsc::unbounded_channel(); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config().with_hooks(Arc::new( - RecordingHooks { - pre_tx: None, - post_tx: Some(post_tx), - deny: false, - }, - ))) - .await - .expect("create session"); - - session - .send_and_wait("Read the contents of world.txt and tell me what it says") - .await - .expect("send"); - - let input = recv_with_timeout(&mut post_rx, "postToolUse hook").await; - assert_eq!(input.0, *session.id()); - assert!(!input.1.is_empty()); - assert!(input.2); - - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }, - ) - .await; -} +const UNSUPPORTED_SDK_HOOKS_MESSAGE: &str = "SDK hook callbacks are no longer supported"; #[tokio::test] -async fn should_invoke_both_pretooluse_and_posttooluse_hooks_for_single_tool_call() { - with_e2e_context( - "hooks", - "should_invoke_both_pretooluse_and_posttooluse_hooks_for_single_tool_call", - |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - std::fs::write(ctx.work_dir().join("both.txt"), "Testing both hooks!") - .expect("write both"); - let (pre_tx, mut pre_rx) = mpsc::unbounded_channel(); - let (post_tx, mut post_rx) = mpsc::unbounded_channel(); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config().with_hooks(Arc::new( - RecordingHooks { - pre_tx: Some(pre_tx), - post_tx: Some(post_tx), - deny: false, - }, - ))) - .await - .expect("create session"); - - session - .send_and_wait("Read the contents of both.txt") - .await - .expect("send"); - - let pre = recv_with_timeout(&mut pre_rx, "preToolUse hook").await; - let post = recv_with_timeout(&mut post_rx, "postToolUse hook").await; - assert_eq!(pre.0, *session.id()); - assert_eq!(post.0, *session.id()); - - let mut pre_tools: HashSet = HashSet::from([pre.1]); - while let Ok((_, tool_name)) = pre_rx.try_recv() { - pre_tools.insert(tool_name); - } - let mut post_tools: HashSet = HashSet::from([post.1]); - while let Ok((_, tool_name, _)) = post_rx.try_recv() { - post_tools.insert(tool_name); - } - assert!( - pre_tools.intersection(&post_tools).next().is_some(), - "expected a tool to appear in both pre and post hooks, got pre={pre_tools:?} post={post_tools:?}" - ); - - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }, - ) +async fn rejects_sdk_callback_hooks() { + with_e2e_context("hooks", "rejects_sdk_callback_hooks", |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let client = ctx.start_client().await; + assert_unsupported_hooks( + &client, + ctx.approve_all_session_config() + .with_hooks(Arc::new(RecordingHooks)), + ) + .await; + client.stop().await.expect("stop client"); + }) + }) .await; } -#[tokio::test] -async fn should_deny_tool_execution_when_pretooluse_returns_deny() { - with_e2e_context( - "hooks", - "should_deny_tool_execution_when_pretooluse_returns_deny", - |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let original_content = "Original content that should not be modified"; - let protected_path = ctx.work_dir().join("protected.txt"); - std::fs::write(&protected_path, original_content).expect("write protected"); - let (pre_tx, mut pre_rx) = mpsc::unbounded_channel(); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config().with_hooks(Arc::new( - RecordingHooks { - pre_tx: Some(pre_tx), - post_tx: None, - deny: true, - }, - ))) - .await - .expect("create session"); - - session - .send_and_wait("Edit protected.txt and replace 'Original' with 'Modified'") - .await - .expect("send"); - - let pre = recv_with_timeout(&mut pre_rx, "preToolUse hook").await; - assert_eq!(pre.0, *session.id()); - assert_eq!( - std::fs::read_to_string(protected_path).expect("read protected"), - original_content - ); - - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }, - ) - .await; +async fn assert_unsupported_hooks( + client: &github_copilot_sdk::Client, + config: github_copilot_sdk::SessionConfig, +) { + match client.create_session(config).await { + Ok(session) => { + session.disconnect().await.expect("disconnect session"); + panic!("expected SDK callback hooks to be rejected"); + } + Err(err) => assert!( + err.to_string().contains(UNSUPPORTED_SDK_HOOKS_MESSAGE), + "expected unsupported hooks error, got: {err}" + ), + } } -struct RecordingHooks { - pre_tx: Option>, - post_tx: Option>, - deny: bool, -} +struct RecordingHooks; #[async_trait] impl SessionHooks for RecordingHooks { async fn on_pre_tool_use( &self, - input: PreToolUseInput, - ctx: HookContext, + _input: PreToolUseInput, + _ctx: HookContext, ) -> Option { - if let Some(pre_tx) = &self.pre_tx { - let _ = pre_tx.send((ctx.session_id, input.tool_name)); - } Some(PreToolUseOutput { - permission_decision: Some(if self.deny { "deny" } else { "allow" }.to_string()), + permission_decision: Some("allow".to_string()), ..PreToolUseOutput::default() }) } async fn on_post_tool_use( &self, - input: PostToolUseInput, - ctx: HookContext, - ) -> Option { - if let Some(post_tx) = &self.post_tx { - let _ = post_tx.send(( - ctx.session_id, - input.tool_name, - !input.tool_result.is_null(), - )); - } + _input: PostToolUseInput, + _ctx: HookContext, + ) -> Option { None } } diff --git a/rust/tests/e2e/hooks_extended.rs b/rust/tests/e2e/hooks_extended.rs index ef53987fff..eeda2db3fd 100644 --- a/rust/tests/e2e/hooks_extended.rs +++ b/rust/tests/e2e/hooks_extended.rs @@ -1,417 +1,42 @@ use std::sync::Arc; use async_trait::async_trait; -use github_copilot_sdk::handler::ApproveAllHandler; use github_copilot_sdk::hooks::{ ErrorOccurredInput, ErrorOccurredOutput, HookContext, PostToolUseFailureInput, PostToolUseFailureOutput, PostToolUseInput, PostToolUseOutput, PreToolUseInput, PreToolUseOutput, SessionEndInput, SessionEndOutput, SessionHooks, SessionStartInput, SessionStartOutput, UserPromptSubmittedInput, UserPromptSubmittedOutput, }; -use github_copilot_sdk::tool::ToolHandler; -use github_copilot_sdk::{Error, SessionConfig, Tool, ToolInvocation, ToolResult}; -use serde_json::json; -use tokio::sync::mpsc; -use super::support::{assistant_message_content, recv_with_timeout, with_e2e_context}; +use super::support::with_e2e_context; -#[tokio::test] -async fn should_invoke_onsessionstart_hook_on_new_session() { - with_e2e_context( - "hooks_extended", - "should_invoke_onsessionstart_hook_on_new_session", - |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let (tx, mut rx) = mpsc::unbounded_channel(); - let client = ctx.start_client().await; - let session = client - .create_session( - ctx.approve_all_session_config() - .with_hooks(Arc::new(RecordingHooks::session_start(tx, None))), - ) - .await - .expect("create session"); - - session.send_and_wait("Say hi").await.expect("send"); - let input = recv_with_timeout(&mut rx, "sessionStart hook").await; - assert_eq!(input.source, "new"); - assert!(input.timestamp > 0); - assert!(!input.working_directory.as_os_str().is_empty()); - - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }, - ) - .await; -} - -#[tokio::test] -async fn should_invoke_onuserpromptsubmitted_hook_when_sending_a_message() { - with_e2e_context( - "hooks_extended", - "should_invoke_onuserpromptsubmitted_hook_when_sending_a_message", - |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let (tx, mut rx) = mpsc::unbounded_channel(); - let client = ctx.start_client().await; - let session = client - .create_session( - ctx.approve_all_session_config() - .with_hooks(Arc::new(RecordingHooks::user_prompt(tx, None))), - ) - .await - .expect("create session"); - - session.send_and_wait("Say hello").await.expect("send"); - let input = recv_with_timeout(&mut rx, "userPromptSubmitted hook").await; - assert!(input.prompt.contains("Say hello")); - assert!(input.timestamp > 0); - assert!(!input.working_directory.as_os_str().is_empty()); - - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }, - ) - .await; -} +const UNSUPPORTED_SDK_HOOKS_MESSAGE: &str = "SDK hook callbacks are no longer supported"; #[tokio::test] -async fn should_invoke_onsessionend_hook_when_session_is_disconnected() { +async fn rejects_extended_sdk_callback_hooks() { with_e2e_context( "hooks_extended", - "should_invoke_onsessionend_hook_when_session_is_disconnected", + "rejects_extended_sdk_callback_hooks", |ctx| { Box::pin(async move { ctx.set_default_copilot_user(); - let (tx, mut rx) = mpsc::unbounded_channel(); let client = ctx.start_client().await; - let session = client + match client .create_session( ctx.approve_all_session_config() - .with_hooks(Arc::new(RecordingHooks::session_end(tx, None))), + .with_hooks(Arc::new(ExtendedHooks)), ) .await - .expect("create session"); - - session.send_and_wait("Say hi").await.expect("send"); - session.disconnect().await.expect("disconnect session"); - let input = recv_with_timeout(&mut rx, "sessionEnd hook").await; - assert!(input.timestamp > 0); - assert!(!input.working_directory.as_os_str().is_empty()); - - client.stop().await.expect("stop client"); - }) - }, - ) - .await; -} - -#[tokio::test] -async fn should_invoke_onerroroccurred_hook_when_error_occurs() { - with_e2e_context( - "hooks_extended", - "should_invoke_onerroroccurred_hook_when_error_occurs", - |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let (tx, mut rx) = mpsc::unbounded_channel(); - let client = ctx.start_client().await; - let session = client - .create_session( - ctx.approve_all_session_config() - .with_hooks(Arc::new(RecordingHooks::error(tx, None))), - ) - .await - .expect("create session"); - - session.send_and_wait("Say hi").await.expect("send"); - assert!(rx.try_recv().is_err()); - - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }, - ) - .await; -} - -#[tokio::test] -async fn should_invoke_userpromptsubmitted_hook_and_modify_prompt() { - with_e2e_context( - "hooks_extended", - "should_invoke_userpromptsubmitted_hook_and_modify_prompt", - |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let (tx, mut rx) = mpsc::unbounded_channel(); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config().with_hooks(Arc::new( - RecordingHooks::user_prompt( - tx, - Some(UserPromptSubmittedOutput { - modified_prompt: Some( - "Reply with exactly: HOOKED_PROMPT".to_string(), - ), - ..UserPromptSubmittedOutput::default() - }), - ), - ))) - .await - .expect("create session"); - - let answer = session - .send_and_wait("Say something else") - .await - .expect("send") - .expect("assistant message"); - let input = recv_with_timeout(&mut rx, "userPromptSubmitted hook").await; - assert!(input.prompt.contains("Say something else")); - assert!(assistant_message_content(&answer).contains("HOOKED_PROMPT")); - - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }, - ) - .await; -} - -#[tokio::test] -async fn should_invoke_sessionstart_hook() { - with_e2e_context("hooks_extended", "should_invoke_sessionstart_hook", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let (tx, mut rx) = mpsc::unbounded_channel(); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config().with_hooks(Arc::new( - RecordingHooks::session_start( - tx, - Some(SessionStartOutput { - additional_context: Some("Session start hook context.".to_string()), - ..SessionStartOutput::default() - }), - ), - ))) - .await - .expect("create session"); - - session.send_and_wait("Say hi").await.expect("send"); - let input = recv_with_timeout(&mut rx, "sessionStart hook").await; - assert_eq!(input.source, "new"); - - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) - .await; -} - -#[tokio::test] -async fn should_invoke_sessionend_hook() { - with_e2e_context("hooks_extended", "should_invoke_sessionend_hook", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let (tx, mut rx) = mpsc::unbounded_channel(); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config().with_hooks(Arc::new( - RecordingHooks::session_end( - tx, - Some(SessionEndOutput { - session_summary: Some("session ended".to_string()), - ..SessionEndOutput::default() - }), + { + Ok(session) => { + session.disconnect().await.expect("disconnect session"); + panic!("expected SDK callback hooks to be rejected"); + } + Err(err) => assert!( + err.to_string().contains(UNSUPPORTED_SDK_HOOKS_MESSAGE), + "expected unsupported hooks error, got: {err}" ), - ))) - .await - .expect("create session"); - - session.send_and_wait("Say bye").await.expect("send"); - session.disconnect().await.expect("disconnect session"); - let input = recv_with_timeout(&mut rx, "sessionEnd hook").await; - assert!(input.timestamp > 0); - - client.stop().await.expect("stop client"); - }) - }) - .await; -} - -#[tokio::test] -async fn should_register_erroroccurred_hook() { - with_e2e_context( - "hooks_extended", - "should_register_erroroccurred_hook", - |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let (tx, mut rx) = mpsc::unbounded_channel(); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config().with_hooks(Arc::new( - RecordingHooks::error( - tx, - Some(ErrorOccurredOutput { - error_handling: Some("skip".to_string()), - ..ErrorOccurredOutput::default() - }), - ), - ))) - .await - .expect("create session"); - - session.send_and_wait("Say hi").await.expect("send"); - assert!(rx.try_recv().is_err()); - - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }, - ) - .await; -} - -#[tokio::test] -async fn should_allow_pretooluse_to_return_modifiedargs_and_suppressoutput() { - with_e2e_context( - "hooks_extended", - "should_allow_pretooluse_to_return_modifiedargs_and_suppressoutput", - |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let (tx, mut rx) = mpsc::unbounded_channel(); - let client = ctx.start_client().await; - let session = client - .create_session( - SessionConfig::default() - .with_github_token(super::support::DEFAULT_TEST_TOKEN) - .with_permission_handler(Arc::new(ApproveAllHandler)) - .with_tools(vec![echo_value_tool()]) - .with_hooks(Arc::new(RecordingHooks::pre_tool(tx))), - ) - .await - .expect("create session"); - - let answer = session - .send_and_wait( - "Call echo_value with value 'original', then reply with the result.", - ) - .await - .expect("send") - .expect("assistant message"); - let mut saw_echo = false; - while let Ok(input) = rx.try_recv() { - saw_echo |= input.tool_name == "echo_value"; } - assert!(saw_echo, "expected preToolUse hook for echo_value"); - assert!(assistant_message_content(&answer).contains("modified by hook")); - - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }, - ) - .await; -} - -#[tokio::test] -async fn should_allow_posttooluse_to_return_modifiedresult() { - with_e2e_context( - "hooks_extended", - "should_allow_posttooluse_to_return_modifiedresult", - |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let (tx, mut rx) = mpsc::unbounded_channel(); - let client = ctx.start_client().await; - let session = client - .create_session( - ctx.approve_all_session_config() - .with_hooks(Arc::new(RecordingHooks::post_tool(tx))), - ) - .await - .expect("create session"); - - let answer = session - .send_and_wait( - "Call the view tool to read the current directory, then reply done.", - ) - .await - .expect("send") - .expect("assistant message"); - let mut saw_view = false; - while let Ok(input) = rx.try_recv() { - saw_view |= input.tool_name == "view"; - } - assert!(saw_view, "expected postToolUse hook for view"); - assert!( - assistant_message_content(&answer) - .to_lowercase() - .contains("done"), - "expected assistant message to contain 'done'" - ); - - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }, - ) - .await; -} - -#[tokio::test] -#[ignore = "Fails with 1.0.64-0 runtime: built-in tools are not available when hooks restrict availableTools, so the failure path cannot be exercised. Follow up with runtime team."] -async fn should_invoke_posttoolusefailure_hook_for_failed_tool_result() { - with_e2e_context( - "hooks_extended", - "should_invoke_posttoolusefailure_hook_for_failed_tool_result", - |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let (failure_tx, mut failure_rx) = mpsc::unbounded_channel(); - let (post_tx, mut post_rx) = mpsc::unbounded_channel(); - let client = ctx.start_client().await; - let session = client - .create_session( - ctx.approve_all_session_config() - .with_available_tools(["report_intent"]) - .with_hooks(Arc::new(RecordingHooks::post_tool_failure( - failure_tx, post_tx, - ))), - ) - .await - .expect("create session"); - - let answer = session - .send_and_wait( - "Call the view tool with path 'missing.txt'. If it fails, use the hook guidance to answer.", - ) - .await - .expect("send") - .expect("assistant message"); - - let input = recv_with_timeout(&mut failure_rx, "postToolUseFailure hook").await; - assert!(post_rx.try_recv().is_err()); - assert_eq!(input.tool_name, "view"); - assert!(input.error.contains("does not exist")); - assert!( - input.tool_args["path"] - .as_str() - .is_some_and(|path| path.contains("missing.txt")) - ); - assert!(input.timestamp > 0); - assert!(!input.working_directory.as_os_str().is_empty()); - assert!( - assistant_message_content(&answer).contains("HOOK_FAILURE_GUIDANCE_APPLIED") - ); - - session.disconnect().await.expect("disconnect session"); client.stop().await.expect("stop client"); }) }, @@ -419,232 +44,84 @@ async fn should_invoke_posttoolusefailure_hook_for_failed_tool_result() { .await; } -#[derive(Default)] -struct RecordingHooks { - session_start: Option>, - session_start_output: Option, - session_end: Option>, - session_end_output: Option, - user_prompt: Option>, - user_prompt_output: Option, - error: Option>, - error_output: Option, - pre_tool: Option>, - post_tool: Option>, - post_tool_failure: Option>, -} - -impl RecordingHooks { - fn session_start( - tx: mpsc::UnboundedSender, - output: Option, - ) -> Self { - Self { - session_start: Some(tx), - session_start_output: output, - ..Self::default() - } - } - - fn session_end( - tx: mpsc::UnboundedSender, - output: Option, - ) -> Self { - Self { - session_end: Some(tx), - session_end_output: output, - ..Self::default() - } - } - - fn user_prompt( - tx: mpsc::UnboundedSender, - output: Option, - ) -> Self { - Self { - user_prompt: Some(tx), - user_prompt_output: output, - ..Self::default() - } - } - - fn error( - tx: mpsc::UnboundedSender, - output: Option, - ) -> Self { - Self { - error: Some(tx), - error_output: output, - ..Self::default() - } - } +struct ExtendedHooks; - fn pre_tool(tx: mpsc::UnboundedSender) -> Self { - Self { - pre_tool: Some(tx), - ..Self::default() - } - } - - fn post_tool(tx: mpsc::UnboundedSender) -> Self { - Self { - post_tool: Some(tx), - ..Self::default() - } - } - - fn post_tool_failure( - failure_tx: mpsc::UnboundedSender, - post_tx: mpsc::UnboundedSender, - ) -> Self { - Self { - post_tool: Some(post_tx), - post_tool_failure: Some(failure_tx), - ..Self::default() - } +#[async_trait] +impl SessionHooks for ExtendedHooks { + async fn on_user_prompt_submitted( + &self, + _input: UserPromptSubmittedInput, + _ctx: HookContext, + ) -> Option { + Some(UserPromptSubmittedOutput { + modified_prompt: Some("not used".to_string()), + ..UserPromptSubmittedOutput::default() + }) } -} -#[async_trait] -impl SessionHooks for RecordingHooks { async fn on_session_start( &self, - input: SessionStartInput, - ctx: HookContext, + _input: SessionStartInput, + _ctx: HookContext, ) -> Option { - assert!(!ctx.session_id.as_str().is_empty()); - if let Some(tx) = &self.session_start { - let _ = tx.send(input); - } - self.session_start_output.clone() + Some(SessionStartOutput { + additional_context: Some("not used".to_string()), + ..SessionStartOutput::default() + }) } async fn on_session_end( &self, - input: SessionEndInput, - ctx: HookContext, + _input: SessionEndInput, + _ctx: HookContext, ) -> Option { - assert!(!ctx.session_id.as_str().is_empty()); - if let Some(tx) = &self.session_end { - let _ = tx.send(input); - } - self.session_end_output.clone() - } - - async fn on_user_prompt_submitted( - &self, - input: UserPromptSubmittedInput, - ctx: HookContext, - ) -> Option { - assert!(!ctx.session_id.as_str().is_empty()); - if let Some(tx) = &self.user_prompt { - let _ = tx.send(input); - } - self.user_prompt_output.clone() + Some(SessionEndOutput { + session_summary: Some("not used".to_string()), + ..SessionEndOutput::default() + }) } async fn on_error_occurred( &self, - input: ErrorOccurredInput, - ctx: HookContext, + _input: ErrorOccurredInput, + _ctx: HookContext, ) -> Option { - assert!(!ctx.session_id.as_str().is_empty()); - assert!( - ["model_call", "tool_execution", "system", "user_input"] - .contains(&input.error_context.as_str()) - ); - if let Some(tx) = &self.error { - let _ = tx.send(input); - } - self.error_output.clone() + Some(ErrorOccurredOutput { + error_handling: Some("skip".to_string()), + ..ErrorOccurredOutput::default() + }) } async fn on_pre_tool_use( &self, - input: PreToolUseInput, + _input: PreToolUseInput, _ctx: HookContext, ) -> Option { - let output = if input.tool_name == "echo_value" { - PreToolUseOutput { - permission_decision: Some("allow".to_string()), - modified_args: Some(json!({ "value": "modified by hook" })), - suppress_output: Some(false), - ..PreToolUseOutput::default() - } - } else { - PreToolUseOutput { - permission_decision: Some("allow".to_string()), - ..PreToolUseOutput::default() - } - }; - if let Some(tx) = &self.pre_tool { - let _ = tx.send(input); - } - Some(output) + Some(PreToolUseOutput { + permission_decision: Some("allow".to_string()), + ..PreToolUseOutput::default() + }) } async fn on_post_tool_use( &self, - input: PostToolUseInput, + _input: PostToolUseInput, _ctx: HookContext, ) -> Option { - let output = - (self.post_tool.is_some() && input.tool_name == "view").then(|| PostToolUseOutput { - modified_result: Some(json!({ - "textResultForLlm": "modified by post hook", - "resultType": "success", - "toolTelemetry": {}, - })), - suppress_output: Some(false), - ..PostToolUseOutput::default() - }); - if let Some(tx) = &self.post_tool { - let _ = tx.send(input); - } - output + Some(PostToolUseOutput { + suppress_output: Some(false), + ..PostToolUseOutput::default() + }) } async fn on_post_tool_use_failure( &self, - input: PostToolUseFailureInput, - ctx: HookContext, + _input: PostToolUseFailureInput, + _ctx: HookContext, ) -> Option { - assert!(!ctx.session_id.as_str().is_empty()); - if let Some(tx) = &self.post_tool_failure { - let _ = tx.send(input); - return Some(PostToolUseFailureOutput { - additional_context: Some("HOOK_FAILURE_GUIDANCE_APPLIED".to_string()), - }); - } - None - } -} - -struct EchoValueTool; - -fn echo_value_tool() -> Tool { - Tool::new("echo_value") - .with_description("Echoes the supplied value") - .with_parameters(json!({ - "type": "object", - "properties": { - "value": { "type": "string" } - }, - "required": ["value"] - })) - .with_handler(Arc::new(EchoValueTool)) -} - -#[async_trait] -impl ToolHandler for EchoValueTool { - async fn call(&self, invocation: ToolInvocation) -> Result { - Ok(ToolResult::Text( - invocation - .arguments - .get("value") - .and_then(serde_json::Value::as_str) - .unwrap_or_default() - .to_string(), - )) + Some(PostToolUseFailureOutput { + additional_context: Some("not used".to_string()), + ..PostToolUseFailureOutput::default() + }) } } diff --git a/rust/tests/e2e/pre_mcp_tool_call_hook.rs b/rust/tests/e2e/pre_mcp_tool_call_hook.rs index 973672f709..c83f43bf5f 100644 --- a/rust/tests/e2e/pre_mcp_tool_call_hook.rs +++ b/rust/tests/e2e/pre_mcp_tool_call_hook.rs @@ -1,135 +1,40 @@ -use std::collections::HashMap; use std::sync::Arc; use async_trait::async_trait; use github_copilot_sdk::hooks::{ HookContext, PreMcpToolCallInput, PreMcpToolCallOutput, SessionHooks, }; -use github_copilot_sdk::{McpServerConfig, McpStdioServerConfig}; -use serde_json::{Value, json}; -use tokio::sync::mpsc; +use serde_json::json; -use super::support::{assistant_message_content, recv_with_timeout, with_e2e_context}; +use super::support::with_e2e_context; -fn meta_echo_mcp_servers(repo_root: &std::path::Path) -> HashMap { - let harness_dir = repo_root.join("test").join("harness"); - let server_path = harness_dir - .join("test-mcp-meta-echo-server.mjs") - .to_string_lossy() - .to_string(); - HashMap::from([( - "meta-echo".to_string(), - McpServerConfig::Stdio(McpStdioServerConfig { - tools: Some(vec!["*".to_string()]), - command: if cfg!(windows) { - "node.exe".to_string() - } else { - "node".to_string() - }, - args: vec![server_path], - working_directory: Some(harness_dir.to_string_lossy().to_string()), - ..McpStdioServerConfig::default() - }), - )]) -} - -struct SetMetaHooks { - tx: mpsc::UnboundedSender, -} - -#[async_trait] -impl SessionHooks for SetMetaHooks { - async fn on_pre_mcp_tool_call( - &self, - input: PreMcpToolCallInput, - _ctx: HookContext, - ) -> Option { - let _ = self.tx.send(input); - Some(PreMcpToolCallOutput { - meta_to_use: Some(json!({"injected": "by-hook", "source": "test"})), - }) - } -} - -struct ReplaceMetaHooks { - tx: mpsc::UnboundedSender, -} - -#[async_trait] -impl SessionHooks for ReplaceMetaHooks { - async fn on_pre_mcp_tool_call( - &self, - input: PreMcpToolCallInput, - _ctx: HookContext, - ) -> Option { - let _ = self.tx.send(input); - Some(PreMcpToolCallOutput { - meta_to_use: Some(json!({"completely": "replaced"})), - }) - } -} - -struct RemoveMetaHooks { - tx: mpsc::UnboundedSender, -} - -#[async_trait] -impl SessionHooks for RemoveMetaHooks { - async fn on_pre_mcp_tool_call( - &self, - input: PreMcpToolCallInput, - _ctx: HookContext, - ) -> Option { - let _ = self.tx.send(input); - Some(PreMcpToolCallOutput { - meta_to_use: Some(Value::Null), - }) - } -} +const UNSUPPORTED_SDK_HOOKS_MESSAGE: &str = "SDK hook callbacks are no longer supported"; #[tokio::test] -async fn should_set_meta_via_premcptoolcall_hook() { +async fn rejects_sdk_premcptoolcall_callback_hooks() { with_e2e_context( "pre_mcp_tool_call_hook", - "should_set_meta_via_premcptoolcall_hook", + "rejects_sdk_premcptoolcall_callback_hooks", |ctx| { Box::pin(async move { ctx.set_default_copilot_user(); - let (tx, mut rx) = mpsc::unbounded_channel(); let client = ctx.start_client().await; - let session = client + match client .create_session( ctx.approve_all_session_config() - .with_mcp_servers(meta_echo_mcp_servers(ctx.repo_root())) - .with_hooks(Arc::new(SetMetaHooks { tx })), - ) - .await - .expect("create session"); - - let answer = session - .send_and_wait( - "Use the meta-echo/echo_meta tool with value 'test-set'. Reply with just the raw tool result.", + .with_hooks(Arc::new(PreMcpHooks)), ) .await - .expect("send") - .expect("assistant message"); - let content = assistant_message_content(&answer); - assert!( - content.contains("injected"), - "Expected 'injected' in response, got: {content}" - ); - assert!( - content.contains("by-hook"), - "Expected 'by-hook' in response, got: {content}" - ); - - let input = recv_with_timeout(&mut rx, "preMcpToolCall hook").await; - assert_eq!(input.server_name, "meta-echo"); - assert_eq!(input.tool_name, "echo_meta"); - assert!(!input.working_directory.as_os_str().is_empty()); - assert!(input.timestamp > 0); - - session.disconnect().await.expect("disconnect session"); + { + Ok(session) => { + session.disconnect().await.expect("disconnect session"); + panic!("expected SDK callback hooks to be rejected"); + } + Err(err) => assert!( + err.to_string().contains(UNSUPPORTED_SDK_HOOKS_MESSAGE), + "expected unsupported hooks error, got: {err}" + ), + } client.stop().await.expect("stop client"); }) }, @@ -137,98 +42,17 @@ async fn should_set_meta_via_premcptoolcall_hook() { .await; } -#[tokio::test] -async fn should_replace_meta_via_premcptoolcall_hook() { - with_e2e_context( - "pre_mcp_tool_call_hook", - "should_replace_meta_via_premcptoolcall_hook", - |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let (tx, mut rx) = mpsc::unbounded_channel(); - let client = ctx.start_client().await; - let session = client - .create_session( - ctx.approve_all_session_config() - .with_mcp_servers(meta_echo_mcp_servers(ctx.repo_root())) - .with_hooks(Arc::new(ReplaceMetaHooks { tx })), - ) - .await - .expect("create session"); - - let answer = session - .send_and_wait( - "Use the meta-echo/echo_meta tool with value 'test-replace'. Reply with just the raw tool result.", - ) - .await - .expect("send") - .expect("assistant message"); - let content = assistant_message_content(&answer); - assert!( - content.contains("completely"), - "Expected 'completely' in response, got: {content}" - ); - assert!( - content.contains("replaced"), - "Expected 'replaced' in response, got: {content}" - ); - - let input = recv_with_timeout(&mut rx, "preMcpToolCall hook").await; - assert_eq!(input.server_name, "meta-echo"); - assert_eq!(input.tool_name, "echo_meta"); - - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }, - ) - .await; -} - -#[tokio::test] -async fn should_remove_meta_via_premcptoolcall_hook() { - with_e2e_context( - "pre_mcp_tool_call_hook", - "should_remove_meta_via_premcptoolcall_hook", - |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let (tx, mut rx) = mpsc::unbounded_channel(); - let client = ctx.start_client().await; - let session = client - .create_session( - ctx.approve_all_session_config() - .with_mcp_servers(meta_echo_mcp_servers(ctx.repo_root())) - .with_hooks(Arc::new(RemoveMetaHooks { tx })), - ) - .await - .expect("create session"); +struct PreMcpHooks; - let answer = session - .send_and_wait( - "Use the meta-echo/echo_meta tool with value 'test-remove'. Reply with just the raw tool result.", - ) - .await - .expect("send") - .expect("assistant message"); - let content = assistant_message_content(&answer); - assert!( - content.contains("\"meta\":null"), - "Expected '\"meta\":null' in response, got: {content}" - ); - assert!( - content.contains("test-remove"), - "Expected 'test-remove' in response, got: {content}" - ); - - let input = recv_with_timeout(&mut rx, "preMcpToolCall hook").await; - assert_eq!(input.server_name, "meta-echo"); - assert_eq!(input.tool_name, "echo_meta"); - - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }, - ) - .await; +#[async_trait] +impl SessionHooks for PreMcpHooks { + async fn on_pre_mcp_tool_call( + &self, + _input: PreMcpToolCallInput, + _ctx: HookContext, + ) -> Option { + Some(PreMcpToolCallOutput { + meta_to_use: Some(json!({"injected": "by-hook"})), + }) + } } diff --git a/rust/tests/e2e/subagent_hooks.rs b/rust/tests/e2e/subagent_hooks.rs index 99529c433b..7978b5cd6f 100644 --- a/rust/tests/e2e/subagent_hooks.rs +++ b/rust/tests/e2e/subagent_hooks.rs @@ -5,91 +5,36 @@ use github_copilot_sdk::hooks::{ HookContext, PostToolUseInput, PostToolUseOutput, PreToolUseInput, PreToolUseOutput, SessionHooks, }; -use parking_lot::Mutex; use super::support::with_e2e_context; +const UNSUPPORTED_SDK_HOOKS_MESSAGE: &str = "SDK hook callbacks are no longer supported"; + #[tokio::test] -async fn should_invoke_pretooluse_and_posttooluse_hooks_for_sub_agent_tool_calls() { +async fn rejects_sdk_callback_hooks_for_sub_agent_hook_propagation() { with_e2e_context( "subagent_hooks", - "should_invoke_pretooluse_and_posttooluse_hooks_for_sub_agent_tool_calls", + "rejects_sdk_callback_hooks_for_sub_agent_hook_propagation", |ctx| { Box::pin(async move { ctx.set_default_copilot_user(); - std::fs::write( - ctx.work_dir().join("subagent-test.txt"), - "Hello from subagent test!", - ) - .expect("write test file"); - - let hook_log = Arc::new(Mutex::new(Vec::::new())); - - let mut opts = ctx.client_options(); - opts.env.push(( - "COPILOT_EXP_COPILOT_CLI_SESSION_BASED_SUBAGENTS".into(), - "true".into(), - )); - - let client = github_copilot_sdk::Client::start(opts) - .await - .expect("start client"); - - let session = client - .create_session(ctx.approve_all_session_config().with_hooks(Arc::new( - RecordingHooks { - log: Arc::clone(&hook_log), - }, - ))) - .await - .expect("create session"); - - session - .send_and_wait( - "Use the task tool to spawn an explore agent that reads the file \ - subagent-test.txt in the current directory and reports its contents. \ - You must use the task tool.", + let client = ctx.start_client().await; + match client + .create_session( + ctx.approve_all_session_config() + .with_hooks(Arc::new(SubagentHooks)), ) .await - .expect("send"); - - let log = hook_log.lock().clone(); - - // Parent tool hooks fire for "task" - let task_pre = log - .iter() - .find(|h| h.kind == "pre" && h.tool_name == "task"); - assert!( - task_pre.is_some(), - "preToolUse should fire for the parent's 'task' tool call" - ); - - // Sub-agent tool hooks fire for "view" - let view_pre: Vec<_> = log - .iter() - .filter(|h| h.kind == "pre" && h.tool_name == "view") - .collect(); - let view_post: Vec<_> = log - .iter() - .filter(|h| h.kind == "post" && h.tool_name == "view") - .collect(); - assert!( - !view_pre.is_empty(), - "preToolUse should fire for the sub-agent's 'view' tool call" - ); - assert!( - !view_post.is_empty(), - "postToolUse should fire for the sub-agent's 'view' tool call" - ); - - // input.session_id distinguishes parent from sub-agent - assert_ne!( - view_pre[0].session_id, - task_pre.unwrap().session_id, - "Sub-agent tool hooks should have a different sessionId than parent tool hooks" - ); - - session.disconnect().await.expect("disconnect session"); + { + Ok(session) => { + session.disconnect().await.expect("disconnect session"); + panic!("expected SDK callback hooks to be rejected"); + } + Err(err) => assert!( + err.to_string().contains(UNSUPPORTED_SDK_HOOKS_MESSAGE), + "expected unsupported hooks error, got: {err}" + ), + } client.stop().await.expect("stop client"); }) }, @@ -97,29 +42,15 @@ async fn should_invoke_pretooluse_and_posttooluse_hooks_for_sub_agent_tool_calls .await; } -#[derive(Clone, Debug)] -struct HookEntry { - kind: String, - tool_name: String, - session_id: String, -} - -struct RecordingHooks { - log: Arc>>, -} +struct SubagentHooks; #[async_trait] -impl SessionHooks for RecordingHooks { +impl SessionHooks for SubagentHooks { async fn on_pre_tool_use( &self, - input: PreToolUseInput, + _input: PreToolUseInput, _ctx: HookContext, ) -> Option { - self.log.lock().push(HookEntry { - kind: "pre".to_string(), - tool_name: input.tool_name, - session_id: input.session_id, - }); Some(PreToolUseOutput { permission_decision: Some("allow".to_string()), ..PreToolUseOutput::default() @@ -128,14 +59,9 @@ impl SessionHooks for RecordingHooks { async fn on_post_tool_use( &self, - input: PostToolUseInput, + _input: PostToolUseInput, _ctx: HookContext, ) -> Option { - self.log.lock().push(HookEntry { - kind: "post".to_string(), - tool_name: input.tool_name, - session_id: input.session_id, - }); None } } diff --git a/scripts/codegen/csharp.ts b/scripts/codegen/csharp.ts index e1ceea5b11..9f760a0461 100644 --- a/scripts/codegen/csharp.ts +++ b/scripts/codegen/csharp.ts @@ -493,7 +493,11 @@ const COPYRIGHT = `/*----------------------------------------------------------- const EXPERIMENTAL_ATTRIBUTE = "[Experimental(Diagnostics.Experimental)]"; const EDITOR_BROWSABLE_NEVER_ATTRIBUTE = "[EditorBrowsable(EditorBrowsableState.Never)]"; -const OBSOLETE_ATTRIBUTE = `[Obsolete("This member is deprecated and will be removed in a future version.")]`; +const OBSOLETE_ATTRIBUTE = `#if NET5_0_OR_GREATER +[Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] +#else +[Obsolete("This member is deprecated and will be removed in a future version.")] +#endif`; const STRING_ENUM_RESERVED_MEMBER_NAMES = new Set(["Value", "Equals", "GetHashCode", "ToString", "Converter"]); function experimentalAttribute(indent = ""): string { @@ -507,7 +511,7 @@ function pushExperimentalAttribute(lines: string[], indent = ""): void { function obsoleteAttributes(indent = ""): string[] { return [ `${indent}${EDITOR_BROWSABLE_NEVER_ATTRIBUTE}`, - `${indent}${OBSOLETE_ATTRIBUTE}`, + ...OBSOLETE_ATTRIBUTE.split("\n").map((line) => line.startsWith("#") ? line : `${indent}${line}`), ]; } diff --git a/scripts/codegen/rust.ts b/scripts/codegen/rust.ts index b8a1c6d6db..3a3ce39a2f 100644 --- a/scripts/codegen/rust.ts +++ b/scripts/codegen/rust.ts @@ -1229,6 +1229,8 @@ export function generateSessionEventsCode(schema: JSONSchema7): string { "//! Auto-generated from session-events.schema.json — do not edit manually.", ); out.push(""); + out.push("#![allow(deprecated)]"); + out.push(""); out.push("use std::collections::HashMap;"); out.push(""); out.push("use serde::{Deserialize, Serialize};"); @@ -1580,6 +1582,7 @@ function generateApiTypesCode( out.push("//! Auto-generated from api.schema.json — do not edit manually."); out.push(""); out.push("#![allow(clippy::large_enum_variant)]"); + out.push("#![allow(deprecated)]"); out.push("#![allow(dead_code)]"); out.push("#![allow(rustdoc::invalid_html_tags)]"); out.push(""); @@ -2002,6 +2005,7 @@ function generateRpcCode(apiSchema: ApiSchema): string { out.push(""); out.push("#![allow(missing_docs)]"); out.push("#![allow(clippy::too_many_arguments)]"); + out.push("#![allow(deprecated)]"); out.push("#![allow(dead_code)]"); out.push(""); out.push("use super::api_types::{rpc_methods, *};"); From 3ea68c88f28f9225bb649cd717a8014136e210ba Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 29 Jun 2026 15:40:19 -0400 Subject: [PATCH 3/5] Fix Java hook test formatting Apply Spotless formatting expected by CI. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- java/src/test/java/com/github/copilot/HooksTest.java | 4 ++-- .../test/java/com/github/copilot/PreMcpToolCallHookTest.java | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/java/src/test/java/com/github/copilot/HooksTest.java b/java/src/test/java/com/github/copilot/HooksTest.java index c47e8d84f8..8e3a35be30 100644 --- a/java/src/test/java/com/github/copilot/HooksTest.java +++ b/java/src/test/java/com/github/copilot/HooksTest.java @@ -52,8 +52,8 @@ void testRejectsSdkCallbackHooks() throws Exception { new SessionHooks().setOnPreToolUse( (input, invocation) -> CompletableFuture.completedFuture(PreToolUseHookOutput.deny())), new SessionHooks() - .setOnPreToolUse((input, invocation) -> CompletableFuture - .completedFuture(PreToolUseHookOutput.allow())) + .setOnPreToolUse( + (input, invocation) -> CompletableFuture.completedFuture(PreToolUseHookOutput.allow())) .setOnPostToolUse((input, invocation) -> CompletableFuture .completedFuture((PostToolUseHookOutput) null))); diff --git a/java/src/test/java/com/github/copilot/PreMcpToolCallHookTest.java b/java/src/test/java/com/github/copilot/PreMcpToolCallHookTest.java index 1da88a82ae..4104d1d226 100644 --- a/java/src/test/java/com/github/copilot/PreMcpToolCallHookTest.java +++ b/java/src/test/java/com/github/copilot/PreMcpToolCallHookTest.java @@ -19,7 +19,9 @@ import com.github.copilot.rpc.SessionConfig; import com.github.copilot.rpc.SessionHooks; -/** Tests for SDK preMcpToolCall callback hook behavior with the native runtime. */ +/** + * Tests for SDK preMcpToolCall callback hook behavior with the native runtime. + */ public class PreMcpToolCallHookTest { private static final String UNSUPPORTED_SDK_HOOKS_MESSAGE = "SDK hook callbacks are no longer supported"; From abde94e09e23e455d6ccd985a8b24a471ba593ba Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 29 Jun 2026 15:51:38 -0400 Subject: [PATCH 4/5] Address Rust hook CodeQL comments Move unsupported hook error checking through a shared E2E helper so the error bindings are used outside assertion macro expansion. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- rust/tests/e2e/hooks.rs | 9 ++------- rust/tests/e2e/hooks_extended.rs | 9 ++------- rust/tests/e2e/pre_mcp_tool_call_hook.rs | 9 ++------- rust/tests/e2e/subagent_hooks.rs | 9 ++------- rust/tests/e2e/support.rs | 10 ++++++++++ 5 files changed, 18 insertions(+), 28 deletions(-) diff --git a/rust/tests/e2e/hooks.rs b/rust/tests/e2e/hooks.rs index 08b67d3a11..c4487ab6d8 100644 --- a/rust/tests/e2e/hooks.rs +++ b/rust/tests/e2e/hooks.rs @@ -6,9 +6,7 @@ use github_copilot_sdk::hooks::{ SessionHooks, }; -use super::support::with_e2e_context; - -const UNSUPPORTED_SDK_HOOKS_MESSAGE: &str = "SDK hook callbacks are no longer supported"; +use super::support::{assert_unsupported_hooks_error, with_e2e_context}; #[tokio::test] async fn rejects_sdk_callback_hooks() { @@ -37,10 +35,7 @@ async fn assert_unsupported_hooks( session.disconnect().await.expect("disconnect session"); panic!("expected SDK callback hooks to be rejected"); } - Err(err) => assert!( - err.to_string().contains(UNSUPPORTED_SDK_HOOKS_MESSAGE), - "expected unsupported hooks error, got: {err}" - ), + Err(err) => assert_unsupported_hooks_error(err), } } diff --git a/rust/tests/e2e/hooks_extended.rs b/rust/tests/e2e/hooks_extended.rs index eeda2db3fd..27d4c4b8ca 100644 --- a/rust/tests/e2e/hooks_extended.rs +++ b/rust/tests/e2e/hooks_extended.rs @@ -8,9 +8,7 @@ use github_copilot_sdk::hooks::{ SessionStartOutput, UserPromptSubmittedInput, UserPromptSubmittedOutput, }; -use super::support::with_e2e_context; - -const UNSUPPORTED_SDK_HOOKS_MESSAGE: &str = "SDK hook callbacks are no longer supported"; +use super::support::{assert_unsupported_hooks_error, with_e2e_context}; #[tokio::test] async fn rejects_extended_sdk_callback_hooks() { @@ -32,10 +30,7 @@ async fn rejects_extended_sdk_callback_hooks() { session.disconnect().await.expect("disconnect session"); panic!("expected SDK callback hooks to be rejected"); } - Err(err) => assert!( - err.to_string().contains(UNSUPPORTED_SDK_HOOKS_MESSAGE), - "expected unsupported hooks error, got: {err}" - ), + Err(err) => assert_unsupported_hooks_error(err), } client.stop().await.expect("stop client"); }) diff --git a/rust/tests/e2e/pre_mcp_tool_call_hook.rs b/rust/tests/e2e/pre_mcp_tool_call_hook.rs index c83f43bf5f..219032bd5e 100644 --- a/rust/tests/e2e/pre_mcp_tool_call_hook.rs +++ b/rust/tests/e2e/pre_mcp_tool_call_hook.rs @@ -6,9 +6,7 @@ use github_copilot_sdk::hooks::{ }; use serde_json::json; -use super::support::with_e2e_context; - -const UNSUPPORTED_SDK_HOOKS_MESSAGE: &str = "SDK hook callbacks are no longer supported"; +use super::support::{assert_unsupported_hooks_error, with_e2e_context}; #[tokio::test] async fn rejects_sdk_premcptoolcall_callback_hooks() { @@ -30,10 +28,7 @@ async fn rejects_sdk_premcptoolcall_callback_hooks() { session.disconnect().await.expect("disconnect session"); panic!("expected SDK callback hooks to be rejected"); } - Err(err) => assert!( - err.to_string().contains(UNSUPPORTED_SDK_HOOKS_MESSAGE), - "expected unsupported hooks error, got: {err}" - ), + Err(err) => assert_unsupported_hooks_error(err), } client.stop().await.expect("stop client"); }) diff --git a/rust/tests/e2e/subagent_hooks.rs b/rust/tests/e2e/subagent_hooks.rs index 7978b5cd6f..0329cadf0c 100644 --- a/rust/tests/e2e/subagent_hooks.rs +++ b/rust/tests/e2e/subagent_hooks.rs @@ -6,9 +6,7 @@ use github_copilot_sdk::hooks::{ SessionHooks, }; -use super::support::with_e2e_context; - -const UNSUPPORTED_SDK_HOOKS_MESSAGE: &str = "SDK hook callbacks are no longer supported"; +use super::support::{assert_unsupported_hooks_error, with_e2e_context}; #[tokio::test] async fn rejects_sdk_callback_hooks_for_sub_agent_hook_propagation() { @@ -30,10 +28,7 @@ async fn rejects_sdk_callback_hooks_for_sub_agent_hook_propagation() { session.disconnect().await.expect("disconnect session"); panic!("expected SDK callback hooks to be rejected"); } - Err(err) => assert!( - err.to_string().contains(UNSUPPORTED_SDK_HOOKS_MESSAGE), - "expected unsupported hooks error, got: {err}" - ), + Err(err) => assert_unsupported_hooks_error(err), } client.stop().await.expect("stop client"); }) diff --git a/rust/tests/e2e/support.rs b/rust/tests/e2e/support.rs index 1805eb145b..b5be05b56c 100644 --- a/rust/tests/e2e/support.rs +++ b/rust/tests/e2e/support.rs @@ -21,9 +21,19 @@ use tokio::sync::Semaphore; static E2E_CONCURRENCY: LazyLock = LazyLock::new(|| Semaphore::new(e2e_concurrency())); pub const DEFAULT_TEST_TOKEN: &str = "rust-e2e-token"; +const UNSUPPORTED_SDK_HOOKS_MESSAGE: &str = "SDK hook callbacks are no longer supported"; type TestFuture<'a> = Pin + 'a>>; +pub fn assert_unsupported_hooks_error(err: impl std::fmt::Display) { + let message = err.to_string(); + if message.contains(UNSUPPORTED_SDK_HOOKS_MESSAGE) { + return; + } + + panic!("expected unsupported hooks error, got: {message}"); +} + pub async fn with_e2e_context(category: &str, snapshot_name: &str, test: F) where F: for<'a> FnOnce(&'a mut E2eContext) -> TestFuture<'a>, From 59c69653dae912799c822f6991f3cbb4573eed02 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 29 Jun 2026 15:59:40 -0400 Subject: [PATCH 5/5] Fix remaining callback hook CI failures Avoid runtime-backed callback hook paths in unit coverage and keep legacy dispatcher tests direct. Suppress obsolete attributes only on targets that support the custom diagnostic id so net472 builds do not fail. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- dotnet/src/Generated/Rpc.cs | 8 -- dotnet/src/Generated/SessionEvents.cs | 12 --- .../github/copilot/ExecutorWiringTest.java | 44 ---------- nodejs/test/client.test.ts | 81 +++++++------------ rust/tests/e2e/hooks_extended.rs | 1 - scripts/codegen/csharp.ts | 2 - 6 files changed, 30 insertions(+), 118 deletions(-) diff --git a/dotnet/src/Generated/Rpc.cs b/dotnet/src/Generated/Rpc.cs index b5fe00305f..e5d830ada5 100644 --- a/dotnet/src/Generated/Rpc.cs +++ b/dotnet/src/Generated/Rpc.cs @@ -78,8 +78,6 @@ public sealed class ModelBillingTokenPricesLongContext [EditorBrowsable(EditorBrowsableState.Never)] #if NET5_0_OR_GREATER [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] -#else - [Obsolete("This member is deprecated and will be removed in a future version.")] #endif [JsonPropertyName("cachePrice")] public double? CachePrice { get; set; } @@ -96,8 +94,6 @@ public sealed class ModelBillingTokenPricesLongContext [EditorBrowsable(EditorBrowsableState.Never)] #if NET5_0_OR_GREATER [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] -#else - [Obsolete("This member is deprecated and will be removed in a future version.")] #endif [JsonPropertyName("contextMax")] public long? ContextMax { get; set; } @@ -127,8 +123,6 @@ public sealed class ModelBillingTokenPrices [EditorBrowsable(EditorBrowsableState.Never)] #if NET5_0_OR_GREATER [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] -#else - [Obsolete("This member is deprecated and will be removed in a future version.")] #endif [JsonPropertyName("cachePrice")] public double? CachePrice { get; set; } @@ -145,8 +139,6 @@ public sealed class ModelBillingTokenPrices [EditorBrowsable(EditorBrowsableState.Never)] #if NET5_0_OR_GREATER [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] -#else - [Obsolete("This member is deprecated and will be removed in a future version.")] #endif [JsonPropertyName("contextMax")] public long? ContextMax { get; set; } diff --git a/dotnet/src/Generated/SessionEvents.cs b/dotnet/src/Generated/SessionEvents.cs index 234785d0de..a125ec003f 100644 --- a/dotnet/src/Generated/SessionEvents.cs +++ b/dotnet/src/Generated/SessionEvents.cs @@ -2421,8 +2421,6 @@ public sealed partial class AssistantMessageData [EditorBrowsable(EditorBrowsableState.Never)] #if NET5_0_OR_GREATER [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] -#else - [Obsolete("This member is deprecated and will be removed in a future version.")] #endif [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("parentToolCallId")] @@ -2497,8 +2495,6 @@ public sealed partial class AssistantMessageDeltaData [EditorBrowsable(EditorBrowsableState.Never)] #if NET5_0_OR_GREATER [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] -#else - [Obsolete("This member is deprecated and will be removed in a future version.")] #endif [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("parentToolCallId")] @@ -2601,8 +2597,6 @@ public sealed partial class AssistantUsageData [EditorBrowsable(EditorBrowsableState.Never)] #if NET5_0_OR_GREATER [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] -#else - [Obsolete("This member is deprecated and will be removed in a future version.")] #endif [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("parentToolCallId")] @@ -2773,8 +2767,6 @@ public sealed partial class ToolExecutionStartData [EditorBrowsable(EditorBrowsableState.Never)] #if NET5_0_OR_GREATER [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] -#else - [Obsolete("This member is deprecated and will be removed in a future version.")] #endif [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("parentToolCallId")] @@ -2855,8 +2847,6 @@ public sealed partial class ToolExecutionCompleteData [EditorBrowsable(EditorBrowsableState.Never)] #if NET5_0_OR_GREATER [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] -#else - [Obsolete("This member is deprecated and will be removed in a future version.")] #endif [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("parentToolCallId")] @@ -5382,8 +5372,6 @@ public sealed partial class ToolExecutionCompleteContentText : ToolExecutionComp [EditorBrowsable(EditorBrowsableState.Never)] #if NET5_0_OR_GREATER [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] -#else -[Obsolete("This member is deprecated and will be removed in a future version.")] #endif public sealed partial class ToolExecutionCompleteContentTerminal : ToolExecutionCompleteContent { diff --git a/java/src/test/java/com/github/copilot/ExecutorWiringTest.java b/java/src/test/java/com/github/copilot/ExecutorWiringTest.java index 78764db0fb..63bba3884f 100644 --- a/java/src/test/java/com/github/copilot/ExecutorWiringTest.java +++ b/java/src/test/java/com/github/copilot/ExecutorWiringTest.java @@ -27,9 +27,7 @@ import com.github.copilot.rpc.PermissionHandler; import com.github.copilot.rpc.PermissionRequestResult; import com.github.copilot.rpc.PermissionRequestResultKind; -import com.github.copilot.rpc.PreToolUseHookOutput; import com.github.copilot.rpc.SessionConfig; -import com.github.copilot.rpc.SessionHooks; import com.github.copilot.rpc.ToolDefinition; import com.github.copilot.rpc.UserInputResponse; @@ -265,48 +263,6 @@ void testUserInputDispatchUsesProvidedExecutor() throws Exception { } } - /** - * Verifies that hooks dispatch routes through the provided executor. - * - *

- * When the LLM triggers a hook, the {@code RpcHandlerDispatcher} calls - * {@code CompletableFuture.runAsync(...)} to dispatch the hooks handler. This - * test asserts that dispatch goes through the caller-supplied executor. - *

- * - * @see Snapshot: hooks/invoke_pre_tool_use_hook_when_model_runs_a_tool - */ - @Test - void testHooksDispatchUsesProvidedExecutor() throws Exception { - ctx.configureForTest("hooks", "invoke_pre_tool_use_hook_when_model_runs_a_tool"); - - TrackingExecutor trackingExecutor = new TrackingExecutor(ForkJoinPool.commonPool()); - - var config = new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL) - .setHooks(new SessionHooks().setOnPreToolUse( - (input, invocation) -> CompletableFuture.completedFuture(PreToolUseHookOutput.allow()))); - - try (CopilotClient client = new CopilotClient(createOptionsWithExecutor(trackingExecutor))) { - CopilotSession session = client.createSession(config).get(); - - Path testFile = ctx.getWorkDir().resolve("hello.txt"); - Files.writeString(testFile, "Hello from the test!"); - - int beforeSend = trackingExecutor.getTaskCount(); - - session.sendAndWait( - new MessageOptions().setPrompt("Read the contents of hello.txt and tell me what it says")) - .get(60, TimeUnit.SECONDS); - - assertTrue(trackingExecutor.getTaskCount() > beforeSend, - "Expected the tracking executor to have been invoked for hooks dispatch, " - + "but task count did not increase after sendAndWait. " - + "RpcHandlerDispatcher is not routing hooks runAsync through the provided executor."); - - session.close(); - } - } - /** * Verifies that {@code CopilotClient.stop()} routes session closure through the * provided executor. diff --git a/nodejs/test/client.test.ts b/nodejs/test/client.test.ts index 96d7da30cf..9612e4ca28 100644 --- a/nodejs/test/client.test.ts +++ b/nodejs/test/client.test.ts @@ -10,7 +10,7 @@ import { type ModelInfo, } from "../src/index.js"; import { CopilotSession } from "../src/session.js"; -import { defaultJoinSessionPermissionHandler } from "../src/types.js"; +import { defaultJoinSessionPermissionHandler, type SessionHooks } from "../src/types.js"; // This file is for unit tests. Where relevant, prefer to add e2e tests in e2e/*.test.ts instead @@ -2427,19 +2427,18 @@ describe("CopilotClient", () => { // corresponding SessionHooks handler. These tests guard against // regressions like the one fixed for postToolUseFailure (issue #1220). - it("dispatches postToolUseFailure to onPostToolUseFailure handler", async () => { - const client = new CopilotClient(); - await client.start(); - onTestFinished(() => client.forceStop()); + function createHookTestSession(hooks: SessionHooks): CopilotSession { + const session = new CopilotSession("session-hooks-test", {} as any); + session.registerHooks(hooks); + return session; + } + it("dispatches postToolUseFailure to onPostToolUseFailure handler", async () => { const received: { input: any; invocation: any }[] = []; - const session = await client.createSession({ - onPermissionRequest: approveAll, - hooks: { - onPostToolUseFailure: async (input, invocation) => { - received.push({ input, invocation }); - return { additionalContext: "failure observed" }; - }, + const session = createHookTestSession({ + onPostToolUseFailure: async (input, invocation) => { + received.push({ input, invocation }); + return { additionalContext: "failure observed" }; }, }); @@ -2469,19 +2468,12 @@ describe("CopilotClient", () => { }); it("does not fall back to onPostToolUse for postToolUseFailure events", async () => { - const client = new CopilotClient(); - await client.start(); - onTestFinished(() => client.forceStop()); - const postUseCalls: string[] = []; - const session = await client.createSession({ - onPermissionRequest: approveAll, - hooks: { - // Only onPostToolUse registered; postToolUseFailure events - // must not be routed here. - onPostToolUse: async (input) => { - postUseCalls.push(input.toolName); - }, + const session = createHookTestSession({ + // Only onPostToolUse registered; postToolUseFailure events + // must not be routed here. + onPostToolUse: async (input) => { + postUseCalls.push(input.toolName); }, }); @@ -2498,21 +2490,14 @@ describe("CopilotClient", () => { }); it("dispatches postToolUse and postToolUseFailure to their respective handlers", async () => { - const client = new CopilotClient(); - await client.start(); - onTestFinished(() => client.forceStop()); - const postCalls: string[] = []; const failureCalls: string[] = []; - const session = await client.createSession({ - onPermissionRequest: approveAll, - hooks: { - onPostToolUse: async (input) => { - postCalls.push(input.toolName); - }, - onPostToolUseFailure: async (input) => { - failureCalls.push(input.toolName); - }, + const session = createHookTestSession({ + onPostToolUse: async (input) => { + postCalls.push(input.toolName); + }, + onPostToolUseFailure: async (input) => { + failureCalls.push(input.toolName); }, }); @@ -2539,29 +2524,23 @@ describe("CopilotClient", () => { }); it("routes hooks.invoke JSON-RPC requests to the SessionHooks handler", async () => { - // Validates the full JSON-RPC entry point used by the CLI: + // Validates the full JSON-RPC entry point used by legacy runtimes: // CopilotClient.handleHooksInvoke({sessionId, hookType, input}) // → CopilotSession._handleHooksInvoke(hookType, input) // → SessionHooks.onPostToolUseFailure(normalizedInput, {sessionId}) // - // This guards the wire-format contract that the bundled Copilot - // CLI relies on: the hookType string "postToolUseFailure" and the + // This guards the wire-format contract: the hookType string "postToolUseFailure" and the // input shape `{toolName, toolArgs, error, timestamp, cwd}`. // The SDK maps that to public `{..., timestamp: Date, workingDirectory}`. - const client = new CopilotClient(); - await client.start(); - onTestFinished(() => client.forceStop()); - const received: { input: any; invocation: any }[] = []; - const session = await client.createSession({ - onPermissionRequest: approveAll, - hooks: { - onPostToolUseFailure: async (input, invocation) => { - received.push({ input, invocation }); - return { additionalContext: "context from failure hook" }; - }, + const client = new CopilotClient(); + const session = createHookTestSession({ + onPostToolUseFailure: async (input, invocation) => { + received.push({ input, invocation }); + return { additionalContext: "context from failure hook" }; }, }); + (client as any).sessions.set(session.sessionId, session); const failureInput = { toolName: "shell", diff --git a/rust/tests/e2e/hooks_extended.rs b/rust/tests/e2e/hooks_extended.rs index 27d4c4b8ca..6fe031c26d 100644 --- a/rust/tests/e2e/hooks_extended.rs +++ b/rust/tests/e2e/hooks_extended.rs @@ -116,7 +116,6 @@ impl SessionHooks for ExtendedHooks { ) -> Option { Some(PostToolUseFailureOutput { additional_context: Some("not used".to_string()), - ..PostToolUseFailureOutput::default() }) } } diff --git a/scripts/codegen/csharp.ts b/scripts/codegen/csharp.ts index 9f760a0461..b06809c183 100644 --- a/scripts/codegen/csharp.ts +++ b/scripts/codegen/csharp.ts @@ -495,8 +495,6 @@ const EXPERIMENTAL_ATTRIBUTE = "[Experimental(Diagnostics.Experimental)]"; const EDITOR_BROWSABLE_NEVER_ATTRIBUTE = "[EditorBrowsable(EditorBrowsableState.Never)]"; const OBSOLETE_ATTRIBUTE = `#if NET5_0_OR_GREATER [Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")] -#else -[Obsolete("This member is deprecated and will be removed in a future version.")] #endif`; const STRING_ENUM_RESERVED_MEMBER_NAMES = new Set(["Value", "Equals", "GetHashCode", "ToString", "Converter"]);