diff --git a/dotnet/src/Generated/Rpc.cs b/dotnet/src/Generated/Rpc.cs index 295fb8bfa..0c9880de6 100644 --- a/dotnet/src/Generated/Rpc.cs +++ b/dotnet/src/Generated/Rpc.cs @@ -815,6 +815,58 @@ internal sealed class WorkspacesCreateFileRequest public string Content { get; set; } = string.Empty; } +/// RPC data type for InstructionsSources operations. +public sealed class InstructionsSources +{ + /// Unique identifier for this source (used for toggling). + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// Human-readable label. + [JsonPropertyName("label")] + public string Label { get; set; } = string.Empty; + + /// File path relative to repo or absolute for home. + [JsonPropertyName("sourcePath")] + public string SourcePath { get; set; } = string.Empty; + + /// Raw content of the instruction file. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; + + /// Category of instruction source — used for merge logic. + [JsonPropertyName("type")] + public InstructionsSourcesType Type { get; set; } + + /// Where this source lives — used for UI grouping. + [JsonPropertyName("location")] + public InstructionsSourcesLocation Location { get; set; } + + /// Glob pattern from frontmatter — when set, this instruction applies only to matching files. + [JsonPropertyName("applyTo")] + public string? ApplyTo { get; set; } + + /// Short description (body after frontmatter) for use in instruction tables. + [JsonPropertyName("description")] + public string? Description { get; set; } +} + +/// RPC data type for InstructionsGetSources operations. +public sealed class InstructionsGetSourcesResult +{ + /// Instruction sources for the session. + [JsonPropertyName("sources")] + public IList Sources { get => field ??= []; set; } +} + +/// RPC data type for SessionInstructionsGetSources operations. +internal sealed class SessionInstructionsGetSourcesRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + /// RPC data type for FleetStart operations. [Experimental(Diagnostics.Experimental)] public sealed class FleetStartResult @@ -837,8 +889,8 @@ internal sealed class FleetStartRequest public string? Prompt { get; set; } } -/// RPC data type for Agent operations. -public sealed class Agent +/// RPC data type for AgentInfo operations. +public sealed class AgentInfo { /// Unique identifier of the custom agent. [JsonPropertyName("name")] @@ -859,7 +911,7 @@ public sealed class AgentList { /// Available custom agents. [JsonPropertyName("agents")] - public IList Agents { get => field ??= []; set; } + public IList Agents { get => field ??= []; set; } } /// RPC data type for SessionAgentList operations. @@ -871,29 +923,13 @@ internal sealed class SessionAgentListRequest public string SessionId { get; set; } = string.Empty; } -/// RPC data type for AgentGetCurrentResultAgent operations. -public sealed class AgentGetCurrentResultAgent -{ - /// Unique identifier of the custom agent. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - - /// Human-readable display name. - [JsonPropertyName("displayName")] - public string DisplayName { get; set; } = string.Empty; - - /// Description of the agent's purpose. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; -} - /// RPC data type for AgentGetCurrent operations. [Experimental(Diagnostics.Experimental)] public sealed class AgentGetCurrentResult { /// Currently selected custom agent, or null if using the default agent. [JsonPropertyName("agent")] - public AgentGetCurrentResultAgent? Agent { get; set; } + public AgentInfo? Agent { get; set; } } /// RPC data type for SessionAgentGetCurrent operations. @@ -905,29 +941,13 @@ internal sealed class SessionAgentGetCurrentRequest public string SessionId { get; set; } = string.Empty; } -/// The newly selected custom agent. -public sealed class AgentSelectAgent -{ - /// Unique identifier of the custom agent. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - - /// Human-readable display name. - [JsonPropertyName("displayName")] - public string DisplayName { get; set; } = string.Empty; - - /// Description of the agent's purpose. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; -} - /// RPC data type for AgentSelect operations. [Experimental(Diagnostics.Experimental)] public sealed class AgentSelectResult { /// The newly selected custom agent. [JsonPropertyName("agent")] - public AgentSelectAgent Agent { get => field ??= new(); set; } + public AgentInfo Agent { get => field ??= new(); set; } } /// RPC data type for AgentSelect operations. @@ -952,29 +972,13 @@ internal sealed class SessionAgentDeselectRequest public string SessionId { get; set; } = string.Empty; } -/// RPC data type for AgentReloadAgent operations. -public sealed class AgentReloadAgent -{ - /// Unique identifier of the custom agent. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - - /// Human-readable display name. - [JsonPropertyName("displayName")] - public string DisplayName { get; set; } = string.Empty; - - /// Description of the agent's purpose. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; -} - /// RPC data type for AgentReload operations. [Experimental(Diagnostics.Experimental)] public sealed class AgentReloadResult { /// Reloaded custom agents. [JsonPropertyName("agents")] - public IList Agents { get => field ??= []; set; } + public IList Agents { get => field ??= []; set; } } /// RPC data type for SessionAgentReload operations. @@ -2009,6 +2013,47 @@ public enum WorkspacesGetWorkspaceResultWorkspaceSessionSyncLevel } +/// Category of instruction source — used for merge logic. +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum InstructionsSourcesType +{ + /// The home variant. + [JsonStringEnumMemberName("home")] + Home, + /// The repo variant. + [JsonStringEnumMemberName("repo")] + Repo, + /// The model variant. + [JsonStringEnumMemberName("model")] + Model, + /// The vscode variant. + [JsonStringEnumMemberName("vscode")] + Vscode, + /// The nested-agents variant. + [JsonStringEnumMemberName("nested-agents")] + NestedAgents, + /// The child-instructions variant. + [JsonStringEnumMemberName("child-instructions")] + ChildInstructions, +} + + +/// Where this source lives — used for UI grouping. +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum InstructionsSourcesLocation +{ + /// The user variant. + [JsonStringEnumMemberName("user")] + User, + /// The repository variant. + [JsonStringEnumMemberName("repository")] + Repository, + /// The working-directory variant. + [JsonStringEnumMemberName("working-directory")] + WorkingDirectory, +} + + /// Connection status: connected, failed, needs-auth, pending, disabled, or not_configured. [JsonConverter(typeof(JsonStringEnumConverter))] public enum McpServerStatus @@ -2380,6 +2425,7 @@ internal SessionRpc(JsonRpc rpc, string sessionId) Name = new NameApi(rpc, sessionId); Plan = new PlanApi(rpc, sessionId); Workspaces = new WorkspacesApi(rpc, sessionId); + Instructions = new InstructionsApi(rpc, sessionId); Fleet = new FleetApi(rpc, sessionId); Agent = new AgentApi(rpc, sessionId); Skills = new SkillsApi(rpc, sessionId); @@ -2410,6 +2456,9 @@ internal SessionRpc(JsonRpc rpc, string sessionId) /// Workspaces APIs. public WorkspacesApi Workspaces { get; } + /// Instructions APIs. + public InstructionsApi Instructions { get; } + /// Fleet APIs. public FleetApi Fleet { get; } @@ -2613,6 +2662,26 @@ public async Task CreateFileAsync(string path, string content, CancellationToken } } +/// Provides session-scoped Instructions APIs. +public sealed class InstructionsApi +{ + private readonly JsonRpc _rpc; + private readonly string _sessionId; + + internal InstructionsApi(JsonRpc rpc, string sessionId) + { + _rpc = rpc; + _sessionId = sessionId; + } + + /// Calls "session.instructions.getSources". + public async Task GetSourcesAsync(CancellationToken cancellationToken = default) + { + var request = new SessionInstructionsGetSourcesRequest { SessionId = _sessionId }; + return await CopilotClient.InvokeRpcAsync(_rpc, "session.instructions.getSources", [request], cancellationToken); + } +} + /// Provides session-scoped Fleet APIs. [Experimental(Diagnostics.Experimental)] public sealed class FleetApi @@ -3144,13 +3213,10 @@ public static void RegisterClientSessionApiHandlers(JsonRpc rpc, FuncUpdated working directory and git context after the change. +/// Working directory and git context at session start. /// Represents the session.context_changed event. public partial class SessionContextChangedEvent : SessionEvent { @@ -731,7 +731,7 @@ public partial class HookEndEvent : SessionEvent public required HookEndData Data { get; set; } } -/// System or developer message content with role and optional template metadata. +/// System/developer instruction content with role and optional template metadata. /// Represents the system.message event. public partial class SystemMessageEvent : SessionEvent { @@ -1124,7 +1124,7 @@ public partial class SessionStartData /// Working directory and git context at session start. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("context")] - public StartContext? Context { get; set; } + public WorkingDirectoryContext? Context { get; set; } /// Whether the session was already in use by another client at start time. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] @@ -1161,7 +1161,7 @@ public partial class SessionResumeData /// Updated working directory and git context at resume time. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("context")] - public ResumeContext? Context { get; set; } + public WorkingDirectoryContext? Context { get; set; } /// Whether the session was already in use by another client at resume time. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] @@ -1469,7 +1469,7 @@ public partial class SessionShutdownData public double? ToolDefinitionsTokens { get; set; } } -/// Updated working directory and git context after the change. +/// Working directory and git context at session start. public partial class SessionContextChangedData { /// Current working directory path. @@ -1489,7 +1489,7 @@ public partial class SessionContextChangedData /// Hosting platform type of the repository (github or ado). [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("hostType")] - public ContextChangedHostType? HostType { get; set; } + public WorkingDirectoryContextHostType? HostType { get; set; } /// Current git branch name. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] @@ -1671,6 +1671,16 @@ public partial class UserMessageData [JsonPropertyName("attachments")] public UserMessageAttachment[]? Attachments { get; set; } + /// Normalized document MIME types that were sent natively instead of through tagged_files XML. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("supportedNativeDocumentMimeTypes")] + public string[]? SupportedNativeDocumentMimeTypes { get; set; } + + /// Path-backed native document attachments that stayed on the tagged_files path flow because native upload would exceed the request size limit. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("nativeDocumentPathFallbackPaths")] + public string[]? NativeDocumentPathFallbackPaths { get; set; } + /// Origin of this message, used for timeline filtering (e.g., "skill-pdf" for skill-injected messages that should be hidden from the user). [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("source")] @@ -1797,6 +1807,7 @@ public partial class AssistantMessageData public string? RequestId { get; set; } /// Tool call ID of the parent tool invocation when this event originates from a sub-agent. + [Obsolete("This member is deprecated and will be removed in a future version.")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("parentToolCallId")] public string? ParentToolCallId { get; set; } @@ -1814,6 +1825,7 @@ public partial class AssistantMessageDeltaData public required string DeltaContent { get; set; } /// Tool call ID of the parent tool invocation when this event originates from a sub-agent. + [Obsolete("This member is deprecated and will be removed in a future version.")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("parentToolCallId")] public string? ParentToolCallId { get; set; } @@ -1895,6 +1907,7 @@ public partial class AssistantUsageData public string? ProviderCallId { get; set; } /// Parent tool call ID when this usage originates from a sub-agent. + [Obsolete("This member is deprecated and will be removed in a future version.")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("parentToolCallId")] public string? ParentToolCallId { get; set; } @@ -1967,6 +1980,7 @@ public partial class ToolExecutionStartData public string? McpToolName { get; set; } /// Tool call ID of the parent tool invocation when this event originates from a sub-agent. + [Obsolete("This member is deprecated and will be removed in a future version.")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("parentToolCallId")] public string? ParentToolCallId { get; set; } @@ -2038,6 +2052,7 @@ public partial class ToolExecutionCompleteData public IDictionary? ToolTelemetry { get; set; } /// Tool call ID of the parent tool invocation when this event originates from a sub-agent. + [Obsolete("This member is deprecated and will be removed in a future version.")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("parentToolCallId")] public string? ParentToolCallId { get; set; } @@ -2239,10 +2254,10 @@ public partial class HookEndData public HookEndError? Error { get; set; } } -/// System or developer message content with role and optional template metadata. +/// System/developer instruction content with role and optional template metadata. public partial class SystemMessageData { - /// The system or developer prompt text. + /// The system or developer prompt text sent as model input. [JsonPropertyName("content")] public required string Content { get; set; } @@ -2673,8 +2688,8 @@ public partial class SessionExtensionsLoadedData } /// Working directory and git context at session start. -/// Nested data type for StartContext. -public partial class StartContext +/// Nested data type for WorkingDirectoryContext. +public partial class WorkingDirectoryContext { /// Current working directory path. [JsonPropertyName("cwd")] @@ -2693,46 +2708,7 @@ public partial class StartContext /// Hosting platform type of the repository (github or ado). [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("hostType")] - public StartContextHostType? HostType { get; set; } - - /// Current git branch name. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("branch")] - public string? Branch { get; set; } - - /// Head commit of current git branch at session start time. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("headCommit")] - public string? HeadCommit { get; set; } - - /// Base commit of current git branch at session start time. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("baseCommit")] - public string? BaseCommit { get; set; } -} - -/// Updated working directory and git context at resume time. -/// Nested data type for ResumeContext. -public partial class ResumeContext -{ - /// Current working directory path. - [JsonPropertyName("cwd")] - public required string Cwd { get; set; } - - /// Root directory of the git repository, resolved via git rev-parse. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("gitRoot")] - public string? GitRoot { get; set; } - - /// Repository identifier derived from the git remote URL ("owner/name" for GitHub, "org/project/repo" for Azure DevOps). - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("repository")] - public string? Repository { get; set; } - - /// Hosting platform type of the repository (github or ado). - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("hostType")] - public ResumeContextHostType? HostType { get; set; } + public WorkingDirectoryContextHostType? HostType { get; set; } /// Current git branch name. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] @@ -3872,20 +3848,8 @@ public partial class ExtensionsLoadedExtension } /// Hosting platform type of the repository (github or ado). -[JsonConverter(typeof(JsonStringEnumConverter))] -public enum StartContextHostType -{ - /// The github variant. - [JsonStringEnumMemberName("github")] - Github, - /// The ado variant. - [JsonStringEnumMemberName("ado")] - Ado, -} - -/// Hosting platform type of the repository (github or ado). -[JsonConverter(typeof(JsonStringEnumConverter))] -public enum ResumeContextHostType +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum WorkingDirectoryContextHostType { /// The github variant. [JsonStringEnumMemberName("github")] @@ -3946,18 +3910,6 @@ public enum ShutdownType Error, } -/// Hosting platform type of the repository (github or ado). -[JsonConverter(typeof(JsonStringEnumConverter))] -public enum ContextChangedHostType -{ - /// The github variant. - [JsonStringEnumMemberName("github")] - Github, - /// The ado variant. - [JsonStringEnumMemberName("ado")] - Ado, -} - /// Type of GitHub reference. [JsonConverter(typeof(JsonStringEnumConverter))] public enum UserMessageAttachmentGithubReferenceType @@ -4278,7 +4230,6 @@ public enum ExtensionsLoadedExtensionStatus [JsonSerializable(typeof(PermissionRequestWrite))] [JsonSerializable(typeof(PermissionRequestedData))] [JsonSerializable(typeof(PermissionRequestedEvent))] -[JsonSerializable(typeof(ResumeContext))] [JsonSerializable(typeof(SamplingCompletedData))] [JsonSerializable(typeof(SamplingCompletedEvent))] [JsonSerializable(typeof(SamplingRequestedData))] @@ -4344,7 +4295,6 @@ public enum ExtensionsLoadedExtensionStatus [JsonSerializable(typeof(SkillInvokedData))] [JsonSerializable(typeof(SkillInvokedEvent))] [JsonSerializable(typeof(SkillsLoadedSkill))] -[JsonSerializable(typeof(StartContext))] [JsonSerializable(typeof(SubagentCompletedData))] [JsonSerializable(typeof(SubagentCompletedEvent))] [JsonSerializable(typeof(SubagentDeselectedData))] @@ -4401,5 +4351,6 @@ public enum ExtensionsLoadedExtensionStatus [JsonSerializable(typeof(UserMessageAttachmentSelectionDetailsStart))] [JsonSerializable(typeof(UserMessageData))] [JsonSerializable(typeof(UserMessageEvent))] +[JsonSerializable(typeof(WorkingDirectoryContext))] [JsonSerializable(typeof(JsonElement))] internal partial class SessionEventsJsonContext : JsonSerializerContext; \ No newline at end of file diff --git a/go/generated_session_events.go b/go/generated_session_events.go index 01a6a0811..95ace9123 100644 --- a/go/generated_session_events.go +++ b/go/generated_session_events.go @@ -637,7 +637,7 @@ type SessionStartData struct { // Reasoning effort level used for model calls, if applicable (e.g. "low", "medium", "high", "xhigh") ReasoningEffort *string `json:"reasoningEffort,omitempty"` // Working directory and git context at session start - Context *StartContext `json:"context,omitempty"` + Context *WorkingDirectoryContext `json:"context,omitempty"` // Whether the session was already in use by another client at start time AlreadyInUse *bool `json:"alreadyInUse,omitempty"` // Whether this session supports remote steering via Mission Control @@ -657,7 +657,7 @@ type SessionResumeData struct { // Reasoning effort level used for model calls, if applicable (e.g. "low", "medium", "high", "xhigh") ReasoningEffort *string `json:"reasoningEffort,omitempty"` // Updated working directory and git context at resume time - Context *ResumeContext `json:"context,omitempty"` + Context *WorkingDirectoryContext `json:"context,omitempty"` // Whether the session was already in use by another client at resume time AlreadyInUse *bool `json:"alreadyInUse,omitempty"` // Whether this session supports remote steering via Mission Control @@ -856,7 +856,7 @@ type SessionShutdownData struct { func (*SessionShutdownData) sessionEventData() {} -// Updated working directory and git context after the change +// Working directory and git context at session start type SessionContextChangedData struct { // Current working directory path Cwd string `json:"cwd"` @@ -865,7 +865,7 @@ type SessionContextChangedData struct { // Repository identifier derived from the git remote URL ("owner/name" for GitHub, "org/project/repo" for Azure DevOps) Repository *string `json:"repository,omitempty"` // Hosting platform type of the repository (github or ado) - HostType *ContextChangedHostType `json:"hostType,omitempty"` + HostType *WorkingDirectoryContextHostType `json:"hostType,omitempty"` // Current git branch name Branch *string `json:"branch,omitempty"` // Head commit of current git branch at session start time @@ -962,6 +962,10 @@ type UserMessageData struct { TransformedContent *string `json:"transformedContent,omitempty"` // Files, selections, or GitHub references attached to the message Attachments []UserMessageAttachment `json:"attachments,omitempty"` + // Normalized document MIME types that were sent natively instead of through tagged_files XML + SupportedNativeDocumentMIMETypes []string `json:"supportedNativeDocumentMimeTypes,omitempty"` + // Path-backed native document attachments that stayed on the tagged_files path flow because native upload would exceed the request size limit + NativeDocumentPathFallbackPaths []string `json:"nativeDocumentPathFallbackPaths,omitempty"` // Origin of this message, used for timeline filtering (e.g., "skill-pdf" for skill-injected messages that should be hidden from the user) Source *string `json:"source,omitempty"` // The agent mode that was active when this message was sent @@ -1047,6 +1051,7 @@ type AssistantMessageData struct { // GitHub request tracing ID (x-github-request-id header) for correlating with server-side logs RequestID *string `json:"requestId,omitempty"` // Tool call ID of the parent tool invocation when this event originates from a sub-agent + // Deprecated: ParentToolCallID is deprecated. ParentToolCallID *string `json:"parentToolCallId,omitempty"` } @@ -1059,6 +1064,7 @@ type AssistantMessageDeltaData struct { // Incremental text chunk to append to the message content DeltaContent string `json:"deltaContent"` // Tool call ID of the parent tool invocation when this event originates from a sub-agent + // Deprecated: ParentToolCallID is deprecated. ParentToolCallID *string `json:"parentToolCallId,omitempty"` } @@ -1101,6 +1107,7 @@ type AssistantUsageData struct { // GitHub request tracing ID (x-github-request-id header) for server-side log correlation ProviderCallID *string `json:"providerCallId,omitempty"` // Parent tool call ID when this usage originates from a sub-agent + // Deprecated: ParentToolCallID is deprecated. ParentToolCallID *string `json:"parentToolCallId,omitempty"` // Per-quota resource usage snapshots, keyed by quota identifier QuotaSnapshots map[string]AssistantUsageQuotaSnapshot `json:"quotaSnapshots,omitempty"` @@ -1145,6 +1152,7 @@ type ToolExecutionStartData struct { // Original tool name on the MCP server, when the tool is an MCP tool McpToolName *string `json:"mcpToolName,omitempty"` // Tool call ID of the parent tool invocation when this event originates from a sub-agent + // Deprecated: ParentToolCallID is deprecated. ParentToolCallID *string `json:"parentToolCallId,omitempty"` } @@ -1189,6 +1197,7 @@ type ToolExecutionCompleteData struct { // Tool-specific telemetry data (e.g., CodeQL check counts, grep match counts) ToolTelemetry map[string]any `json:"toolTelemetry,omitempty"` // Tool call ID of the parent tool invocation when this event originates from a sub-agent + // Deprecated: ParentToolCallID is deprecated. ParentToolCallID *string `json:"parentToolCallId,omitempty"` } @@ -1316,9 +1325,9 @@ type HookEndData struct { func (*HookEndData) sessionEventData() {} -// System or developer message content with role and optional template metadata +// System/developer instruction content with role and optional template metadata type SystemMessageData struct { - // The system or developer prompt text + // The system or developer prompt text sent as model input Content string `json:"content"` // Message role: "system" for system prompts, "developer" for developer-injected instructions Role SystemMessageRole `json:"role"` @@ -1632,25 +1641,7 @@ type SessionExtensionsLoadedData struct { func (*SessionExtensionsLoadedData) sessionEventData() {} // Working directory and git context at session start -type StartContext struct { - // Current working directory path - Cwd string `json:"cwd"` - // Root directory of the git repository, resolved via git rev-parse - GitRoot *string `json:"gitRoot,omitempty"` - // Repository identifier derived from the git remote URL ("owner/name" for GitHub, "org/project/repo" for Azure DevOps) - Repository *string `json:"repository,omitempty"` - // Hosting platform type of the repository (github or ado) - HostType *StartContextHostType `json:"hostType,omitempty"` - // Current git branch name - Branch *string `json:"branch,omitempty"` - // Head commit of current git branch at session start time - HeadCommit *string `json:"headCommit,omitempty"` - // Base commit of current git branch at session start time - BaseCommit *string `json:"baseCommit,omitempty"` -} - -// Updated working directory and git context at resume time -type ResumeContext struct { +type WorkingDirectoryContext struct { // Current working directory path Cwd string `json:"cwd"` // Root directory of the git repository, resolved via git rev-parse @@ -1658,7 +1649,7 @@ type ResumeContext struct { // Repository identifier derived from the git remote URL ("owner/name" for GitHub, "org/project/repo" for Azure DevOps) Repository *string `json:"repository,omitempty"` // Hosting platform type of the repository (github or ado) - HostType *ResumeContextHostType `json:"hostType,omitempty"` + HostType *WorkingDirectoryContextHostType `json:"hostType,omitempty"` // Current git branch name Branch *string `json:"branch,omitempty"` // Head commit of current git branch at session start time @@ -2109,19 +2100,11 @@ type ExtensionsLoadedExtension struct { } // Hosting platform type of the repository (github or ado) -type StartContextHostType string +type WorkingDirectoryContextHostType string const ( - StartContextHostTypeGithub StartContextHostType = "github" - StartContextHostTypeAdo StartContextHostType = "ado" -) - -// Hosting platform type of the repository (github or ado) -type ResumeContextHostType string - -const ( - ResumeContextHostTypeGithub ResumeContextHostType = "github" - ResumeContextHostTypeAdo ResumeContextHostType = "ado" + WorkingDirectoryContextHostTypeGithub WorkingDirectoryContextHostType = "github" + WorkingDirectoryContextHostTypeAdo WorkingDirectoryContextHostType = "ado" ) // The type of operation performed on the plan file @@ -2157,14 +2140,6 @@ const ( ShutdownTypeError ShutdownType = "error" ) -// Hosting platform type of the repository (github or ado) -type ContextChangedHostType string - -const ( - ContextChangedHostTypeGithub ContextChangedHostType = "github" - ContextChangedHostTypeAdo ContextChangedHostType = "ado" -) - // Type discriminator for UserMessageAttachment. type UserMessageAttachmentType string diff --git a/go/internal/e2e/multi_client_test.go b/go/internal/e2e/multi_client_test.go index 389912284..3b009e898 100644 --- a/go/internal/e2e/multi_client_test.go +++ b/go/internal/e2e/multi_client_test.go @@ -200,9 +200,7 @@ func TestMultiClient(t *testing.T) { mu1.Lock() c1PermRequested := filterEventsByType(client1Events, copilot.SessionEventTypePermissionRequested) mu1.Unlock() - mu2.Lock() - c2PermRequested := filterEventsByType(client2Events, copilot.SessionEventTypePermissionRequested) - mu2.Unlock() + c2PermRequested := waitForEventsByType(t, &mu2, &client2Events, copilot.SessionEventTypePermissionRequested, 5*time.Second) if len(c1PermRequested) == 0 { t.Errorf("Expected client 1 to see permission.requested events") @@ -215,9 +213,7 @@ func TestMultiClient(t *testing.T) { mu1.Lock() c1PermCompleted := filterEventsByType(client1Events, copilot.SessionEventTypePermissionCompleted) mu1.Unlock() - mu2.Lock() - c2PermCompleted := filterEventsByType(client2Events, copilot.SessionEventTypePermissionCompleted) - mu2.Unlock() + c2PermCompleted := waitForEventsByType(t, &mu2, &client2Events, copilot.SessionEventTypePermissionCompleted, 5*time.Second) if len(c1PermCompleted) == 0 { t.Errorf("Expected client 1 to see permission.completed events") @@ -297,9 +293,7 @@ func TestMultiClient(t *testing.T) { mu1.Lock() c1PermRequested := filterEventsByType(client1Events, copilot.SessionEventTypePermissionRequested) mu1.Unlock() - mu2.Lock() - c2PermRequested := filterEventsByType(client2Events, copilot.SessionEventTypePermissionRequested) - mu2.Unlock() + c2PermRequested := waitForEventsByType(t, &mu2, &client2Events, copilot.SessionEventTypePermissionRequested, 5*time.Second) if len(c1PermRequested) == 0 { t.Errorf("Expected client 1 to see permission.requested events") @@ -312,9 +306,7 @@ func TestMultiClient(t *testing.T) { mu1.Lock() c1PermCompleted := filterEventsByType(client1Events, copilot.SessionEventTypePermissionCompleted) mu1.Unlock() - mu2.Lock() - c2PermCompleted := filterEventsByType(client2Events, copilot.SessionEventTypePermissionCompleted) - mu2.Unlock() + c2PermCompleted := waitForEventsByType(t, &mu2, &client2Events, copilot.SessionEventTypePermissionCompleted, 5*time.Second) if len(c1PermCompleted) == 0 { t.Errorf("Expected client 1 to see permission.completed events") @@ -519,3 +511,20 @@ func filterEventsByType(events []copilot.SessionEvent, eventType copilot.Session } return filtered } + +// waitForEventsByType polls the event slice until at least one event of the given type appears +// or the timeout is reached. This avoids flaky assertions on async event delivery. +func waitForEventsByType(t *testing.T, mu *sync.Mutex, events *[]copilot.SessionEvent, eventType copilot.SessionEventType, timeout time.Duration) []copilot.SessionEvent { + t.Helper() + deadline := time.Now().Add(timeout) + for time.Now().Before(deadline) { + mu.Lock() + filtered := filterEventsByType(*events, eventType) + mu.Unlock() + if len(filtered) > 0 { + return filtered + } + time.Sleep(50 * time.Millisecond) + } + return nil +} diff --git a/go/rpc/generated_rpc.go b/go/rpc/generated_rpc.go index 23bdfb618..528a933b5 100644 --- a/go/rpc/generated_rpc.go +++ b/go/rpc/generated_rpc.go @@ -12,6 +12,319 @@ import ( "time" ) +type UIElicitationResponseContent map[string]*UIElicitationFieldValue + +// Model capabilities and limits +type ModelCapabilities struct { + // Token limits for prompts, outputs, and context window + Limits *ModelCapabilitiesLimits `json:"limits,omitempty"` + // Feature flags indicating what the model supports + Supports *ModelCapabilitiesSupports `json:"supports,omitempty"` +} + +// Token limits for prompts, outputs, and context window +type ModelCapabilitiesLimits struct { + // Maximum total context window size in tokens + MaxContextWindowTokens *int64 `json:"max_context_window_tokens,omitempty"` + // Maximum number of output/completion tokens + MaxOutputTokens *int64 `json:"max_output_tokens,omitempty"` + // Maximum number of prompt/input tokens + MaxPromptTokens *int64 `json:"max_prompt_tokens,omitempty"` + // Vision-specific limits + Vision *PurpleModelCapabilitiesLimitsVision `json:"vision,omitempty"` +} + +// Vision-specific limits +type PurpleModelCapabilitiesLimitsVision struct { + // Maximum image size in bytes + MaxPromptImageSize int64 `json:"max_prompt_image_size"` + // Maximum number of images per prompt + MaxPromptImages int64 `json:"max_prompt_images"` + // MIME types the model accepts + SupportedMediaTypes []string `json:"supported_media_types"` +} + +// Feature flags indicating what the model supports +type ModelCapabilitiesSupports struct { + // Whether this model supports reasoning effort configuration + ReasoningEffort *bool `json:"reasoningEffort,omitempty"` + // Whether this model supports vision/image input + Vision *bool `json:"vision,omitempty"` +} + +// Vision-specific limits +type ModelCapabilitiesLimitsVision struct { + // Maximum image size in bytes + MaxPromptImageSize int64 `json:"max_prompt_image_size"` + // Maximum number of images per prompt + MaxPromptImages int64 `json:"max_prompt_images"` + // MIME types the model accepts + SupportedMediaTypes []string `json:"supported_media_types"` +} + +// MCP server configuration (local/stdio or remote/http) +type MCPServerConfig struct { + Args []string `json:"args,omitempty"` + Command *string `json:"command,omitempty"` + Cwd *string `json:"cwd,omitempty"` + Env map[string]string `json:"env,omitempty"` + FilterMapping *FilterMapping `json:"filterMapping"` + IsDefaultServer *bool `json:"isDefaultServer,omitempty"` + // Timeout in milliseconds for tool calls to this server. + Timeout *int64 `json:"timeout,omitempty"` + // Tools to include. Defaults to all tools if not specified. + Tools []string `json:"tools,omitempty"` + // Remote transport type. Defaults to "http" when omitted. + Type *MCPServerConfigType `json:"type,omitempty"` + Headers map[string]string `json:"headers,omitempty"` + OauthClientID *string `json:"oauthClientId,omitempty"` + OauthPublicClient *bool `json:"oauthPublicClient,omitempty"` + URL *string `json:"url,omitempty"` +} + +type DiscoveredMCPServer struct { + // Whether the server is enabled (not in the disabled list) + Enabled bool `json:"enabled"` + // Server name (config key) + Name string `json:"name"` + // Configuration source + Source MCPServerSource `json:"source"` + // Server transport type: stdio, http, sse, or memory (local configs are normalized to stdio) + Type *DiscoveredMCPServerType `json:"type,omitempty"` +} + +type ServerSkillList struct { + // All discovered skills across all sources + Skills []SkillElement `json:"skills"` +} + +type SkillElement struct { + // Description of what the skill does + Description string `json:"description"` + // Whether the skill is currently enabled (based on global config) + Enabled bool `json:"enabled"` + // Unique identifier for the skill + Name string `json:"name"` + // Absolute path to the skill file + Path *string `json:"path,omitempty"` + // The project path this skill belongs to (only for project/inherited skills) + ProjectPath *string `json:"projectPath,omitempty"` + // Source location type (e.g., project, personal-copilot, plugin, builtin) + Source string `json:"source"` + // Whether the skill can be invoked by the user as a slash command + UserInvocable bool `json:"userInvocable"` +} + +type ServerSkill struct { + // Description of what the skill does + Description string `json:"description"` + // Whether the skill is currently enabled (based on global config) + Enabled bool `json:"enabled"` + // Unique identifier for the skill + Name string `json:"name"` + // Absolute path to the skill file + Path *string `json:"path,omitempty"` + // The project path this skill belongs to (only for project/inherited skills) + ProjectPath *string `json:"projectPath,omitempty"` + // Source location type (e.g., project, personal-copilot, plugin, builtin) + Source string `json:"source"` + // Whether the skill can be invoked by the user as a slash command + UserInvocable bool `json:"userInvocable"` +} + +type CurrentModel struct { + // Currently active model identifier + ModelID *string `json:"modelId,omitempty"` +} + +// Override individual model capabilities resolved by the runtime +type ModelCapabilitiesOverride struct { + // Token limits for prompts, outputs, and context window + Limits *ModelCapabilitiesOverrideLimits `json:"limits,omitempty"` + // Feature flags indicating what the model supports + Supports *ModelCapabilitiesOverrideSupports `json:"supports,omitempty"` +} + +// Token limits for prompts, outputs, and context window +type ModelCapabilitiesOverrideLimits struct { + // Maximum total context window size in tokens + MaxContextWindowTokens *int64 `json:"max_context_window_tokens,omitempty"` + MaxOutputTokens *int64 `json:"max_output_tokens,omitempty"` + MaxPromptTokens *int64 `json:"max_prompt_tokens,omitempty"` + Vision *PurpleModelCapabilitiesOverrideLimitsVision `json:"vision,omitempty"` +} + +type PurpleModelCapabilitiesOverrideLimitsVision struct { + // Maximum image size in bytes + MaxPromptImageSize *int64 `json:"max_prompt_image_size,omitempty"` + // Maximum number of images per prompt + MaxPromptImages *int64 `json:"max_prompt_images,omitempty"` + // MIME types the model accepts + SupportedMediaTypes []string `json:"supported_media_types,omitempty"` +} + +// Feature flags indicating what the model supports +type ModelCapabilitiesOverrideSupports struct { + ReasoningEffort *bool `json:"reasoningEffort,omitempty"` + Vision *bool `json:"vision,omitempty"` +} + +type AgentInfo struct { + // Description of the agent's purpose + Description string `json:"description"` + // Human-readable display name + DisplayName string `json:"displayName"` + // Unique identifier of the custom agent + Name string `json:"name"` +} + +type MCPServerList struct { + // Configured MCP servers + Servers []MCPServer `json:"servers"` +} + +type MCPServer struct { + // Error message if the server failed to connect + Error *string `json:"error,omitempty"` + // Server name (config key) + Name string `json:"name"` + // Configuration source: user, workspace, plugin, or builtin + Source *MCPServerSource `json:"source,omitempty"` + // Connection status: connected, failed, needs-auth, pending, disabled, or not_configured + Status MCPServerStatus `json:"status"` +} + +type ToolCallResult struct { + // Error message if the tool call failed + Error *string `json:"error,omitempty"` + // Type of the tool result + ResultType *string `json:"resultType,omitempty"` + // Text result to send back to the LLM + TextResultForLlm string `json:"textResultForLlm"` + // Telemetry data from tool execution + ToolTelemetry map[string]any `json:"toolTelemetry,omitempty"` +} + +type HandleToolCallResult struct { + // Whether the tool call result was handled successfully + Success bool `json:"success"` +} + +type UIElicitationStringEnumField struct { + Default *string `json:"default,omitempty"` + Description *string `json:"description,omitempty"` + Enum []string `json:"enum"` + EnumNames []string `json:"enumNames,omitempty"` + Title *string `json:"title,omitempty"` + Type UIElicitationStringEnumFieldType `json:"type"` +} + +type UIElicitationStringOneOfField struct { + Default *string `json:"default,omitempty"` + Description *string `json:"description,omitempty"` + OneOf []UIElicitationStringOneOfFieldOneOf `json:"oneOf"` + Title *string `json:"title,omitempty"` + Type UIElicitationStringEnumFieldType `json:"type"` +} + +type UIElicitationStringOneOfFieldOneOf struct { + Const string `json:"const"` + Title string `json:"title"` +} + +type UIElicitationArrayEnumField struct { + Default []string `json:"default,omitempty"` + Description *string `json:"description,omitempty"` + Items UIElicitationArrayEnumFieldItems `json:"items"` + MaxItems *float64 `json:"maxItems,omitempty"` + MinItems *float64 `json:"minItems,omitempty"` + Title *string `json:"title,omitempty"` + Type UIElicitationArrayEnumFieldType `json:"type"` +} + +type UIElicitationArrayEnumFieldItems struct { + Enum []string `json:"enum"` + Type UIElicitationStringEnumFieldType `json:"type"` +} + +type UIElicitationArrayAnyOfField struct { + Default []string `json:"default,omitempty"` + Description *string `json:"description,omitempty"` + Items UIElicitationArrayAnyOfFieldItems `json:"items"` + MaxItems *float64 `json:"maxItems,omitempty"` + MinItems *float64 `json:"minItems,omitempty"` + Title *string `json:"title,omitempty"` + Type UIElicitationArrayEnumFieldType `json:"type"` +} + +type UIElicitationArrayAnyOfFieldItems struct { + AnyOf []PurpleUIElicitationArrayAnyOfFieldItemsAnyOf `json:"anyOf"` +} + +type PurpleUIElicitationArrayAnyOfFieldItemsAnyOf struct { + Const string `json:"const"` + Title string `json:"title"` +} + +// The elicitation response (accept with form values, decline, or cancel) +type UIElicitationResponse struct { + // The user's response: accept (submitted), decline (rejected), or cancel (dismissed) + Action UIElicitationResponseAction `json:"action"` + // The form values submitted by the user (present when action is 'accept') + Content map[string]*UIElicitationFieldValue `json:"content,omitempty"` +} + +type UIHandlePendingElicitationRequest struct { + // The unique request ID from the elicitation.requested event + RequestID string `json:"requestId"` + // The elicitation response (accept with form values, decline, or cancel) + Result UIElicitationResponse `json:"result"` +} + +type UIElicitationResult struct { + // Whether the response was accepted. False if the request was already resolved by another + // client. + Success bool `json:"success"` +} + +type PermissionDecisionRequest struct { + // Request ID of the pending permission request + RequestID string `json:"requestId"` + Result PermissionDecision `json:"result"` +} + +type PermissionDecision struct { + // The permission request was approved + // + // Denied because approval rules explicitly blocked it + // + // Denied because no approval rule matched and user confirmation was unavailable + // + // Denied by the user during an interactive prompt + // + // Denied by the organization's content exclusion policy + // + // Denied by a permission request hook registered by an extension or plugin + Kind Kind `json:"kind"` + // Rules that denied the request + Rules []any `json:"rules,omitempty"` + // Optional feedback from the user explaining the denial + Feedback *string `json:"feedback,omitempty"` + // Human-readable explanation of why the path was excluded + // + // Optional message from the hook explaining the denial + Message *string `json:"message,omitempty"` + // File path that triggered the exclusion + Path *string `json:"path,omitempty"` + // Whether to interrupt the current agent turn + Interrupt *bool `json:"interrupt,omitempty"` +} + +type PermissionRequestResult struct { + // Whether the permission request was handled successfully + Success bool `json:"success"` +} + type PingResult struct { // Echoed message (or default greeting) Message string `json:"message"` @@ -28,14 +341,14 @@ type PingRequest struct { type ModelList struct { // List of available models with full metadata - Models []Model `json:"models"` + Models []ModelElement `json:"models"` } -type Model struct { +type ModelElement struct { // Billing information Billing *ModelBilling `json:"billing,omitempty"` // Model capabilities and limits - Capabilities ModelCapabilities `json:"capabilities"` + Capabilities CapabilitiesClass `json:"capabilities"` // Default reasoning effort level (only present if model supports reasoning effort) DefaultReasoningEffort *string `json:"defaultReasoningEffort,omitempty"` // Model identifier (e.g., "claude-sonnet-4.5") @@ -55,15 +368,15 @@ type ModelBilling struct { } // Model capabilities and limits -type ModelCapabilities struct { +type CapabilitiesClass struct { // Token limits for prompts, outputs, and context window - Limits *ModelCapabilitiesLimits `json:"limits,omitempty"` + Limits *CapabilitiesLimits `json:"limits,omitempty"` // Feature flags indicating what the model supports - Supports *ModelCapabilitiesSupports `json:"supports,omitempty"` + Supports *CapabilitiesSupports `json:"supports,omitempty"` } // Token limits for prompts, outputs, and context window -type ModelCapabilitiesLimits struct { +type CapabilitiesLimits struct { // Maximum total context window size in tokens MaxContextWindowTokens *int64 `json:"max_context_window_tokens,omitempty"` // Maximum number of output/completion tokens @@ -71,11 +384,11 @@ type ModelCapabilitiesLimits struct { // Maximum number of prompt/input tokens MaxPromptTokens *int64 `json:"max_prompt_tokens,omitempty"` // Vision-specific limits - Vision *ModelCapabilitiesLimitsVision `json:"vision,omitempty"` + Vision *FluffyModelCapabilitiesLimitsVision `json:"vision,omitempty"` } // Vision-specific limits -type ModelCapabilitiesLimitsVision struct { +type FluffyModelCapabilitiesLimitsVision struct { // Maximum image size in bytes MaxPromptImageSize int64 `json:"max_prompt_image_size"` // Maximum number of images per prompt @@ -85,7 +398,7 @@ type ModelCapabilitiesLimitsVision struct { } // Feature flags indicating what the model supports -type ModelCapabilitiesSupports struct { +type CapabilitiesSupports struct { // Whether this model supports reasoning effort configuration ReasoningEffort *bool `json:"reasoningEffort,omitempty"` // Whether this model supports vision/image input @@ -147,27 +460,27 @@ type AccountQuotaSnapshot struct { type MCPConfigList struct { // All MCP servers from user config, keyed by name - Servers map[string]MCPConfigServer `json:"servers"` + Servers map[string]MCPServerConfigValue `json:"servers"` } // MCP server configuration (local/stdio or remote/http) -type MCPConfigServer struct { - Args []string `json:"args,omitempty"` - Command *string `json:"command,omitempty"` - Cwd *string `json:"cwd,omitempty"` - Env map[string]string `json:"env,omitempty"` - FilterMapping *MCPConfigFilterMapping `json:"filterMapping"` - IsDefaultServer *bool `json:"isDefaultServer,omitempty"` +type MCPServerConfigValue struct { + Args []string `json:"args,omitempty"` + Command *string `json:"command,omitempty"` + Cwd *string `json:"cwd,omitempty"` + Env map[string]string `json:"env,omitempty"` + FilterMapping *FilterMapping `json:"filterMapping"` + IsDefaultServer *bool `json:"isDefaultServer,omitempty"` // Timeout in milliseconds for tool calls to this server. Timeout *int64 `json:"timeout,omitempty"` // Tools to include. Defaults to all tools if not specified. Tools []string `json:"tools,omitempty"` // Remote transport type. Defaults to "http" when omitted. - Type *MCPConfigType `json:"type,omitempty"` - Headers map[string]string `json:"headers,omitempty"` - OauthClientID *string `json:"oauthClientId,omitempty"` - OauthPublicClient *bool `json:"oauthPublicClient,omitempty"` - URL *string `json:"url,omitempty"` + Type *MCPServerConfigType `json:"type,omitempty"` + Headers map[string]string `json:"headers,omitempty"` + OauthClientID *string `json:"oauthClientId,omitempty"` + OauthPublicClient *bool `json:"oauthPublicClient,omitempty"` + URL *string `json:"url,omitempty"` } type MCPConfigAddResult struct { @@ -175,29 +488,29 @@ type MCPConfigAddResult struct { type MCPConfigAddRequest struct { // MCP server configuration (local/stdio or remote/http) - Config MCPConfigAddConfig `json:"config"` + Config MCPConfigAddRequestMCPServerConfig `json:"config"` // Unique name for the MCP server Name string `json:"name"` } // MCP server configuration (local/stdio or remote/http) -type MCPConfigAddConfig struct { - Args []string `json:"args,omitempty"` - Command *string `json:"command,omitempty"` - Cwd *string `json:"cwd,omitempty"` - Env map[string]string `json:"env,omitempty"` - FilterMapping *MCPConfigFilterMapping `json:"filterMapping"` - IsDefaultServer *bool `json:"isDefaultServer,omitempty"` +type MCPConfigAddRequestMCPServerConfig struct { + Args []string `json:"args,omitempty"` + Command *string `json:"command,omitempty"` + Cwd *string `json:"cwd,omitempty"` + Env map[string]string `json:"env,omitempty"` + FilterMapping *FilterMapping `json:"filterMapping"` + IsDefaultServer *bool `json:"isDefaultServer,omitempty"` // Timeout in milliseconds for tool calls to this server. Timeout *int64 `json:"timeout,omitempty"` // Tools to include. Defaults to all tools if not specified. Tools []string `json:"tools,omitempty"` // Remote transport type. Defaults to "http" when omitted. - Type *MCPConfigType `json:"type,omitempty"` - Headers map[string]string `json:"headers,omitempty"` - OauthClientID *string `json:"oauthClientId,omitempty"` - OauthPublicClient *bool `json:"oauthPublicClient,omitempty"` - URL *string `json:"url,omitempty"` + Type *MCPServerConfigType `json:"type,omitempty"` + Headers map[string]string `json:"headers,omitempty"` + OauthClientID *string `json:"oauthClientId,omitempty"` + OauthPublicClient *bool `json:"oauthPublicClient,omitempty"` + URL *string `json:"url,omitempty"` } type MCPConfigUpdateResult struct { @@ -205,29 +518,29 @@ type MCPConfigUpdateResult struct { type MCPConfigUpdateRequest struct { // MCP server configuration (local/stdio or remote/http) - Config MCPConfigUpdateConfig `json:"config"` + Config MCPConfigUpdateRequestMCPServerConfig `json:"config"` // Name of the MCP server to update Name string `json:"name"` } // MCP server configuration (local/stdio or remote/http) -type MCPConfigUpdateConfig struct { - Args []string `json:"args,omitempty"` - Command *string `json:"command,omitempty"` - Cwd *string `json:"cwd,omitempty"` - Env map[string]string `json:"env,omitempty"` - FilterMapping *MCPConfigFilterMapping `json:"filterMapping"` - IsDefaultServer *bool `json:"isDefaultServer,omitempty"` +type MCPConfigUpdateRequestMCPServerConfig struct { + Args []string `json:"args,omitempty"` + Command *string `json:"command,omitempty"` + Cwd *string `json:"cwd,omitempty"` + Env map[string]string `json:"env,omitempty"` + FilterMapping *FilterMapping `json:"filterMapping"` + IsDefaultServer *bool `json:"isDefaultServer,omitempty"` // Timeout in milliseconds for tool calls to this server. Timeout *int64 `json:"timeout,omitempty"` // Tools to include. Defaults to all tools if not specified. Tools []string `json:"tools,omitempty"` // Remote transport type. Defaults to "http" when omitted. - Type *MCPConfigType `json:"type,omitempty"` - Headers map[string]string `json:"headers,omitempty"` - OauthClientID *string `json:"oauthClientId,omitempty"` - OauthPublicClient *bool `json:"oauthPublicClient,omitempty"` - URL *string `json:"url,omitempty"` + Type *MCPServerConfigType `json:"type,omitempty"` + Headers map[string]string `json:"headers,omitempty"` + OauthClientID *string `json:"oauthClientId,omitempty"` + OauthPublicClient *bool `json:"oauthPublicClient,omitempty"` + URL *string `json:"url,omitempty"` } type MCPConfigRemoveResult struct { @@ -240,10 +553,10 @@ type MCPConfigRemoveRequest struct { type MCPDiscoverResult struct { // MCP servers discovered from all sources - Servers []DiscoveredMCPServer `json:"servers"` + Servers []ServerElement `json:"servers"` } -type DiscoveredMCPServer struct { +type ServerElement struct { // Whether the server is enabled (not in the disabled list) Enabled bool `json:"enabled"` // Server name (config key) @@ -267,28 +580,6 @@ type SkillsConfigSetDisabledSkillsRequest struct { DisabledSkills []string `json:"disabledSkills"` } -type ServerSkillList struct { - // All discovered skills across all sources - Skills []ServerSkill `json:"skills"` -} - -type ServerSkill struct { - // Description of what the skill does - Description string `json:"description"` - // Whether the skill is currently enabled (based on global config) - Enabled bool `json:"enabled"` - // Unique identifier for the skill - Name string `json:"name"` - // Absolute path to the skill file - Path *string `json:"path,omitempty"` - // The project path this skill belongs to (only for project/inherited skills) - ProjectPath *string `json:"projectPath,omitempty"` - // Source location type (e.g., project, personal-copilot, plugin, builtin) - Source string `json:"source"` - // Whether the skill can be invoked by the user as a slash command - UserInvocable bool `json:"userInvocable"` -} - type SkillsDiscoverRequest struct { // Optional list of project directory paths to scan for project-scoped skills ProjectPaths []string `json:"projectPaths,omitempty"` @@ -325,11 +616,6 @@ type SessionsForkRequest struct { ToEventID *string `json:"toEventId,omitempty"` } -type CurrentModel struct { - // Currently active model identifier - ModelID *string `json:"modelId,omitempty"` -} - type ModelSwitchToResult struct { // Currently active model identifier after the switch ModelID *string `json:"modelId,omitempty"` @@ -337,7 +623,7 @@ type ModelSwitchToResult struct { type ModelSwitchToRequest struct { // Override individual model capabilities resolved by the runtime - ModelCapabilities *ModelCapabilitiesOverride `json:"modelCapabilities,omitempty"` + ModelCapabilities *ModelCapabilitiesClass `json:"modelCapabilities,omitempty"` // Model identifier to switch to ModelID string `json:"modelId"` // Reasoning effort level to use for the model @@ -345,23 +631,23 @@ type ModelSwitchToRequest struct { } // Override individual model capabilities resolved by the runtime -type ModelCapabilitiesOverride struct { +type ModelCapabilitiesClass struct { // Token limits for prompts, outputs, and context window - Limits *ModelCapabilitiesOverrideLimits `json:"limits,omitempty"` + Limits *ModelCapabilitiesLimitsClass `json:"limits,omitempty"` // Feature flags indicating what the model supports Supports *ModelCapabilitiesOverrideSupports `json:"supports,omitempty"` } // Token limits for prompts, outputs, and context window -type ModelCapabilitiesOverrideLimits struct { +type ModelCapabilitiesLimitsClass struct { // Maximum total context window size in tokens - MaxContextWindowTokens *int64 `json:"max_context_window_tokens,omitempty"` - MaxOutputTokens *int64 `json:"max_output_tokens,omitempty"` - MaxPromptTokens *int64 `json:"max_prompt_tokens,omitempty"` - Vision *ModelCapabilitiesOverrideLimitsVision `json:"vision,omitempty"` + MaxContextWindowTokens *int64 `json:"max_context_window_tokens,omitempty"` + MaxOutputTokens *int64 `json:"max_output_tokens,omitempty"` + MaxPromptTokens *int64 `json:"max_prompt_tokens,omitempty"` + Vision *FluffyModelCapabilitiesOverrideLimitsVision `json:"vision,omitempty"` } -type ModelCapabilitiesOverrideLimitsVision struct { +type FluffyModelCapabilitiesOverrideLimitsVision struct { // Maximum image size in bytes MaxPromptImageSize *int64 `json:"max_prompt_image_size,omitempty"` // Maximum number of images per prompt @@ -370,12 +656,6 @@ type ModelCapabilitiesOverrideLimitsVision struct { SupportedMediaTypes []string `json:"supported_media_types,omitempty"` } -// Feature flags indicating what the model supports -type ModelCapabilitiesOverrideSupports struct { - ReasoningEffort *bool `json:"reasoningEffort,omitempty"` - Vision *bool `json:"vision,omitempty"` -} - type ModeSetResult struct { } @@ -467,6 +747,30 @@ type WorkspacesCreateFileRequest struct { Path string `json:"path"` } +type InstructionsGetSourcesResult struct { + // Instruction sources for the session + Sources []InstructionsSources `json:"sources"` +} + +type InstructionsSources struct { + // Glob pattern from frontmatter — when set, this instruction applies only to matching files + ApplyTo *string `json:"applyTo,omitempty"` + // Raw content of the instruction file + Content string `json:"content"` + // Short description (body after frontmatter) for use in instruction tables + Description *string `json:"description,omitempty"` + // Unique identifier for this source (used for toggling) + ID string `json:"id"` + // Human-readable label + Label string `json:"label"` + // Where this source lives — used for UI grouping + Location InstructionsSourcesLocation `json:"location"` + // File path relative to repo or absolute for home + SourcePath string `json:"sourcePath"` + // Category of instruction source — used for merge logic + Type InstructionsSourcesType `json:"type"` +} + // Experimental: FleetStartResult is part of an experimental API and may change or be removed. type FleetStartResult struct { // Whether fleet mode was successfully activated @@ -482,10 +786,10 @@ type FleetStartRequest struct { // Experimental: AgentList is part of an experimental API and may change or be removed. type AgentList struct { // Available custom agents - Agents []Agent `json:"agents"` + Agents []AgentListAgent `json:"agents"` } -type Agent struct { +type AgentListAgent struct { // Description of the agent's purpose Description string `json:"description"` // Human-readable display name @@ -497,26 +801,17 @@ type Agent struct { // Experimental: AgentGetCurrentResult is part of an experimental API and may change or be removed. type AgentGetCurrentResult struct { // Currently selected custom agent, or null if using the default agent - Agent *AgentGetCurrentResultAgent `json:"agent"` -} - -type AgentGetCurrentResultAgent struct { - // Description of the agent's purpose - Description string `json:"description"` - // Human-readable display name - DisplayName string `json:"displayName"` - // Unique identifier of the custom agent - Name string `json:"name"` + Agent *AgentReloadResultAgent `json:"agent"` } // Experimental: AgentSelectResult is part of an experimental API and may change or be removed. type AgentSelectResult struct { // The newly selected custom agent - Agent AgentSelectAgent `json:"agent"` + Agent AgentSelectResultAgent `json:"agent"` } // The newly selected custom agent -type AgentSelectAgent struct { +type AgentSelectResultAgent struct { // Description of the agent's purpose Description string `json:"description"` // Human-readable display name @@ -538,10 +833,10 @@ type AgentDeselectResult struct { // Experimental: AgentReloadResult is part of an experimental API and may change or be removed. type AgentReloadResult struct { // Reloaded custom agents - Agents []AgentReloadAgent `json:"agents"` + Agents []AgentReloadResultAgent `json:"agents"` } -type AgentReloadAgent struct { +type AgentReloadResultAgent struct { // Description of the agent's purpose Description string `json:"description"` // Human-readable display name @@ -595,22 +890,6 @@ type SkillsDisableRequest struct { type SkillsReloadResult struct { } -type MCPServerList struct { - // Configured MCP servers - Servers []MCPServer `json:"servers"` -} - -type MCPServer struct { - // Error message if the server failed to connect - Error *string `json:"error,omitempty"` - // Server name (config key) - Name string `json:"name"` - // Configuration source: user, workspace, plugin, or builtin - Source *MCPServerSource `json:"source,omitempty"` - // Connection status: connected, failed, needs-auth, pending, disabled, or not_configured - Status MCPServerStatus `json:"status"` -} - type MCPEnableResult struct { } @@ -690,11 +969,6 @@ type ExtensionsDisableRequest struct { type ExtensionsReloadResult struct { } -type HandleToolCallResult struct { - // Whether the tool call result was handled successfully - Success bool `json:"success"` -} - type ToolsHandlePendingToolCallRequest struct { // Error message if the tool call failed Error *string `json:"error,omitempty"` @@ -704,17 +978,6 @@ type ToolsHandlePendingToolCallRequest struct { Result *ToolsHandlePendingToolCall `json:"result"` } -type ToolCallResult struct { - // Error message if the tool call failed - Error *string `json:"error,omitempty"` - // Type of the tool result - ResultType *string `json:"resultType,omitempty"` - // Text result to send back to the LLM - TextResultForLlm string `json:"textResultForLlm"` - // Telemetry data from tool execution - ToolTelemetry map[string]any `json:"toolTelemetry,omitempty"` -} - type CommandsHandlePendingCommandResult struct { // Whether the command was handled successfully Success bool `json:"success"` @@ -727,14 +990,6 @@ type CommandsHandlePendingCommandRequest struct { RequestID string `json:"requestId"` } -// The elicitation response (accept with form values, decline, or cancel) -type UIElicitationResponse struct { - // The user's response: accept (submitted), decline (rejected), or cancel (dismissed) - Action UIElicitationResponseAction `json:"action"` - // The form values submitted by the user (present when action is 'accept') - Content map[string]*UIElicitationFieldValue `json:"content,omitempty"` -} - type UIElicitationRequest struct { // Message describing what information is needed from the user Message string `json:"message"` @@ -759,7 +1014,7 @@ type UIElicitationSchemaProperty struct { EnumNames []string `json:"enumNames,omitempty"` Title *string `json:"title,omitempty"` Type UIElicitationSchemaPropertyNumberType `json:"type"` - OneOf []UIElicitationStringOneOfFieldOneOf `json:"oneOf,omitempty"` + OneOf []UIElicitationSchemaPropertyOneOf `json:"oneOf,omitempty"` Items *UIElicitationArrayFieldItems `json:"items,omitempty"` MaxItems *float64 `json:"maxItems,omitempty"` MinItems *float64 `json:"minItems,omitempty"` @@ -771,72 +1026,21 @@ type UIElicitationSchemaProperty struct { } type UIElicitationArrayFieldItems struct { - Enum []string `json:"enum,omitempty"` - Type *ItemsType `json:"type,omitempty"` - AnyOf []UIElicitationArrayAnyOfFieldItemsAnyOf `json:"anyOf,omitempty"` + Enum []string `json:"enum,omitempty"` + Type *UIElicitationStringEnumFieldType `json:"type,omitempty"` + AnyOf []FluffyUIElicitationArrayAnyOfFieldItemsAnyOf `json:"anyOf,omitempty"` } -type UIElicitationArrayAnyOfFieldItemsAnyOf struct { +type FluffyUIElicitationArrayAnyOfFieldItemsAnyOf struct { Const string `json:"const"` Title string `json:"title"` } -type UIElicitationStringOneOfFieldOneOf struct { +type UIElicitationSchemaPropertyOneOf struct { Const string `json:"const"` Title string `json:"title"` } -type UIElicitationResult struct { - // Whether the response was accepted. False if the request was already resolved by another - // client. - Success bool `json:"success"` -} - -type UIHandlePendingElicitationRequest struct { - // The unique request ID from the elicitation.requested event - RequestID string `json:"requestId"` - // The elicitation response (accept with form values, decline, or cancel) - Result UIElicitationResponse `json:"result"` -} - -type PermissionRequestResult struct { - // Whether the permission request was handled successfully - Success bool `json:"success"` -} - -type PermissionDecisionRequest struct { - // Request ID of the pending permission request - RequestID string `json:"requestId"` - Result PermissionDecision `json:"result"` -} - -type PermissionDecision struct { - // The permission request was approved - // - // Denied because approval rules explicitly blocked it - // - // Denied because no approval rule matched and user confirmation was unavailable - // - // Denied by the user during an interactive prompt - // - // Denied by the organization's content exclusion policy - // - // Denied by a permission request hook registered by an extension or plugin - Kind Kind `json:"kind"` - // Rules that denied the request - Rules []any `json:"rules,omitempty"` - // Optional feedback from the user explaining the denial - Feedback *string `json:"feedback,omitempty"` - // Human-readable explanation of why the path was excluded - // - // Optional message from the hook explaining the denial - Message *string `json:"message,omitempty"` - // File path that triggered the exclusion - Path *string `json:"path,omitempty"` - // Whether to interrupt the current agent turn - Interrupt *bool `json:"interrupt,omitempty"` -} - type LogResult struct { // The unique identifier of the emitted session event EventID string `json:"eventId"` @@ -1125,22 +1329,22 @@ type SessionFSRenameRequest struct { Src string `json:"src"` } -type MCPConfigFilterMappingString string +type FilterMappingString string const ( - MCPConfigFilterMappingStringHiddenCharacters MCPConfigFilterMappingString = "hidden_characters" - MCPConfigFilterMappingStringMarkdown MCPConfigFilterMappingString = "markdown" - MCPConfigFilterMappingStringNone MCPConfigFilterMappingString = "none" + FilterMappingStringHiddenCharacters FilterMappingString = "hidden_characters" + FilterMappingStringMarkdown FilterMappingString = "markdown" + FilterMappingStringNone FilterMappingString = "none" ) // Remote transport type. Defaults to "http" when omitted. -type MCPConfigType string +type MCPServerConfigType string const ( - MCPConfigTypeHTTP MCPConfigType = "http" - MCPConfigTypeLocal MCPConfigType = "local" - MCPConfigTypeSSE MCPConfigType = "sse" - MCPConfigTypeStdio MCPConfigType = "stdio" + MCPServerConfigTypeHTTP MCPServerConfigType = "http" + MCPServerConfigTypeLocal MCPServerConfigType = "local" + MCPServerConfigTypeSSE MCPServerConfigType = "sse" + MCPServerConfigTypeStdio MCPServerConfigType = "stdio" ) // Configuration source @@ -1165,6 +1369,50 @@ const ( DiscoveredMCPServerTypeMemory DiscoveredMCPServerType = "memory" ) +// Connection status: connected, failed, needs-auth, pending, disabled, or not_configured +type MCPServerStatus string + +const ( + MCPServerStatusConnected MCPServerStatus = "connected" + MCPServerStatusDisabled MCPServerStatus = "disabled" + MCPServerStatusFailed MCPServerStatus = "failed" + MCPServerStatusNeedsAuth MCPServerStatus = "needs-auth" + MCPServerStatusNotConfigured MCPServerStatus = "not_configured" + MCPServerStatusPending MCPServerStatus = "pending" +) + +type UIElicitationStringEnumFieldType string + +const ( + UIElicitationStringEnumFieldTypeString UIElicitationStringEnumFieldType = "string" +) + +type UIElicitationArrayEnumFieldType string + +const ( + UIElicitationArrayEnumFieldTypeArray UIElicitationArrayEnumFieldType = "array" +) + +// The user's response: accept (submitted), decline (rejected), or cancel (dismissed) +type UIElicitationResponseAction string + +const ( + UIElicitationResponseActionAccept UIElicitationResponseAction = "accept" + UIElicitationResponseActionCancel UIElicitationResponseAction = "cancel" + UIElicitationResponseActionDecline UIElicitationResponseAction = "decline" +) + +type Kind string + +const ( + KindApproved Kind = "approved" + KindDeniedByContentExclusionPolicy Kind = "denied-by-content-exclusion-policy" + KindDeniedByPermissionRequestHook Kind = "denied-by-permission-request-hook" + KindDeniedByRules Kind = "denied-by-rules" + KindDeniedInteractivelyByUser Kind = "denied-interactively-by-user" + KindDeniedNoApprovalRuleAndCouldNotRequestFromUser Kind = "denied-no-approval-rule-and-could-not-request-from-user" +) + // Path conventions used by this filesystem type SessionFSSetProviderConventions string @@ -1197,16 +1445,25 @@ const ( SessionSyncLevelUser SessionSyncLevel = "user" ) -// Connection status: connected, failed, needs-auth, pending, disabled, or not_configured -type MCPServerStatus string +// Where this source lives — used for UI grouping +type InstructionsSourcesLocation string const ( - MCPServerStatusConnected MCPServerStatus = "connected" - MCPServerStatusDisabled MCPServerStatus = "disabled" - MCPServerStatusFailed MCPServerStatus = "failed" - MCPServerStatusNeedsAuth MCPServerStatus = "needs-auth" - MCPServerStatusNotConfigured MCPServerStatus = "not_configured" - MCPServerStatusPending MCPServerStatus = "pending" + InstructionsSourcesLocationUser InstructionsSourcesLocation = "user" + InstructionsSourcesLocationRepository InstructionsSourcesLocation = "repository" + InstructionsSourcesLocationWorkingDirectory InstructionsSourcesLocation = "working-directory" +) + +// Category of instruction source — used for merge logic +type InstructionsSourcesType string + +const ( + InstructionsSourcesTypeChildInstructions InstructionsSourcesType = "child-instructions" + InstructionsSourcesTypeHome InstructionsSourcesType = "home" + InstructionsSourcesTypeModel InstructionsSourcesType = "model" + InstructionsSourcesTypeNestedAgents InstructionsSourcesType = "nested-agents" + InstructionsSourcesTypeRepo InstructionsSourcesType = "repo" + InstructionsSourcesTypeVscode InstructionsSourcesType = "vscode" ) // Discovery source: project (.github/extensions/) or user (~/.copilot/extensions/) @@ -1227,15 +1484,6 @@ const ( ExtensionStatusStarting ExtensionStatus = "starting" ) -// The user's response: accept (submitted), decline (rejected), or cancel (dismissed) -type UIElicitationResponseAction string - -const ( - UIElicitationResponseActionAccept UIElicitationResponseAction = "accept" - UIElicitationResponseActionCancel UIElicitationResponseAction = "cancel" - UIElicitationResponseActionDecline UIElicitationResponseAction = "decline" -) - type UIElicitationSchemaPropertyStringFormat string const ( @@ -1245,19 +1493,13 @@ const ( UIElicitationSchemaPropertyStringFormatURI UIElicitationSchemaPropertyStringFormat = "uri" ) -type ItemsType string - -const ( - ItemsTypeString ItemsType = "string" -) - type UIElicitationSchemaPropertyNumberType string const ( - UIElicitationSchemaPropertyNumberTypeArray UIElicitationSchemaPropertyNumberType = "array" UIElicitationSchemaPropertyNumberTypeBoolean UIElicitationSchemaPropertyNumberType = "boolean" UIElicitationSchemaPropertyNumberTypeInteger UIElicitationSchemaPropertyNumberType = "integer" UIElicitationSchemaPropertyNumberTypeNumber UIElicitationSchemaPropertyNumberType = "number" + UIElicitationSchemaPropertyNumberTypeArray UIElicitationSchemaPropertyNumberType = "array" UIElicitationSchemaPropertyNumberTypeString UIElicitationSchemaPropertyNumberType = "string" ) @@ -1267,17 +1509,6 @@ const ( RequestedSchemaTypeObject RequestedSchemaType = "object" ) -type Kind string - -const ( - KindApproved Kind = "approved" - KindDeniedByContentExclusionPolicy Kind = "denied-by-content-exclusion-policy" - KindDeniedByPermissionRequestHook Kind = "denied-by-permission-request-hook" - KindDeniedByRules Kind = "denied-by-rules" - KindDeniedInteractivelyByUser Kind = "denied-interactively-by-user" - KindDeniedNoApprovalRuleAndCouldNotRequestFromUser Kind = "denied-no-approval-rule-and-could-not-request-from-user" -) - // Log severity level. Determines how the message is displayed in the timeline. Defaults to // "info". type SessionLogLevel string @@ -1305,15 +1536,9 @@ const ( SessionFSReaddirWithTypesEntryTypeFile SessionFSReaddirWithTypesEntryType = "file" ) -type MCPConfigFilterMapping struct { - Enum *MCPConfigFilterMappingString - EnumMap map[string]MCPConfigFilterMappingString -} - -// Tool call result (string or expanded result object) -type ToolsHandlePendingToolCall struct { - String *string - ToolCallResult *ToolCallResult +type FilterMapping struct { + Enum *FilterMappingString + EnumMap map[string]FilterMappingString } type UIElicitationFieldValue struct { @@ -1323,6 +1548,12 @@ type UIElicitationFieldValue struct { StringArray []string } +// Tool call result (string or expanded result object) +type ToolsHandlePendingToolCall struct { + String *string + ToolCallResult *ToolCallResult +} + type serverApi struct { client *jsonrpc2.Client } @@ -1745,6 +1976,21 @@ func (a *WorkspacesApi) CreateFile(ctx context.Context, params *WorkspacesCreate return &result, nil } +type InstructionsApi sessionApi + +func (a *InstructionsApi) GetSources(ctx context.Context) (*InstructionsGetSourcesResult, error) { + req := map[string]any{"sessionId": a.sessionID} + raw, err := a.client.Request("session.instructions.getSources", req) + if err != nil { + return nil, err + } + var result InstructionsGetSourcesResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // Experimental: FleetApi contains experimental APIs that may change or be removed. type FleetApi sessionApi @@ -2231,24 +2477,25 @@ func (a *UsageApi) GetMetrics(ctx context.Context) (*UsageGetMetricsResult, erro type SessionRpc struct { common sessionApi // Reuse a single struct instead of allocating one for each service on the heap. - Model *ModelApi - Mode *ModeApi - Name *NameApi - Plan *PlanApi - Workspaces *WorkspacesApi - Fleet *FleetApi - Agent *AgentApi - Skills *SkillsApi - Mcp *McpApi - Plugins *PluginsApi - Extensions *ExtensionsApi - Tools *ToolsApi - Commands *CommandsApi - UI *UIApi - Permissions *PermissionsApi - Shell *ShellApi - History *HistoryApi - Usage *UsageApi + Model *ModelApi + Mode *ModeApi + Name *NameApi + Plan *PlanApi + Workspaces *WorkspacesApi + Instructions *InstructionsApi + Fleet *FleetApi + Agent *AgentApi + Skills *SkillsApi + Mcp *McpApi + Plugins *PluginsApi + Extensions *ExtensionsApi + Tools *ToolsApi + Commands *CommandsApi + UI *UIApi + Permissions *PermissionsApi + Shell *ShellApi + History *HistoryApi + Usage *UsageApi } func (a *SessionRpc) Log(ctx context.Context, params *LogRequest) (*LogResult, error) { @@ -2284,6 +2531,7 @@ func NewSessionRpc(client *jsonrpc2.Client, sessionID string) *SessionRpc { r.Name = (*NameApi)(&r.common) r.Plan = (*PlanApi)(&r.common) r.Workspaces = (*WorkspacesApi)(&r.common) + r.Instructions = (*InstructionsApi)(&r.common) r.Fleet = (*FleetApi)(&r.common) r.Agent = (*AgentApi)(&r.common) r.Skills = (*SkillsApi)(&r.common) diff --git a/go/session.go b/go/session.go index be8c78e2b..bf42bf03a 100644 --- a/go/session.go +++ b/go/session.go @@ -1213,7 +1213,7 @@ func (s *Session) SetModel(ctx context.Context, model string, opts *SetModelOpti params := &rpc.ModelSwitchToRequest{ModelID: model} if opts != nil { params.ReasoningEffort = opts.ReasoningEffort - params.ModelCapabilities = opts.ModelCapabilities + params.ModelCapabilities = convertModelCapabilitiesToClass(opts.ModelCapabilities) } _, err := s.RPC.Model.SwitchTo(ctx, params) if err != nil { @@ -1223,7 +1223,34 @@ func (s *Session) SetModel(ctx context.Context, model string, opts *SetModelOpti return nil } -// LogOptions configures optional parameters for [Session.Log]. +// convertModelCapabilitiesToClass converts from ModelCapabilitiesOverride +// (used in the public API) to ModelCapabilitiesClass (used internally by +// the ModelSwitchToRequest RPC). The two types are structurally identical +// but have different Go types due to code generation. +func convertModelCapabilitiesToClass(src *rpc.ModelCapabilitiesOverride) *rpc.ModelCapabilitiesClass { + if src == nil { + return nil + } + dst := &rpc.ModelCapabilitiesClass{ + Supports: src.Supports, + } + if src.Limits != nil { + dst.Limits = &rpc.ModelCapabilitiesLimitsClass{ + MaxContextWindowTokens: src.Limits.MaxContextWindowTokens, + MaxOutputTokens: src.Limits.MaxOutputTokens, + MaxPromptTokens: src.Limits.MaxPromptTokens, + } + if src.Limits.Vision != nil { + dst.Limits.Vision = &rpc.FluffyModelCapabilitiesOverrideLimitsVision{ + MaxPromptImageSize: src.Limits.Vision.MaxPromptImageSize, + MaxPromptImages: src.Limits.Vision.MaxPromptImages, + SupportedMediaTypes: src.Limits.Vision.SupportedMediaTypes, + } + } + } + return dst +} + type LogOptions struct { // Level sets the log severity. Valid values are [rpc.SessionLogLevelInfo] (default), // [rpc.SessionLogLevelWarning], and [rpc.SessionLogLevelError]. diff --git a/go/types.go b/go/types.go index f889d3e2a..15c62cec0 100644 --- a/go/types.go +++ b/go/types.go @@ -848,7 +848,7 @@ type ( ModelCapabilitiesOverride = rpc.ModelCapabilitiesOverride ModelCapabilitiesOverrideSupports = rpc.ModelCapabilitiesOverrideSupports ModelCapabilitiesOverrideLimits = rpc.ModelCapabilitiesOverrideLimits - ModelCapabilitiesOverrideLimitsVision = rpc.ModelCapabilitiesOverrideLimitsVision + ModelCapabilitiesOverrideLimitsVision = rpc.PurpleModelCapabilitiesOverrideLimitsVision ) // ModelPolicy contains model policy state diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index 002edfbf3..9ccf85c04 100644 --- a/nodejs/package-lock.json +++ b/nodejs/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.8", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.30", + "@github/copilot": "^1.0.32-1", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" }, @@ -663,26 +663,26 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.30", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.30.tgz", - "integrity": "sha512-JYZNMM6hteAE6tIMbHobRjpAaXzvqeeglXgGlDCr26rRq3K6h5ul2GN27qzhMBaWyujUQN402KLKdrhDPqcL7A==", + "version": "1.0.32-1", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.32-1.tgz", + "integrity": "sha512-uJgZWkd+gYS6t8NeWgZd+KDlQ41RFvAydOPdJqMDdB8aBwJYKQA75AVQzJyIne/CaMmv2Cy24X+IeRsMXvg+YA==", "license": "SEE LICENSE IN LICENSE.md", "bin": { "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.30", - "@github/copilot-darwin-x64": "1.0.30", - "@github/copilot-linux-arm64": "1.0.30", - "@github/copilot-linux-x64": "1.0.30", - "@github/copilot-win32-arm64": "1.0.30", - "@github/copilot-win32-x64": "1.0.30" + "@github/copilot-darwin-arm64": "1.0.32-1", + "@github/copilot-darwin-x64": "1.0.32-1", + "@github/copilot-linux-arm64": "1.0.32-1", + "@github/copilot-linux-x64": "1.0.32-1", + "@github/copilot-win32-arm64": "1.0.32-1", + "@github/copilot-win32-x64": "1.0.32-1" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.30", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.30.tgz", - "integrity": "sha512-qhLMhAY7nskG6yabbsWSqErxPWcZLX1ixJBdQX3RLqgw5dyNvZRNzG2evUnABo5bqgndztsFXjE3u4XtfX0WkA==", + "version": "1.0.32-1", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.32-1.tgz", + "integrity": "sha512-MGz9kKJYqrfZ94DOVsKy8c0sTFn1Gax60hM3TjMt6K+Tt7n8vGhrpBn+KjFYOb+6+r7fp3E7fc6tTtwjgaURVw==", "cpu": [ "arm64" ], @@ -696,9 +696,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.30", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.30.tgz", - "integrity": "sha512-nsjGRt1jLBzCaVd6eb3ok75zqePr8eU8GSTqu1KVf5KUrnvvfIlsvESkEAE8l+lkR14f7SGQLfMJ2EEbcJMGcg==", + "version": "1.0.32-1", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.32-1.tgz", + "integrity": "sha512-HSLJXMVk2yf6Xb6NhNxEYvD57hBGdWs5zQ7EOHrFYO+qA5/iD4JVGgQNg7sS88+qsTR5PtEcxwbtQPid1KZJnQ==", "cpu": [ "x64" ], @@ -712,9 +712,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.30", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.30.tgz", - "integrity": "sha512-7wOrOKm9MHnglyzzGeZnXSkfRi4sXB2Db7rK/CgUenxS+dwwIuXhT4rgkH/DIOiDbGCxYjigICxln28Jvbs+cA==", + "version": "1.0.32-1", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.32-1.tgz", + "integrity": "sha512-XBiX4947+ygPugwsZrrVOwftIWWASoknq1FzehIpj7BqPxjwTpzDXPDJNleHf+6a1cGm8cUutDn/wslHjJEW9A==", "cpu": [ "arm64" ], @@ -728,9 +728,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.30", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.30.tgz", - "integrity": "sha512-OSJtP7mV9vnDzGFjBkI3sgbNOcxsRcq7vXrT4PNrjJw4Mc71aaW55hc5F1j2fElfGWIb+Jubm3AB8nb6AoufnA==", + "version": "1.0.32-1", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.32-1.tgz", + "integrity": "sha512-iJkcWKSoaDY5GKtOZtoZV5YhuOqvVSdENashNKjXzkIoFN0mqonIhsbAv3OB2Kr34ZwoQF3CfNoOCNBs2tg8pg==", "cpu": [ "x64" ], @@ -744,9 +744,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.30", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.30.tgz", - "integrity": "sha512-5nCz/+9VWJdNvW2uRYeMmnRdQq/gpuSlmYMvRv8fIsFF8KH0mdJndJn8xN6GeJtx0fKJrLzgKqJHWdgb5MtLgA==", + "version": "1.0.32-1", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.32-1.tgz", + "integrity": "sha512-U/lfmWAqOIxucqotmsOsJtOjfAhNIYAFeqxyaKo+V35YkurXZGTNjB2YxqUlmKm/7fuOgAACHKvrK+tWs+Mlvg==", "cpu": [ "arm64" ], @@ -760,9 +760,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.30", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.30.tgz", - "integrity": "sha512-tJvgCsWLJVQvHLvFyQZ0P5MQ7YGX51/bl9kbXDUFCGATtPpELul3NyHWwEYGjRv+VDPvhFxjbf+V7Bf/VzYZ7w==", + "version": "1.0.32-1", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.32-1.tgz", + "integrity": "sha512-oSNG9nRHsyTdi2miBfti4egT+CHPGu0QTXXUasISsfwhex6SS4qeVFe8mt8/clnTlyJD9N7EDgABDduSYQv87g==", "cpu": [ "x64" ], diff --git a/nodejs/package.json b/nodejs/package.json index 7576406df..2ccb7632c 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -56,7 +56,7 @@ "author": "GitHub", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.30", + "@github/copilot": "^1.0.32-1", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" }, diff --git a/nodejs/samples/package-lock.json b/nodejs/samples/package-lock.json index 574f9878b..7281be70f 100644 --- a/nodejs/samples/package-lock.json +++ b/nodejs/samples/package-lock.json @@ -18,7 +18,7 @@ "version": "0.1.8", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.30", + "@github/copilot": "^1.0.32-1", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" }, diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index c5b84a6d4..a3d50d5ff 100644 --- a/nodejs/src/client.ts +++ b/nodejs/src/client.ts @@ -1019,6 +1019,26 @@ export class CopilotClient { const result = await this.connection.sendRequest("models.list", {}); const response = result as { models: ModelInfo[] }; models = response.models; + + // Normalize model capabilities — some models (e.g. embedding models) + // may omit 'supports' or 'limits' in their capabilities. + for (const model of models) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const m = model as any; + if (!m.capabilities) { + m.capabilities = { + supports: {}, + limits: { max_context_window_tokens: 0 }, + }; + } else { + if (!m.capabilities.supports) m.capabilities.supports = {}; + if (!m.capabilities.limits) { + m.capabilities.limits = { max_context_window_tokens: 0 }; + } else if (m.capabilities.limits.max_context_window_tokens === undefined) { + m.capabilities.limits.max_context_window_tokens = 0; + } + } + } } // Update cache before releasing lock (copy to prevent external mutation) diff --git a/nodejs/src/generated/rpc.ts b/nodejs/src/generated/rpc.ts index ff60d2534..dedfa8068 100644 --- a/nodejs/src/generated/rpc.ts +++ b/nodejs/src/generated/rpc.ts @@ -5,6 +5,60 @@ import type { MessageConnection } from "vscode-jsonrpc/node.js"; +/** + * MCP server configuration (local/stdio or remote/http) + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "McpServerConfig". + */ +export type McpServerConfig = + | { + /** + * Tools to include. Defaults to all tools if not specified. + */ + tools?: string[]; + type?: "local" | "stdio"; + isDefaultServer?: boolean; + filterMapping?: FilterMapping; + /** + * Timeout in milliseconds for tool calls to this server. + */ + timeout?: number; + command: string; + args: string[]; + cwd?: string; + env?: { + [k: string]: string; + }; + } + | { + /** + * Tools to include. Defaults to all tools if not specified. + */ + tools?: string[]; + /** + * Remote transport type. Defaults to "http" when omitted. + */ + type?: "http" | "sse"; + isDefaultServer?: boolean; + filterMapping?: FilterMapping; + /** + * Timeout in milliseconds for tool calls to this server. + */ + timeout?: number; + url: string; + headers?: { + [k: string]: string; + }; + oauthClientId?: string; + oauthPublicClient?: boolean; + }; + +export type FilterMapping = + | { + [k: string]: "none" | "markdown" | "hidden_characters"; + } + | ("none" | "markdown" | "hidden_characters"); /** * The agent mode. Valid values: "interactive", "plan", "autopilot". * @@ -12,11 +66,16 @@ import type { MessageConnection } from "vscode-jsonrpc/node.js"; * via the `definition` "SessionMode". */ export type SessionMode = "interactive" | "plan" | "autopilot"; + +export type UIElicitationFieldValue = string | number | boolean | string[]; /** * The user's response: accept (submitted), decline (rejected), or cancel (dismissed) + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UIElicitationResponseAction". */ export type UIElicitationResponseAction = "accept" | "decline" | "cancel"; -export type UIElicitationFieldValue = string | number | boolean | string[]; + export type PermissionDecision = | { /** @@ -66,22 +125,523 @@ export type PermissionDecision = } | { /** - * Denied by a permission request hook registered by an extension or plugin + * Denied by a permission request hook registered by an extension or plugin + */ + kind: "denied-by-permission-request-hook"; + /** + * Optional message from the hook explaining the denial + */ + message?: string; + /** + * Whether to interrupt the current agent turn + */ + interrupt?: boolean; + }; +/** + * Log severity level. Determines how the message is displayed in the timeline. Defaults to "info". + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SessionLogLevel". + */ +export type SessionLogLevel = "info" | "warning" | "error"; +/** + * MCP server configuration (local/stdio or remote/http) + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "$defs_McpServerConfig". + */ +export type $Defs_McpServerConfig = + | { + /** + * Tools to include. Defaults to all tools if not specified. + */ + tools?: string[]; + type?: "local" | "stdio"; + isDefaultServer?: boolean; + filterMapping?: FilterMapping; + /** + * Timeout in milliseconds for tool calls to this server. + */ + timeout?: number; + command: string; + args: string[]; + cwd?: string; + env?: { + [k: string]: string; + }; + } + | { + /** + * Tools to include. Defaults to all tools if not specified. + */ + tools?: string[]; + /** + * Remote transport type. Defaults to "http" when omitted. + */ + type?: "http" | "sse"; + isDefaultServer?: boolean; + filterMapping?: FilterMapping; + /** + * Timeout in milliseconds for tool calls to this server. + */ + timeout?: number; + url: string; + headers?: { + [k: string]: string; + }; + oauthClientId?: string; + oauthPublicClient?: boolean; + }; + +export type $Defs_FilterMapping = + | { + [k: string]: "none" | "markdown" | "hidden_characters"; + } + | ("none" | "markdown" | "hidden_characters"); +/** + * The agent mode. Valid values: "interactive", "plan", "autopilot". + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "$defs_SessionMode". + */ +export type $Defs_SessionMode = "interactive" | "plan" | "autopilot"; +/** + * The user's response: accept (submitted), decline (rejected), or cancel (dismissed) + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "$defs_UIElicitationResponseAction". + */ +export type $Defs_UIElicitationResponseAction = "accept" | "decline" | "cancel"; + +export type $Defs_UIElicitationFieldValue = string | number | boolean | string[]; + +export type $Defs_PermissionDecision = + | { + /** + * The permission request was approved + */ + kind: "approved"; + } + | { + /** + * Denied because approval rules explicitly blocked it + */ + kind: "denied-by-rules"; + /** + * Rules that denied the request + */ + rules: unknown[]; + } + | { + /** + * Denied because no approval rule matched and user confirmation was unavailable + */ + kind: "denied-no-approval-rule-and-could-not-request-from-user"; + } + | { + /** + * Denied by the user during an interactive prompt + */ + kind: "denied-interactively-by-user"; + /** + * Optional feedback from the user explaining the denial + */ + feedback?: string; + } + | { + /** + * Denied by the organization's content exclusion policy + */ + kind: "denied-by-content-exclusion-policy"; + /** + * File path that triggered the exclusion + */ + path: string; + /** + * Human-readable explanation of why the path was excluded + */ + message: string; + } + | { + /** + * Denied by a permission request hook registered by an extension or plugin + */ + kind: "denied-by-permission-request-hook"; + /** + * Optional message from the hook explaining the denial + */ + message?: string; + /** + * Whether to interrupt the current agent turn + */ + interrupt?: boolean; + }; +/** + * Log severity level. Determines how the message is displayed in the timeline. Defaults to "info". + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "$defs_SessionLogLevel". + */ +export type $Defs_SessionLogLevel = "info" | "warning" | "error"; + +/** + * Model capabilities and limits + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ModelCapabilities". + */ +export interface ModelCapabilities { + /** + * Feature flags indicating what the model supports + */ + supports?: { + /** + * Whether this model supports vision/image input + */ + vision?: boolean; + /** + * Whether this model supports reasoning effort configuration + */ + reasoningEffort?: boolean; + }; + /** + * Token limits for prompts, outputs, and context window + */ + limits?: { + /** + * Maximum number of prompt/input tokens + */ + max_prompt_tokens?: number; + /** + * Maximum number of output/completion tokens + */ + max_output_tokens?: number; + /** + * Maximum total context window size in tokens + */ + max_context_window_tokens?: number; + vision?: ModelCapabilitiesLimitsVision; + }; +} +/** + * Vision-specific limits + */ +export interface ModelCapabilitiesLimitsVision { + /** + * MIME types the model accepts + */ + supported_media_types: string[]; + /** + * Maximum number of images per prompt + */ + max_prompt_images: number; + /** + * Maximum image size in bytes + */ + max_prompt_image_size: number; +} +/** + * Vision-specific limits + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ModelCapabilitiesLimitsVision". + */ +export interface ModelCapabilitiesLimitsVision1 { + /** + * MIME types the model accepts + */ + supported_media_types: string[]; + /** + * Maximum number of images per prompt + */ + max_prompt_images: number; + /** + * Maximum image size in bytes + */ + max_prompt_image_size: number; +} + +export interface DiscoveredMcpServer { + /** + * Server name (config key) + */ + name: string; + /** + * Server transport type: stdio, http, sse, or memory (local configs are normalized to stdio) + */ + type?: "stdio" | "http" | "sse" | "memory"; + /** + * Configuration source + */ + source: "user" | "workspace" | "plugin" | "builtin"; + /** + * Whether the server is enabled (not in the disabled list) + */ + enabled: boolean; +} + +export interface ServerSkillList { + /** + * All discovered skills across all sources + */ + skills: ServerSkill[]; +} + +export interface ServerSkill { + /** + * Unique identifier for the skill + */ + name: string; + /** + * Description of what the skill does + */ + description: string; + /** + * Source location type (e.g., project, personal-copilot, plugin, builtin) + */ + source: string; + /** + * Whether the skill can be invoked by the user as a slash command + */ + userInvocable: boolean; + /** + * Whether the skill is currently enabled (based on global config) + */ + enabled: boolean; + /** + * Absolute path to the skill file + */ + path?: string; + /** + * The project path this skill belongs to (only for project/inherited skills) + */ + projectPath?: string; +} + +export interface CurrentModel { + /** + * Currently active model identifier + */ + modelId?: string; +} +/** + * Override individual model capabilities resolved by the runtime + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ModelCapabilitiesOverride". + */ +export interface ModelCapabilitiesOverride { + /** + * Feature flags indicating what the model supports + */ + supports?: { + vision?: boolean; + reasoningEffort?: boolean; + }; + /** + * Token limits for prompts, outputs, and context window + */ + limits?: { + max_prompt_tokens?: number; + max_output_tokens?: number; + /** + * Maximum total context window size in tokens + */ + max_context_window_tokens?: number; + vision?: { + /** + * MIME types the model accepts */ - kind: "denied-by-permission-request-hook"; + supported_media_types?: string[]; /** - * Optional message from the hook explaining the denial + * Maximum number of images per prompt */ - message?: string; + max_prompt_images?: number; /** - * Whether to interrupt the current agent turn + * Maximum image size in bytes */ - interrupt?: boolean; + max_prompt_image_size?: number; }; + }; +} + +export interface AgentInfo { + /** + * Unique identifier of the custom agent + */ + name: string; + /** + * Human-readable display name + */ + displayName: string; + /** + * Description of the agent's purpose + */ + description: string; +} + +/** @experimental */ +export interface McpServerList { + /** + * Configured MCP servers + */ + servers: { + /** + * Server name (config key) + */ + name: string; + /** + * Connection status: connected, failed, needs-auth, pending, disabled, or not_configured + */ + status: "connected" | "failed" | "needs-auth" | "pending" | "disabled" | "not_configured"; + /** + * Configuration source: user, workspace, plugin, or builtin + */ + source?: "user" | "workspace" | "plugin" | "builtin"; + /** + * Error message if the server failed to connect + */ + error?: string; + }[]; +} + +export interface ToolCallResult { + /** + * Text result to send back to the LLM + */ + textResultForLlm: string; + /** + * Type of the tool result + */ + resultType?: string; + /** + * Error message if the tool call failed + */ + error?: string; + /** + * Telemetry data from tool execution + */ + toolTelemetry?: { + [k: string]: unknown; + }; +} + +export interface HandleToolCallResult { + /** + * Whether the tool call result was handled successfully + */ + success: boolean; +} + +export interface UIElicitationStringEnumField { + type: "string"; + description?: string; + enum: string[]; + enumNames?: string[]; + default?: string; +} + +export interface UIElicitationStringOneOfField { + type: "string"; + description?: string; + oneOf: { + const: string; + }[]; + default?: string; +} + +export interface UIElicitationArrayEnumField { + type: "array"; + description?: string; + minItems?: number; + maxItems?: number; + items: { + type: "string"; + enum: string[]; + }; + default?: string[]; +} + +export interface UIElicitationArrayAnyOfField { + type: "array"; + description?: string; + minItems?: number; + maxItems?: number; + items: { + anyOf: { + const: string; + }[]; + }; + default?: string[]; +} /** - * Log severity level. Determines how the message is displayed in the timeline. Defaults to "info". + * The elicitation response (accept with form values, decline, or cancel) + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UIElicitationResponse". */ -export type SessionLogLevel = "info" | "warning" | "error"; +export interface UIElicitationResponse { + /** + * The user's response: accept (submitted), decline (rejected), or cancel (dismissed) + */ + action: "accept" | "decline" | "cancel"; + content?: UIElicitationResponseContent; +} +/** + * The form values submitted by the user (present when action is 'accept') + */ +export interface UIElicitationResponseContent { + [k: string]: UIElicitationFieldValue; +} +/** + * The form values submitted by the user (present when action is 'accept') + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "UIElicitationResponseContent". + */ +export interface UIElicitationResponseContent1 { + [k: string]: UIElicitationFieldValue; +} + +export interface UIHandlePendingElicitationRequest { + /** + * The unique request ID from the elicitation.requested event + */ + requestId: string; + result: UIElicitationResponse1; +} +/** + * The elicitation response (accept with form values, decline, or cancel) + */ +export interface UIElicitationResponse1 { + /** + * The user's response: accept (submitted), decline (rejected), or cancel (dismissed) + */ + action: "accept" | "decline" | "cancel"; + content?: UIElicitationResponseContent; +} + +export interface UIElicitationResult { + /** + * Whether the response was accepted. False if the request was already resolved by another client. + */ + success: boolean; +} + +export interface PermissionDecisionRequest { + /** + * Request ID of the pending permission request + */ + requestId: string; + result: PermissionDecision; +} + +export interface PermissionRequestResult { + /** + * Whether the permission request was handled successfully + */ + success: boolean; +} export interface PingResult { /** @@ -118,7 +678,7 @@ export interface ModelList { * Display name */ name: string; - capabilities: ModelCapabilities; + capabilities: ModelCapabilities1; /** * Policy state (if applicable) */ @@ -154,7 +714,7 @@ export interface ModelList { /** * Model capabilities and limits */ -export interface ModelCapabilities { +export interface ModelCapabilities1 { /** * Feature flags indicating what the model supports */ @@ -184,23 +744,7 @@ export interface ModelCapabilities { * Maximum total context window size in tokens */ max_context_window_tokens?: number; - /** - * Vision-specific limits - */ - vision?: { - /** - * MIME types the model accepts - */ - supported_media_types: string[]; - /** - * Maximum number of images per prompt - */ - max_prompt_images: number; - /** - * Maximum image size in bytes - */ - max_prompt_image_size: number; - }; + vision?: ModelCapabilitiesLimitsVision; }; } @@ -291,11 +835,7 @@ export interface McpConfigList { tools?: string[]; type?: "local" | "stdio"; isDefaultServer?: boolean; - filterMapping?: - | { - [k: string]: "none" | "markdown" | "hidden_characters"; - } - | ("none" | "markdown" | "hidden_characters"); + filterMapping?: FilterMapping; /** * Timeout in milliseconds for tool calls to this server. */ @@ -317,11 +857,7 @@ export interface McpConfigList { */ type?: "http" | "sse"; isDefaultServer?: boolean; - filterMapping?: - | { - [k: string]: "none" | "markdown" | "hidden_characters"; - } - | ("none" | "markdown" | "hidden_characters"); + filterMapping?: FilterMapping; /** * Timeout in milliseconds for tool calls to this server. */ @@ -352,11 +888,7 @@ export interface McpConfigAddRequest { tools?: string[]; type?: "local" | "stdio"; isDefaultServer?: boolean; - filterMapping?: - | { - [k: string]: "none" | "markdown" | "hidden_characters"; - } - | ("none" | "markdown" | "hidden_characters"); + filterMapping?: FilterMapping; /** * Timeout in milliseconds for tool calls to this server. */ @@ -378,11 +910,7 @@ export interface McpConfigAddRequest { */ type?: "http" | "sse"; isDefaultServer?: boolean; - filterMapping?: - | { - [k: string]: "none" | "markdown" | "hidden_characters"; - } - | ("none" | "markdown" | "hidden_characters"); + filterMapping?: FilterMapping; /** * Timeout in milliseconds for tool calls to this server. */ @@ -412,11 +940,7 @@ export interface McpConfigUpdateRequest { tools?: string[]; type?: "local" | "stdio"; isDefaultServer?: boolean; - filterMapping?: - | { - [k: string]: "none" | "markdown" | "hidden_characters"; - } - | ("none" | "markdown" | "hidden_characters"); + filterMapping?: FilterMapping; /** * Timeout in milliseconds for tool calls to this server. */ @@ -438,11 +962,7 @@ export interface McpConfigUpdateRequest { */ type?: "http" | "sse"; isDefaultServer?: boolean; - filterMapping?: - | { - [k: string]: "none" | "markdown" | "hidden_characters"; - } - | ("none" | "markdown" | "hidden_characters"); + filterMapping?: FilterMapping; /** * Timeout in milliseconds for tool calls to this server. */ @@ -465,78 +985,23 @@ export interface McpConfigRemoveRequest { export interface McpDiscoverResult { /** - * MCP servers discovered from all sources - */ - servers: DiscoveredMcpServer[]; -} -export interface DiscoveredMcpServer { - /** - * Server name (config key) - */ - name: string; - /** - * Server transport type: stdio, http, sse, or memory (local configs are normalized to stdio) - */ - type?: "stdio" | "http" | "sse" | "memory"; - /** - * Configuration source - */ - source: "user" | "workspace" | "plugin" | "builtin"; - /** - * Whether the server is enabled (not in the disabled list) - */ - enabled: boolean; -} - -export interface McpDiscoverRequest { - /** - * Working directory used as context for discovery (e.g., plugin resolution) - */ - workingDirectory?: string; -} - -export interface SkillsConfigSetDisabledSkillsRequest { - /** - * List of skill names to disable - */ - disabledSkills: string[]; -} - -export interface ServerSkillList { - /** - * All discovered skills across all sources - */ - skills: ServerSkill[]; -} -export interface ServerSkill { - /** - * Unique identifier for the skill - */ - name: string; - /** - * Description of what the skill does - */ - description: string; - /** - * Source location type (e.g., project, personal-copilot, plugin, builtin) - */ - source: string; - /** - * Whether the skill can be invoked by the user as a slash command - */ - userInvocable: boolean; - /** - * Whether the skill is currently enabled (based on global config) + * MCP servers discovered from all sources */ - enabled: boolean; + servers: DiscoveredMcpServer[]; +} + +export interface McpDiscoverRequest { /** - * Absolute path to the skill file + * Working directory used as context for discovery (e.g., plugin resolution) */ - path?: string; + workingDirectory?: string; +} + +export interface SkillsConfigSetDisabledSkillsRequest { /** - * The project path this skill belongs to (only for project/inherited skills) + * List of skill names to disable */ - projectPath?: string; + disabledSkills: string[]; } export interface SkillsDiscoverRequest { @@ -592,13 +1057,6 @@ export interface SessionsForkRequest { toEventId?: string; } -export interface CurrentModel { - /** - * Currently active model identifier - */ - modelId?: string; -} - export interface ModelSwitchToResult { /** * Currently active model identifier after the switch @@ -615,12 +1073,12 @@ export interface ModelSwitchToRequest { * Reasoning effort level to use for the model */ reasoningEffort?: string; - modelCapabilities?: ModelCapabilitiesOverride; + modelCapabilities?: ModelCapabilitiesOverride1; } /** * Override individual model capabilities resolved by the runtime */ -export interface ModelCapabilitiesOverride { +export interface ModelCapabilitiesOverride1 { /** * Feature flags indicating what the model supports */ @@ -656,7 +1114,10 @@ export interface ModelCapabilitiesOverride { } export interface ModeSetRequest { - mode: SessionMode; + /** + * The agent mode. Valid values: "interactive", "plan", "autopilot". + */ + mode: "interactive" | "plan" | "autopilot"; } export interface NameGetResult { @@ -752,6 +1213,46 @@ export interface WorkspacesCreateFileRequest { content: string; } +export interface InstructionsGetSourcesResult { + /** + * Instruction sources for the session + */ + sources: { + /** + * Unique identifier for this source (used for toggling) + */ + id: string; + /** + * Human-readable label + */ + label: string; + /** + * File path relative to repo or absolute for home + */ + sourcePath: string; + /** + * Raw content of the instruction file + */ + content: string; + /** + * Category of instruction source — used for merge logic + */ + type: "home" | "repo" | "model" | "vscode" | "nested-agents" | "child-instructions"; + /** + * Where this source lives — used for UI grouping + */ + location: "user" | "repository" | "working-directory"; + /** + * Glob pattern from frontmatter — when set, this instruction applies only to matching files + */ + applyTo?: string; + /** + * Short description (body after frontmatter) for use in instruction tables + */ + description?: string; + }[]; +} + /** @experimental */ export interface FleetStartResult { /** @@ -773,20 +1274,7 @@ export interface AgentList { /** * Available custom agents */ - agents: { - /** - * Unique identifier of the custom agent - */ - name: string; - /** - * Human-readable display name - */ - displayName: string; - /** - * Description of the agent's purpose - */ - description: string; - }[]; + agents: AgentInfo[]; } /** @experimental */ @@ -794,41 +1282,29 @@ export interface AgentGetCurrentResult { /** * Currently selected custom agent, or null if using the default agent */ - agent: { - /** - * Unique identifier of the custom agent - */ - name: string; - /** - * Human-readable display name - */ - displayName: string; - /** - * Description of the agent's purpose - */ - description: string; - } | null; + agent?: AgentInfo | null; } /** @experimental */ export interface AgentSelectResult { + agent: AgentInfo1; +} +/** + * The newly selected custom agent + */ +export interface AgentInfo1 { /** - * The newly selected custom agent + * Unique identifier of the custom agent */ - agent: { - /** - * Unique identifier of the custom agent - */ - name: string; - /** - * Human-readable display name - */ - displayName: string; - /** - * Description of the agent's purpose - */ - description: string; - }; + name: string; + /** + * Human-readable display name + */ + displayName: string; + /** + * Description of the agent's purpose + */ + description: string; } /** @experimental */ @@ -844,20 +1320,7 @@ export interface AgentReloadResult { /** * Reloaded custom agents */ - agents: { - /** - * Unique identifier of the custom agent - */ - name: string; - /** - * Human-readable display name - */ - displayName: string; - /** - * Description of the agent's purpose - */ - description: string; - }[]; + agents: AgentInfo[]; } /** @experimental */ @@ -909,31 +1372,6 @@ export interface SkillsDisableRequest { name: string; } -/** @experimental */ -export interface McpServerList { - /** - * Configured MCP servers - */ - servers: { - /** - * Server name (config key) - */ - name: string; - /** - * Connection status: connected, failed, needs-auth, pending, disabled, or not_configured - */ - status: "connected" | "failed" | "needs-auth" | "pending" | "disabled" | "not_configured"; - /** - * Configuration source: user, workspace, plugin, or builtin - */ - source?: "user" | "workspace" | "plugin" | "builtin"; - /** - * Error message if the server failed to connect - */ - error?: string; - }[]; -} - /** @experimental */ export interface McpEnableRequest { /** @@ -1020,13 +1458,6 @@ export interface ExtensionsDisableRequest { id: string; } -export interface HandleToolCallResult { - /** - * Whether the tool call result was handled successfully - */ - success: boolean; -} - export interface ToolsHandlePendingToolCallRequest { /** * Request ID of the pending tool call @@ -1041,26 +1472,6 @@ export interface ToolsHandlePendingToolCallRequest { */ error?: string; } -export interface ToolCallResult { - /** - * Text result to send back to the LLM - */ - textResultForLlm: string; - /** - * Type of the tool result - */ - resultType?: string; - /** - * Error message if the tool call failed - */ - error?: string; - /** - * Telemetry data from tool execution - */ - toolTelemetry?: { - [k: string]: unknown; - }; -} export interface CommandsHandlePendingCommandResult { /** @@ -1079,22 +1490,6 @@ export interface CommandsHandlePendingCommandRequest { */ error?: string; } -/** - * The elicitation response (accept with form values, decline, or cancel) - * - * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema - * via the `definition` "UIElicitationResponse". - */ -export interface UIElicitationResponse { - action: UIElicitationResponseAction; - content?: UIElicitationResponseContent; -} -/** - * The form values submitted by the user (present when action is 'accept') - */ -export interface UIElicitationResponseContent { - [k: string]: UIElicitationFieldValue; -} export interface UIElicitationRequest { /** @@ -1116,102 +1511,34 @@ export interface UIElicitationRequest { [k: string]: | UIElicitationStringEnumField | UIElicitationStringOneOfField - | UIElicitationArrayEnumField - | UIElicitationArrayAnyOfField - | { - type: "boolean"; - description?: string; - default?: boolean; - } - | { - type: "string"; - description?: string; - minLength?: number; - maxLength?: number; - format?: "email" | "uri" | "date" | "date-time"; - default?: string; - } - | { - type: "number" | "integer"; - description?: string; - minimum?: number; - maximum?: number; - default?: number; - }; - }; - /** - * List of required field names - */ - required?: string[]; - }; -} -export interface UIElicitationStringEnumField { - type: "string"; - description?: string; - enum: string[]; - enumNames?: string[]; - default?: string; -} -export interface UIElicitationStringOneOfField { - type: "string"; - description?: string; - oneOf: { - const: string; - }[]; - default?: string; -} -export interface UIElicitationArrayEnumField { - type: "array"; - description?: string; - minItems?: number; - maxItems?: number; - items: { - type: "string"; - enum: string[]; - }; - default?: string[]; -} -export interface UIElicitationArrayAnyOfField { - type: "array"; - description?: string; - minItems?: number; - maxItems?: number; - items: { - anyOf: { - const: string; - }[]; - }; - default?: string[]; -} - -export interface UIElicitationResult { - /** - * Whether the response was accepted. False if the request was already resolved by another client. - */ - success: boolean; -} - -export interface UIHandlePendingElicitationRequest { - /** - * The unique request ID from the elicitation.requested event - */ - requestId: string; - result: UIElicitationResponse; -} - -export interface PermissionRequestResult { - /** - * Whether the permission request was handled successfully - */ - success: boolean; -} - -export interface PermissionDecisionRequest { - /** - * Request ID of the pending permission request - */ - requestId: string; - result: PermissionDecision; + | UIElicitationArrayEnumField + | UIElicitationArrayAnyOfField + | { + type: "boolean"; + description?: string; + default?: boolean; + } + | { + type: "string"; + description?: string; + minLength?: number; + maxLength?: number; + format?: "email" | "uri" | "date" | "date-time"; + default?: string; + } + | { + type: "number" | "integer"; + description?: string; + minimum?: number; + maximum?: number; + default?: number; + }; + }; + /** + * List of required field names + */ + required?: string[]; + }; } export interface LogResult { @@ -1226,7 +1553,10 @@ export interface LogRequest { * Human-readable message */ message: string; - level?: SessionLogLevel; + /** + * Log severity level. Determines how the message is displayed in the timeline. Defaults to "info". + */ + level?: "info" | "warning" | "error"; /** * When true, the message is transient and not persisted to the session event log on disk */ @@ -1466,176 +1796,533 @@ export interface SessionFsWriteFileRequest { /** * Optional POSIX-style mode for newly created files */ - mode?: number; + mode?: number; +} + +export interface SessionFsAppendFileRequest { + /** + * Target session identifier + */ + sessionId: string; + /** + * Path using SessionFs conventions + */ + path: string; + /** + * Content to append + */ + content: string; + /** + * Optional POSIX-style mode for newly created files + */ + mode?: number; +} + +export interface SessionFsExistsResult { + /** + * Whether the path exists + */ + exists: boolean; +} + +export interface SessionFsExistsRequest { + /** + * Target session identifier + */ + sessionId: string; + /** + * Path using SessionFs conventions + */ + path: string; +} + +export interface SessionFsStatResult { + /** + * Whether the path is a file + */ + isFile: boolean; + /** + * Whether the path is a directory + */ + isDirectory: boolean; + /** + * File size in bytes + */ + size: number; + /** + * ISO 8601 timestamp of last modification + */ + mtime: string; + /** + * ISO 8601 timestamp of creation + */ + birthtime: string; +} + +export interface SessionFsStatRequest { + /** + * Target session identifier + */ + sessionId: string; + /** + * Path using SessionFs conventions + */ + path: string; +} + +export interface SessionFsMkdirRequest { + /** + * Target session identifier + */ + sessionId: string; + /** + * Path using SessionFs conventions + */ + path: string; + /** + * Create parent directories as needed + */ + recursive?: boolean; + /** + * Optional POSIX-style mode for newly created directories + */ + mode?: number; +} + +export interface SessionFsReaddirResult { + /** + * Entry names in the directory + */ + entries: string[]; +} + +export interface SessionFsReaddirRequest { + /** + * Target session identifier + */ + sessionId: string; + /** + * Path using SessionFs conventions + */ + path: string; +} + +export interface SessionFsReaddirWithTypesResult { + /** + * Directory entries with type information + */ + entries: { + /** + * Entry name + */ + name: string; + /** + * Entry type + */ + type: "file" | "directory"; + }[]; +} + +export interface SessionFsReaddirWithTypesRequest { + /** + * Target session identifier + */ + sessionId: string; + /** + * Path using SessionFs conventions + */ + path: string; +} + +export interface SessionFsRmRequest { + /** + * Target session identifier + */ + sessionId: string; + /** + * Path using SessionFs conventions + */ + path: string; + /** + * Remove directories and their contents recursively + */ + recursive?: boolean; + /** + * Ignore errors if the path does not exist + */ + force?: boolean; +} + +export interface SessionFsRenameRequest { + /** + * Target session identifier + */ + sessionId: string; + /** + * Source path using SessionFs conventions + */ + src: string; + /** + * Destination path using SessionFs conventions + */ + dest: string; +} +/** + * Model capabilities and limits + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "$defs_ModelCapabilities". + */ +export interface $Defs_ModelCapabilities { + /** + * Feature flags indicating what the model supports + */ + supports?: { + /** + * Whether this model supports vision/image input + */ + vision?: boolean; + /** + * Whether this model supports reasoning effort configuration + */ + reasoningEffort?: boolean; + }; + /** + * Token limits for prompts, outputs, and context window + */ + limits?: { + /** + * Maximum number of prompt/input tokens + */ + max_prompt_tokens?: number; + /** + * Maximum number of output/completion tokens + */ + max_output_tokens?: number; + /** + * Maximum total context window size in tokens + */ + max_context_window_tokens?: number; + vision?: ModelCapabilitiesLimitsVision2; + }; +} +/** + * Vision-specific limits + */ +export interface ModelCapabilitiesLimitsVision2 { + /** + * MIME types the model accepts + */ + supported_media_types: string[]; + /** + * Maximum number of images per prompt + */ + max_prompt_images: number; + /** + * Maximum image size in bytes + */ + max_prompt_image_size: number; +} +/** + * Vision-specific limits + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "$defs_ModelCapabilitiesLimitsVision". + */ +export interface $Defs_ModelCapabilitiesLimitsVision { + /** + * MIME types the model accepts + */ + supported_media_types: string[]; + /** + * Maximum number of images per prompt + */ + max_prompt_images: number; + /** + * Maximum image size in bytes + */ + max_prompt_image_size: number; } -export interface SessionFsAppendFileRequest { +export interface $Defs_DiscoveredMcpServer { /** - * Target session identifier + * Server name (config key) */ - sessionId: string; + name: string; /** - * Path using SessionFs conventions + * Server transport type: stdio, http, sse, or memory (local configs are normalized to stdio) */ - path: string; + type?: "stdio" | "http" | "sse" | "memory"; /** - * Content to append + * Configuration source */ - content: string; + source: "user" | "workspace" | "plugin" | "builtin"; /** - * Optional POSIX-style mode for newly created files + * Whether the server is enabled (not in the disabled list) */ - mode?: number; + enabled: boolean; } -export interface SessionFsExistsResult { +export interface $Defs_ServerSkillList { /** - * Whether the path exists + * All discovered skills across all sources */ - exists: boolean; + skills: ServerSkill[]; } -export interface SessionFsExistsRequest { +export interface $Defs_ServerSkill { /** - * Target session identifier + * Unique identifier for the skill */ - sessionId: string; + name: string; /** - * Path using SessionFs conventions + * Description of what the skill does */ - path: string; -} - -export interface SessionFsStatResult { + description: string; /** - * Whether the path is a file + * Source location type (e.g., project, personal-copilot, plugin, builtin) */ - isFile: boolean; + source: string; /** - * Whether the path is a directory + * Whether the skill can be invoked by the user as a slash command */ - isDirectory: boolean; + userInvocable: boolean; /** - * File size in bytes + * Whether the skill is currently enabled (based on global config) */ - size: number; + enabled: boolean; /** - * ISO 8601 timestamp of last modification + * Absolute path to the skill file */ - mtime: string; + path?: string; /** - * ISO 8601 timestamp of creation + * The project path this skill belongs to (only for project/inherited skills) */ - birthtime: string; + projectPath?: string; } -export interface SessionFsStatRequest { +export interface $Defs_CurrentModel { /** - * Target session identifier - */ - sessionId: string; - /** - * Path using SessionFs conventions + * Currently active model identifier */ - path: string; + modelId?: string; } - -export interface SessionFsMkdirRequest { - /** - * Target session identifier - */ - sessionId: string; - /** - * Path using SessionFs conventions - */ - path: string; +/** + * Override individual model capabilities resolved by the runtime + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "$defs_ModelCapabilitiesOverride". + */ +export interface $Defs_ModelCapabilitiesOverride { /** - * Create parent directories as needed + * Feature flags indicating what the model supports */ - recursive?: boolean; + supports?: { + vision?: boolean; + reasoningEffort?: boolean; + }; /** - * Optional POSIX-style mode for newly created directories + * Token limits for prompts, outputs, and context window */ - mode?: number; + limits?: { + max_prompt_tokens?: number; + max_output_tokens?: number; + /** + * Maximum total context window size in tokens + */ + max_context_window_tokens?: number; + vision?: { + /** + * MIME types the model accepts + */ + supported_media_types?: string[]; + /** + * Maximum number of images per prompt + */ + max_prompt_images?: number; + /** + * Maximum image size in bytes + */ + max_prompt_image_size?: number; + }; + }; } -export interface SessionFsReaddirResult { +export interface $Defs_AgentInfo { /** - * Entry names in the directory + * Unique identifier of the custom agent */ - entries: string[]; -} - -export interface SessionFsReaddirRequest { + name: string; /** - * Target session identifier + * Human-readable display name */ - sessionId: string; + displayName: string; /** - * Path using SessionFs conventions + * Description of the agent's purpose */ - path: string; + description: string; } -export interface SessionFsReaddirWithTypesResult { +export interface $Defs_McpServerList { /** - * Directory entries with type information + * Configured MCP servers */ - entries: { + servers: { /** - * Entry name + * Server name (config key) */ name: string; /** - * Entry type + * Connection status: connected, failed, needs-auth, pending, disabled, or not_configured */ - type: "file" | "directory"; + status: "connected" | "failed" | "needs-auth" | "pending" | "disabled" | "not_configured"; + /** + * Configuration source: user, workspace, plugin, or builtin + */ + source?: "user" | "workspace" | "plugin" | "builtin"; + /** + * Error message if the server failed to connect + */ + error?: string; }[]; } -export interface SessionFsReaddirWithTypesRequest { +export interface $Defs_ToolCallResult { /** - * Target session identifier + * Text result to send back to the LLM */ - sessionId: string; + textResultForLlm: string; /** - * Path using SessionFs conventions + * Type of the tool result */ - path: string; + resultType?: string; + /** + * Error message if the tool call failed + */ + error?: string; + /** + * Telemetry data from tool execution + */ + toolTelemetry?: { + [k: string]: unknown; + }; } -export interface SessionFsRmRequest { +export interface $Defs_HandleToolCallResult { /** - * Target session identifier + * Whether the tool call result was handled successfully */ - sessionId: string; + success: boolean; +} + +export interface $Defs_UIElicitationStringEnumField { + type: "string"; + description?: string; + enum: string[]; + enumNames?: string[]; + default?: string; +} + +export interface $Defs_UIElicitationStringOneOfField { + type: "string"; + description?: string; + oneOf: { + const: string; + }[]; + default?: string; +} + +export interface $Defs_UIElicitationArrayEnumField { + type: "array"; + description?: string; + minItems?: number; + maxItems?: number; + items: { + type: "string"; + enum: string[]; + }; + default?: string[]; +} + +export interface $Defs_UIElicitationArrayAnyOfField { + type: "array"; + description?: string; + minItems?: number; + maxItems?: number; + items: { + anyOf: { + const: string; + }[]; + }; + default?: string[]; +} +/** + * The elicitation response (accept with form values, decline, or cancel) + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "$defs_UIElicitationResponse". + */ +export interface $Defs_UIElicitationResponse { /** - * Path using SessionFs conventions + * The user's response: accept (submitted), decline (rejected), or cancel (dismissed) */ - path: string; + action: "accept" | "decline" | "cancel"; + content?: UIElicitationResponseContent2; +} +/** + * The form values submitted by the user (present when action is 'accept') + */ +export interface UIElicitationResponseContent2 { + [k: string]: UIElicitationFieldValue; +} +/** + * The form values submitted by the user (present when action is 'accept') + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "$defs_UIElicitationResponseContent". + */ +export interface $Defs_UIElicitationResponseContent { + [k: string]: UIElicitationFieldValue; +} + +export interface $Defs_UIHandlePendingElicitationRequest { /** - * Remove directories and their contents recursively + * The unique request ID from the elicitation.requested event */ - recursive?: boolean; + requestId: string; + result: UIElicitationResponse2; +} +/** + * The elicitation response (accept with form values, decline, or cancel) + */ +export interface UIElicitationResponse2 { /** - * Ignore errors if the path does not exist + * The user's response: accept (submitted), decline (rejected), or cancel (dismissed) */ - force?: boolean; + action: "accept" | "decline" | "cancel"; + content?: UIElicitationResponseContent; } -export interface SessionFsRenameRequest { +export interface $Defs_UIElicitationResult { /** - * Target session identifier + * Whether the response was accepted. False if the request was already resolved by another client. */ - sessionId: string; + success: boolean; +} + +export interface $Defs_PermissionDecisionRequest { /** - * Source path using SessionFs conventions + * Request ID of the pending permission request */ - src: string; + requestId: string; + result: PermissionDecision; +} + +export interface $Defs_PermissionRequestResult { /** - * Destination path using SessionFs conventions + * Whether the permission request was handled successfully */ - dest: string; + success: boolean; } /** Create typed server-scoped RPC methods (no session required). */ @@ -1728,6 +2415,10 @@ export function createSessionRpc(connection: MessageConnection, sessionId: strin createFile: async (params: Omit): Promise => connection.sendRequest("session.workspaces.createFile", { sessionId, ...params }), }, + instructions: { + getSources: async (): Promise => + connection.sendRequest("session.instructions.getSources", { sessionId }), + }, /** @experimental */ fleet: { start: async (params: Omit): Promise => diff --git a/nodejs/src/generated/session-events.ts b/nodejs/src/generated/session-events.ts index 65deaf2b3..d2de8d250 100644 --- a/nodejs/src/generated/session-events.ts +++ b/nodejs/src/generated/session-events.ts @@ -21,6 +21,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "session.start"; /** * Session initialization metadata including context and configuration @@ -54,39 +58,7 @@ export type SessionEvent = * Reasoning effort level used for model calls, if applicable (e.g. "low", "medium", "high", "xhigh") */ reasoningEffort?: string; - /** - * Working directory and git context at session start - */ - context?: { - /** - * Current working directory path - */ - cwd: string; - /** - * Root directory of the git repository, resolved via git rev-parse - */ - gitRoot?: string; - /** - * Repository identifier derived from the git remote URL ("owner/name" for GitHub, "org/project/repo" for Azure DevOps) - */ - repository?: string; - /** - * Hosting platform type of the repository (github or ado) - */ - hostType?: "github" | "ado"; - /** - * Current git branch name - */ - branch?: string; - /** - * Head commit of current git branch at session start time - */ - headCommit?: string; - /** - * Base commit of current git branch at session start time - */ - baseCommit?: string; - }; + context?: WorkingDirectoryContext; /** * Whether the session was already in use by another client at start time */ @@ -114,6 +86,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "session.resume"; /** * Session resume metadata including current context and event count @@ -135,39 +111,7 @@ export type SessionEvent = * Reasoning effort level used for model calls, if applicable (e.g. "low", "medium", "high", "xhigh") */ reasoningEffort?: string; - /** - * Updated working directory and git context at resume time - */ - context?: { - /** - * Current working directory path - */ - cwd: string; - /** - * Root directory of the git repository, resolved via git rev-parse - */ - gitRoot?: string; - /** - * Repository identifier derived from the git remote URL ("owner/name" for GitHub, "org/project/repo" for Azure DevOps) - */ - repository?: string; - /** - * Hosting platform type of the repository (github or ado) - */ - hostType?: "github" | "ado"; - /** - * Current git branch name - */ - branch?: string; - /** - * Head commit of current git branch at session start time - */ - headCommit?: string; - /** - * Base commit of current git branch at session start time - */ - baseCommit?: string; - }; + context?: WorkingDirectoryContext1; /** * Whether the session was already in use by another client at resume time */ @@ -195,6 +139,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "session.remote_steerable_changed"; /** * Notifies Mission Control that the session's remote steering capability has changed @@ -223,6 +171,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "session.error"; /** * Error details for timeline display including message and optional diagnostic information @@ -268,6 +220,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "session.idle"; /** * Payload indicating the session is idle with no background agents in flight @@ -293,6 +249,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "session.title_changed"; /** * Session title change payload containing the new display title @@ -316,6 +276,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "session.info"; /** * Informational message for timeline display with categorization @@ -352,6 +316,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "session.warning"; /** * Warning message for timeline display with categorization @@ -388,6 +356,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "session.model_change"; /** * Model change details including previous and new model identifiers @@ -428,6 +400,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "session.mode_changed"; /** * Agent mode change details including previous and new modes @@ -460,6 +436,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "session.plan_changed"; /** * Plan file operation details indicating what changed @@ -488,6 +468,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "session.workspace_file_changed"; /** * Workspace file change details including path and operation type @@ -520,6 +504,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "session.handoff"; /** * Session handoff metadata including source, context, and repository information @@ -585,6 +573,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "session.truncation"; /** * Conversation truncation statistics including token counts and removed content metrics @@ -638,6 +630,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "session.snapshot_rewind"; /** * Session rewind details including target event and count of removed events @@ -670,6 +666,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "session.shutdown"; /** * Session termination metrics including usage statistics, code changes, and shutdown reason @@ -796,40 +796,12 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; - type: "session.context_changed"; /** - * Updated working directory and git context after the change + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. */ - data: { - /** - * Current working directory path - */ - cwd: string; - /** - * Root directory of the git repository, resolved via git rev-parse - */ - gitRoot?: string; - /** - * Repository identifier derived from the git remote URL ("owner/name" for GitHub, "org/project/repo" for Azure DevOps) - */ - repository?: string; - /** - * Hosting platform type of the repository (github or ado) - */ - hostType?: "github" | "ado"; - /** - * Current git branch name - */ - branch?: string; - /** - * Head commit of current git branch at session start time - */ - headCommit?: string; - /** - * Base commit of current git branch at session start time - */ - baseCommit?: string; - }; + agentId?: string; + type: "session.context_changed"; + data: WorkingDirectoryContext2; } | { /** @@ -845,6 +817,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "session.usage_info"; /** * Current context window usage statistics including token and message counts @@ -897,6 +873,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "session.compaction_start"; /** * Context window breakdown at the start of LLM-powered conversation compaction @@ -933,6 +913,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "session.compaction_complete"; /** * Conversation compaction results including success status, metrics, and optional error details @@ -1030,6 +1014,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "session.task_complete"; /** * Task completion notification with summary from the agent @@ -1062,6 +1050,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "user.message"; data: { /** @@ -1207,6 +1199,14 @@ export type SessionEvent = displayName?: string; } )[]; + /** + * Normalized document MIME types that were sent natively instead of through tagged_files XML + */ + supportedNativeDocumentMimeTypes?: string[]; + /** + * Path-backed native document attachments that stayed on the tagged_files path flow because native upload would exceed the request size limit + */ + nativeDocumentPathFallbackPaths?: string[]; /** * Origin of this message, used for timeline filtering (e.g., "skill-pdf" for skill-injected messages that should be hidden from the user) */ @@ -1235,6 +1235,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "pending_messages.modified"; /** * Empty payload; the event signals that the pending message queue has changed @@ -1258,6 +1262,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "assistant.turn_start"; /** * Turn initialization metadata including identifier and interaction tracking @@ -1287,6 +1295,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "assistant.intent"; /** * Agent intent description for current activity or plan @@ -1315,6 +1327,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "assistant.reasoning"; /** * Assistant reasoning content for timeline display with complete thinking text @@ -1344,6 +1360,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "assistant.reasoning_delta"; /** * Streaming reasoning delta for incremental extended thinking updates @@ -1373,6 +1393,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "assistant.streaming_delta"; /** * Streaming response progress with cumulative byte count @@ -1401,6 +1425,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "assistant.message"; /** * Assistant response containing text content, optional tool requests, and interaction metadata @@ -1478,6 +1506,7 @@ export type SessionEvent = */ requestId?: string; /** + * @deprecated * Tool call ID of the parent tool invocation when this event originates from a sub-agent */ parentToolCallId?: string; @@ -1497,6 +1526,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "assistant.message_delta"; /** * Streaming assistant message delta for incremental response updates @@ -1511,6 +1544,7 @@ export type SessionEvent = */ deltaContent: string; /** + * @deprecated * Tool call ID of the parent tool invocation when this event originates from a sub-agent */ parentToolCallId?: string; @@ -1533,6 +1567,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "assistant.turn_end"; /** * Turn completion metadata including the turn identifier @@ -1558,6 +1596,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "assistant.usage"; /** * LLM API call usage metrics including tokens, costs, quotas, and billing information @@ -1616,6 +1658,7 @@ export type SessionEvent = */ providerCallId?: string; /** + * @deprecated * Parent tool call ID when this usage originates from a sub-agent */ parentToolCallId?: string; @@ -1711,6 +1754,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "abort"; /** * Turn abort information including the reason for termination @@ -1739,6 +1786,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "tool.user_requested"; /** * User-initiated tool invocation request with tool name and arguments @@ -1777,6 +1828,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "tool.execution_start"; /** * Tool execution startup details including MCP server information when applicable @@ -1805,6 +1860,7 @@ export type SessionEvent = */ mcpToolName?: string; /** + * @deprecated * Tool call ID of the parent tool invocation when this event originates from a sub-agent */ parentToolCallId?: string; @@ -1824,6 +1880,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "tool.execution_partial_result"; /** * Streaming tool execution output for incremental result display @@ -1853,6 +1913,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "tool.execution_progress"; /** * Tool execution progress notification with status message @@ -1885,6 +1949,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "tool.execution_complete"; /** * Tool execution completion results including success status, detailed output, and error information @@ -2061,6 +2129,7 @@ export type SessionEvent = [k: string]: unknown; }; /** + * @deprecated * Tool call ID of the parent tool invocation when this event originates from a sub-agent */ parentToolCallId?: string; @@ -2083,6 +2152,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "skill.invoked"; /** * Skill invocation details including content, allowed tools, and plugin metadata @@ -2135,6 +2208,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "subagent.started"; /** * Sub-agent startup details including parent tool call and agent information @@ -2175,6 +2252,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "subagent.completed"; /** * Sub-agent completion details for successful execution @@ -2227,6 +2308,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "subagent.failed"; /** * Sub-agent failure details including error message and agent information @@ -2283,6 +2368,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "subagent.selected"; /** * Custom agent selection details including name and available tools @@ -2319,6 +2408,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "subagent.deselected"; /** * Empty payload; the event signals that the custom agent was deselected, returning to the default agent @@ -2342,6 +2435,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "hook.start"; /** * Hook invocation start details including type and input data @@ -2380,6 +2477,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "hook.end"; /** * Hook invocation completion details including output, success status, and error information @@ -2435,13 +2536,17 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "system.message"; /** - * System or developer message content with role and optional template metadata + * System/developer instruction content with role and optional template metadata */ data: { /** - * The system or developer prompt text + * The system or developer prompt text sent as model input */ content: string; /** @@ -2486,6 +2591,10 @@ export type SessionEvent = * When true, the event is transient and not persisted to the session event log on disk */ ephemeral?: boolean; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "system.notification"; /** * System-generated notification for runtime events like background task completion @@ -2579,6 +2688,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "permission.requested"; /** * Permission request notification requiring client approval with request details @@ -2848,6 +2961,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "permission.completed"; /** * Permission request completion notification signaling UI dismissal @@ -2888,6 +3005,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "user_input.requested"; /** * User input request notification with question and optional predefined choices @@ -2929,6 +3050,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "user_input.completed"; /** * User input request completion with the user's response @@ -2962,6 +3087,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "elicitation.requested"; /** * Elicitation request; may be form-based (structured input) or URL-based (browser redirect) @@ -3027,6 +3156,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "elicitation.completed"; /** * Elicitation request completion with the user's response @@ -3062,6 +3195,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "sampling.requested"; /** * Sampling request from an MCP server; contains the server name and a requestId for correlation @@ -3096,6 +3233,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "sampling.completed"; /** * Sampling request completion notification signaling UI dismissal @@ -3121,6 +3262,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "mcp.oauth_required"; /** * OAuth authentication request for an MCP server @@ -3167,6 +3312,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "mcp.oauth_completed"; /** * MCP OAuth request completion notification @@ -3192,6 +3341,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "external_tool.requested"; /** * External tool invocation request for client-side tool execution @@ -3243,6 +3396,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "external_tool.completed"; /** * External tool completion notification signaling UI dismissal @@ -3268,6 +3425,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "command.queued"; /** * Queued slash command dispatch request for client execution @@ -3297,6 +3458,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "command.execute"; /** * Registered command dispatch request routed to the owning client @@ -3334,6 +3499,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "command.completed"; /** * Queued command completion notification signaling UI dismissal @@ -3359,6 +3528,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "commands.changed"; /** * SDK command registration change notification @@ -3387,6 +3560,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "capabilities.changed"; /** * Session capability change notification @@ -3417,6 +3594,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "exit_plan_mode.requested"; /** * Plan approval request with plan content and available user actions @@ -3458,6 +3639,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "exit_plan_mode.completed"; /** * Plan mode exit completion with the user's approval decision and optional feedback @@ -3499,6 +3684,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "session.tools_updated"; data: { model: string; @@ -3518,6 +3707,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "session.background_tasks_changed"; data: {}; } @@ -3535,6 +3728,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "session.skills_loaded"; data: { /** @@ -3582,6 +3779,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "session.custom_agents_updated"; data: { /** @@ -3645,6 +3846,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "session.mcp_servers_loaded"; data: { /** @@ -3684,6 +3889,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "session.mcp_server_status_changed"; data: { /** @@ -3710,6 +3919,10 @@ export type SessionEvent = */ parentId: string | null; ephemeral: true; + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; type: "session.extensions_loaded"; data: { /** @@ -3736,6 +3949,105 @@ export type SessionEvent = }; }; +/** + * Working directory and git context at session start + */ +export interface WorkingDirectoryContext { + /** + * Current working directory path + */ + cwd: string; + /** + * Root directory of the git repository, resolved via git rev-parse + */ + gitRoot?: string; + /** + * Repository identifier derived from the git remote URL ("owner/name" for GitHub, "org/project/repo" for Azure DevOps) + */ + repository?: string; + /** + * Hosting platform type of the repository (github or ado) + */ + hostType?: "github" | "ado"; + /** + * Current git branch name + */ + branch?: string; + /** + * Head commit of current git branch at session start time + */ + headCommit?: string; + /** + * Base commit of current git branch at session start time + */ + baseCommit?: string; +} +/** + * Updated working directory and git context at resume time + */ +export interface WorkingDirectoryContext1 { + /** + * Current working directory path + */ + cwd: string; + /** + * Root directory of the git repository, resolved via git rev-parse + */ + gitRoot?: string; + /** + * Repository identifier derived from the git remote URL ("owner/name" for GitHub, "org/project/repo" for Azure DevOps) + */ + repository?: string; + /** + * Hosting platform type of the repository (github or ado) + */ + hostType?: "github" | "ado"; + /** + * Current git branch name + */ + branch?: string; + /** + * Head commit of current git branch at session start time + */ + headCommit?: string; + /** + * Base commit of current git branch at session start time + */ + baseCommit?: string; +} +/** + * Updated working directory and git context after the change + */ +export interface WorkingDirectoryContext2 { + /** + * Current working directory path + */ + cwd: string; + /** + * Root directory of the git repository, resolved via git rev-parse + */ + gitRoot?: string; + /** + * Repository identifier derived from the git remote URL ("owner/name" for GitHub, "org/project/repo" for Azure DevOps) + */ + repository?: string; + /** + * Hosting platform type of the repository (github or ado) + */ + hostType?: "github" | "ado"; + /** + * Current git branch name + */ + branch?: string; + /** + * Head commit of current git branch at session start time + */ + headCommit?: string; + /** + * Base commit of current git branch at session start time + */ + baseCommit?: string; +} export interface EmbeddedTextResourceContents { /** * URI identifying the resource diff --git a/python/copilot/generated/rpc.py b/python/copilot/generated/rpc.py index c99182b17..1aa658823 100644 --- a/python/copilot/generated/rpc.py +++ b/python/copilot/generated/rpc.py @@ -21,14 +21,18 @@ EnumT = TypeVar("EnumT", bound=Enum) -def from_str(x: Any) -> str: - assert isinstance(x, str) - return x - def from_int(x: Any) -> int: assert isinstance(x, int) and not isinstance(x, bool) return x +def from_list(f: Callable[[Any], T], x: Any) -> list[T]: + assert isinstance(x, list) + return [f(y) for y in x] + +def from_str(x: Any) -> str: + assert isinstance(x, str) + return x + def from_none(x: Any) -> Any: assert x is None return x @@ -41,18 +45,6 @@ def from_union(fs, x): pass assert False -def from_float(x: Any) -> float: - assert isinstance(x, (float, int)) and not isinstance(x, bool) - return float(x) - -def to_float(x: Any) -> float: - assert isinstance(x, (int, float)) - return x - -def from_list(f: Callable[[Any], T], x: Any) -> list[T]: - assert isinstance(x, list) - return [f(y) for y in x] - def to_class(c: type[T], x: Any) -> dict: assert isinstance(x, c) return cast(Any, x).to_dict() @@ -65,76 +57,23 @@ def from_dict(f: Callable[[Any], T], x: Any) -> dict[str, T]: assert isinstance(x, dict) return { k: f(v) for (k, v) in x.items() } -def from_datetime(x: Any) -> datetime: - return dateutil.parser.parse(x) - def to_enum(c: type[EnumT], x: Any) -> EnumT: assert isinstance(x, c) return x.value -@dataclass -class PingResult: - message: str - """Echoed message (or default greeting)""" - - protocol_version: int - """Server protocol version number""" - - timestamp: int - """Server timestamp in milliseconds""" - - @staticmethod - def from_dict(obj: Any) -> 'PingResult': - assert isinstance(obj, dict) - message = from_str(obj.get("message")) - protocol_version = from_int(obj.get("protocolVersion")) - timestamp = from_int(obj.get("timestamp")) - return PingResult(message, protocol_version, timestamp) - - def to_dict(self) -> dict: - result: dict = {} - result["message"] = from_str(self.message) - result["protocolVersion"] = from_int(self.protocol_version) - result["timestamp"] = from_int(self.timestamp) - return result - -@dataclass -class PingRequest: - message: str | None = None - """Optional message to echo back""" - - @staticmethod - def from_dict(obj: Any) -> 'PingRequest': - assert isinstance(obj, dict) - message = from_union([from_str, from_none], obj.get("message")) - return PingRequest(message) - - def to_dict(self) -> dict: - result: dict = {} - if self.message is not None: - result["message"] = from_union([from_str, from_none], self.message) - return result - -@dataclass -class ModelBilling: - """Billing information""" - - multiplier: float - """Billing cost multiplier relative to the base rate""" +def from_float(x: Any) -> float: + assert isinstance(x, (float, int)) and not isinstance(x, bool) + return float(x) - @staticmethod - def from_dict(obj: Any) -> 'ModelBilling': - assert isinstance(obj, dict) - multiplier = from_float(obj.get("multiplier")) - return ModelBilling(multiplier) +def to_float(x: Any) -> float: + assert isinstance(x, (int, float)) + return x - def to_dict(self) -> dict: - result: dict = {} - result["multiplier"] = to_float(self.multiplier) - return result +def from_datetime(x: Any) -> datetime: + return dateutil.parser.parse(x) @dataclass -class ModelCapabilitiesLimitsVision: +class PurpleModelCapabilitiesLimitsVision: """Vision-specific limits""" max_prompt_image_size: int @@ -147,12 +86,12 @@ class ModelCapabilitiesLimitsVision: """MIME types the model accepts""" @staticmethod - def from_dict(obj: Any) -> 'ModelCapabilitiesLimitsVision': + def from_dict(obj: Any) -> 'PurpleModelCapabilitiesLimitsVision': assert isinstance(obj, dict) max_prompt_image_size = from_int(obj.get("max_prompt_image_size")) max_prompt_images = from_int(obj.get("max_prompt_images")) supported_media_types = from_list(from_str, obj.get("supported_media_types")) - return ModelCapabilitiesLimitsVision(max_prompt_image_size, max_prompt_images, supported_media_types) + return PurpleModelCapabilitiesLimitsVision(max_prompt_image_size, max_prompt_images, supported_media_types) def to_dict(self) -> dict: result: dict = {} @@ -161,43 +100,6 @@ def to_dict(self) -> dict: result["supported_media_types"] = from_list(from_str, self.supported_media_types) return result -@dataclass -class ModelCapabilitiesLimits: - """Token limits for prompts, outputs, and context window""" - - max_context_window_tokens: int | None = None - """Maximum total context window size in tokens""" - - max_output_tokens: int | None = None - """Maximum number of output/completion tokens""" - - max_prompt_tokens: int | None = None - """Maximum number of prompt/input tokens""" - - vision: ModelCapabilitiesLimitsVision | None = None - """Vision-specific limits""" - - @staticmethod - def from_dict(obj: Any) -> 'ModelCapabilitiesLimits': - assert isinstance(obj, dict) - max_context_window_tokens = from_union([from_int, from_none], obj.get("max_context_window_tokens")) - max_output_tokens = from_union([from_int, from_none], obj.get("max_output_tokens")) - max_prompt_tokens = from_union([from_int, from_none], obj.get("max_prompt_tokens")) - vision = from_union([ModelCapabilitiesLimitsVision.from_dict, from_none], obj.get("vision")) - return ModelCapabilitiesLimits(max_context_window_tokens, max_output_tokens, max_prompt_tokens, vision) - - def to_dict(self) -> dict: - result: dict = {} - if self.max_context_window_tokens is not None: - result["max_context_window_tokens"] = from_union([from_int, from_none], self.max_context_window_tokens) - if self.max_output_tokens is not None: - result["max_output_tokens"] = from_union([from_int, from_none], self.max_output_tokens) - if self.max_prompt_tokens is not None: - result["max_prompt_tokens"] = from_union([from_int, from_none], self.max_prompt_tokens) - if self.vision is not None: - result["vision"] = from_union([lambda x: to_class(ModelCapabilitiesLimitsVision, x), from_none], self.vision) - return result - @dataclass class ModelCapabilitiesSupports: """Feature flags indicating what the model supports""" @@ -224,716 +126,690 @@ def to_dict(self) -> dict: return result @dataclass -class ModelCapabilities: - """Model capabilities and limits""" +class ModelCapabilitiesLimitsVision: + """Vision-specific limits""" - limits: ModelCapabilitiesLimits | None = None - """Token limits for prompts, outputs, and context window""" + max_prompt_image_size: int + """Maximum image size in bytes""" - supports: ModelCapabilitiesSupports | None = None - """Feature flags indicating what the model supports""" + max_prompt_images: int + """Maximum number of images per prompt""" + + supported_media_types: list[str] + """MIME types the model accepts""" @staticmethod - def from_dict(obj: Any) -> 'ModelCapabilities': + def from_dict(obj: Any) -> 'ModelCapabilitiesLimitsVision': assert isinstance(obj, dict) - limits = from_union([ModelCapabilitiesLimits.from_dict, from_none], obj.get("limits")) - supports = from_union([ModelCapabilitiesSupports.from_dict, from_none], obj.get("supports")) - return ModelCapabilities(limits, supports) + max_prompt_image_size = from_int(obj.get("max_prompt_image_size")) + max_prompt_images = from_int(obj.get("max_prompt_images")) + supported_media_types = from_list(from_str, obj.get("supported_media_types")) + return ModelCapabilitiesLimitsVision(max_prompt_image_size, max_prompt_images, supported_media_types) def to_dict(self) -> dict: result: dict = {} - if self.limits is not None: - result["limits"] = from_union([lambda x: to_class(ModelCapabilitiesLimits, x), from_none], self.limits) - if self.supports is not None: - result["supports"] = from_union([lambda x: to_class(ModelCapabilitiesSupports, x), from_none], self.supports) + result["max_prompt_image_size"] = from_int(self.max_prompt_image_size) + result["max_prompt_images"] = from_int(self.max_prompt_images) + result["supported_media_types"] = from_list(from_str, self.supported_media_types) return result -@dataclass -class ModelPolicy: - """Policy state (if applicable)""" +class FilterMappingString(Enum): + HIDDEN_CHARACTERS = "hidden_characters" + MARKDOWN = "markdown" + NONE = "none" - state: str - """Current policy state for this model""" +class MCPServerConfigType(Enum): + """Remote transport type. Defaults to "http" when omitted.""" - terms: str - """Usage terms or conditions for this model""" + HTTP = "http" + LOCAL = "local" + SSE = "sse" + STDIO = "stdio" - @staticmethod - def from_dict(obj: Any) -> 'ModelPolicy': - assert isinstance(obj, dict) - state = from_str(obj.get("state")) - terms = from_str(obj.get("terms")) - return ModelPolicy(state, terms) +class MCPServerSource(Enum): + """Configuration source - def to_dict(self) -> dict: - result: dict = {} - result["state"] = from_str(self.state) - result["terms"] = from_str(self.terms) - return result + Configuration source: user, workspace, plugin, or builtin + """ + BUILTIN = "builtin" + PLUGIN = "plugin" + USER = "user" + WORKSPACE = "workspace" + +class DiscoveredMCPServerType(Enum): + """Server transport type: stdio, http, sse, or memory (local configs are normalized to stdio)""" + + HTTP = "http" + MEMORY = "memory" + SSE = "sse" + STDIO = "stdio" @dataclass -class Model: - capabilities: ModelCapabilities - """Model capabilities and limits""" +class SkillElement: + description: str + """Description of what the skill does""" - id: str - """Model identifier (e.g., "claude-sonnet-4.5")""" + enabled: bool + """Whether the skill is currently enabled (based on global config)""" name: str - """Display name""" + """Unique identifier for the skill""" - billing: ModelBilling | None = None - """Billing information""" + source: str + """Source location type (e.g., project, personal-copilot, plugin, builtin)""" - default_reasoning_effort: str | None = None - """Default reasoning effort level (only present if model supports reasoning effort)""" + user_invocable: bool + """Whether the skill can be invoked by the user as a slash command""" - policy: ModelPolicy | None = None - """Policy state (if applicable)""" + path: str | None = None + """Absolute path to the skill file""" - supported_reasoning_efforts: list[str] | None = None - """Supported reasoning effort levels (only present if model supports reasoning effort)""" + project_path: str | None = None + """The project path this skill belongs to (only for project/inherited skills)""" @staticmethod - def from_dict(obj: Any) -> 'Model': + def from_dict(obj: Any) -> 'SkillElement': assert isinstance(obj, dict) - capabilities = ModelCapabilities.from_dict(obj.get("capabilities")) - id = from_str(obj.get("id")) + description = from_str(obj.get("description")) + enabled = from_bool(obj.get("enabled")) name = from_str(obj.get("name")) - billing = from_union([ModelBilling.from_dict, from_none], obj.get("billing")) - default_reasoning_effort = from_union([from_str, from_none], obj.get("defaultReasoningEffort")) - policy = from_union([ModelPolicy.from_dict, from_none], obj.get("policy")) - supported_reasoning_efforts = from_union([lambda x: from_list(from_str, x), from_none], obj.get("supportedReasoningEfforts")) - return Model(capabilities, id, name, billing, default_reasoning_effort, policy, supported_reasoning_efforts) + source = from_str(obj.get("source")) + user_invocable = from_bool(obj.get("userInvocable")) + path = from_union([from_str, from_none], obj.get("path")) + project_path = from_union([from_str, from_none], obj.get("projectPath")) + return SkillElement(description, enabled, name, source, user_invocable, path, project_path) def to_dict(self) -> dict: result: dict = {} - result["capabilities"] = to_class(ModelCapabilities, self.capabilities) - result["id"] = from_str(self.id) + result["description"] = from_str(self.description) + result["enabled"] = from_bool(self.enabled) result["name"] = from_str(self.name) - if self.billing is not None: - result["billing"] = from_union([lambda x: to_class(ModelBilling, x), from_none], self.billing) - if self.default_reasoning_effort is not None: - result["defaultReasoningEffort"] = from_union([from_str, from_none], self.default_reasoning_effort) - if self.policy is not None: - result["policy"] = from_union([lambda x: to_class(ModelPolicy, x), from_none], self.policy) - if self.supported_reasoning_efforts is not None: - result["supportedReasoningEfforts"] = from_union([lambda x: from_list(from_str, x), from_none], self.supported_reasoning_efforts) - return result - -@dataclass -class ModelList: - models: list[Model] - """List of available models with full metadata""" - - @staticmethod - def from_dict(obj: Any) -> 'ModelList': - assert isinstance(obj, dict) - models = from_list(Model.from_dict, obj.get("models")) - return ModelList(models) - - def to_dict(self) -> dict: - result: dict = {} - result["models"] = from_list(lambda x: to_class(Model, x), self.models) + result["source"] = from_str(self.source) + result["userInvocable"] = from_bool(self.user_invocable) + if self.path is not None: + result["path"] = from_union([from_str, from_none], self.path) + if self.project_path is not None: + result["projectPath"] = from_union([from_str, from_none], self.project_path) return result @dataclass -class Tool: +class ServerSkill: description: str - """Description of what the tool does""" + """Description of what the skill does""" + + enabled: bool + """Whether the skill is currently enabled (based on global config)""" name: str - """Tool identifier (e.g., "bash", "grep", "str_replace_editor")""" + """Unique identifier for the skill""" - instructions: str | None = None - """Optional instructions for how to use this tool effectively""" + source: str + """Source location type (e.g., project, personal-copilot, plugin, builtin)""" - namespaced_name: str | None = None - """Optional namespaced name for declarative filtering (e.g., "playwright/navigate" for MCP - tools) - """ - parameters: dict[str, Any] | None = None - """JSON Schema for the tool's input parameters""" + user_invocable: bool + """Whether the skill can be invoked by the user as a slash command""" + + path: str | None = None + """Absolute path to the skill file""" + + project_path: str | None = None + """The project path this skill belongs to (only for project/inherited skills)""" @staticmethod - def from_dict(obj: Any) -> 'Tool': + def from_dict(obj: Any) -> 'ServerSkill': assert isinstance(obj, dict) description = from_str(obj.get("description")) + enabled = from_bool(obj.get("enabled")) name = from_str(obj.get("name")) - instructions = from_union([from_str, from_none], obj.get("instructions")) - namespaced_name = from_union([from_str, from_none], obj.get("namespacedName")) - parameters = from_union([lambda x: from_dict(lambda x: x, x), from_none], obj.get("parameters")) - return Tool(description, name, instructions, namespaced_name, parameters) + source = from_str(obj.get("source")) + user_invocable = from_bool(obj.get("userInvocable")) + path = from_union([from_str, from_none], obj.get("path")) + project_path = from_union([from_str, from_none], obj.get("projectPath")) + return ServerSkill(description, enabled, name, source, user_invocable, path, project_path) def to_dict(self) -> dict: result: dict = {} result["description"] = from_str(self.description) + result["enabled"] = from_bool(self.enabled) result["name"] = from_str(self.name) - if self.instructions is not None: - result["instructions"] = from_union([from_str, from_none], self.instructions) - if self.namespaced_name is not None: - result["namespacedName"] = from_union([from_str, from_none], self.namespaced_name) - if self.parameters is not None: - result["parameters"] = from_union([lambda x: from_dict(lambda x: x, x), from_none], self.parameters) + result["source"] = from_str(self.source) + result["userInvocable"] = from_bool(self.user_invocable) + if self.path is not None: + result["path"] = from_union([from_str, from_none], self.path) + if self.project_path is not None: + result["projectPath"] = from_union([from_str, from_none], self.project_path) return result @dataclass -class ToolList: - tools: list[Tool] - """List of available built-in tools with metadata""" +class CurrentModel: + model_id: str | None = None + """Currently active model identifier""" @staticmethod - def from_dict(obj: Any) -> 'ToolList': + def from_dict(obj: Any) -> 'CurrentModel': assert isinstance(obj, dict) - tools = from_list(Tool.from_dict, obj.get("tools")) - return ToolList(tools) + model_id = from_union([from_str, from_none], obj.get("modelId")) + return CurrentModel(model_id) def to_dict(self) -> dict: result: dict = {} - result["tools"] = from_list(lambda x: to_class(Tool, x), self.tools) + if self.model_id is not None: + result["modelId"] = from_union([from_str, from_none], self.model_id) return result @dataclass -class ToolsListRequest: - model: str | None = None - """Optional model ID — when provided, the returned tool list reflects model-specific - overrides - """ +class PurpleModelCapabilitiesOverrideLimitsVision: + max_prompt_image_size: int | None = None + """Maximum image size in bytes""" + + max_prompt_images: int | None = None + """Maximum number of images per prompt""" + + supported_media_types: list[str] | None = None + """MIME types the model accepts""" @staticmethod - def from_dict(obj: Any) -> 'ToolsListRequest': + def from_dict(obj: Any) -> 'PurpleModelCapabilitiesOverrideLimitsVision': assert isinstance(obj, dict) - model = from_union([from_str, from_none], obj.get("model")) - return ToolsListRequest(model) + max_prompt_image_size = from_union([from_int, from_none], obj.get("max_prompt_image_size")) + max_prompt_images = from_union([from_int, from_none], obj.get("max_prompt_images")) + supported_media_types = from_union([lambda x: from_list(from_str, x), from_none], obj.get("supported_media_types")) + return PurpleModelCapabilitiesOverrideLimitsVision(max_prompt_image_size, max_prompt_images, supported_media_types) def to_dict(self) -> dict: result: dict = {} - if self.model is not None: - result["model"] = from_union([from_str, from_none], self.model) + if self.max_prompt_image_size is not None: + result["max_prompt_image_size"] = from_union([from_int, from_none], self.max_prompt_image_size) + if self.max_prompt_images is not None: + result["max_prompt_images"] = from_union([from_int, from_none], self.max_prompt_images) + if self.supported_media_types is not None: + result["supported_media_types"] = from_union([lambda x: from_list(from_str, x), from_none], self.supported_media_types) return result @dataclass -class AccountQuotaSnapshot: - entitlement_requests: int - """Number of requests included in the entitlement""" - - overage: int - """Number of overage requests made this period""" - - overage_allowed_with_exhausted_quota: bool - """Whether pay-per-request usage is allowed when quota is exhausted""" - - remaining_percentage: float - """Percentage of entitlement remaining""" - - used_requests: int - """Number of requests used so far this period""" +class ModelCapabilitiesOverrideSupports: + """Feature flags indicating what the model supports""" - reset_date: datetime | None = None - """Date when the quota resets (ISO 8601)""" + reasoning_effort: bool | None = None + vision: bool | None = None @staticmethod - def from_dict(obj: Any) -> 'AccountQuotaSnapshot': + def from_dict(obj: Any) -> 'ModelCapabilitiesOverrideSupports': assert isinstance(obj, dict) - entitlement_requests = from_int(obj.get("entitlementRequests")) - overage = from_int(obj.get("overage")) - overage_allowed_with_exhausted_quota = from_bool(obj.get("overageAllowedWithExhaustedQuota")) - remaining_percentage = from_float(obj.get("remainingPercentage")) - used_requests = from_int(obj.get("usedRequests")) - reset_date = from_union([from_datetime, from_none], obj.get("resetDate")) - return AccountQuotaSnapshot(entitlement_requests, overage, overage_allowed_with_exhausted_quota, remaining_percentage, used_requests, reset_date) + reasoning_effort = from_union([from_bool, from_none], obj.get("reasoningEffort")) + vision = from_union([from_bool, from_none], obj.get("vision")) + return ModelCapabilitiesOverrideSupports(reasoning_effort, vision) def to_dict(self) -> dict: result: dict = {} - result["entitlementRequests"] = from_int(self.entitlement_requests) - result["overage"] = from_int(self.overage) - result["overageAllowedWithExhaustedQuota"] = from_bool(self.overage_allowed_with_exhausted_quota) - result["remainingPercentage"] = to_float(self.remaining_percentage) - result["usedRequests"] = from_int(self.used_requests) - if self.reset_date is not None: - result["resetDate"] = from_union([lambda x: x.isoformat(), from_none], self.reset_date) + if self.reasoning_effort is not None: + result["reasoningEffort"] = from_union([from_bool, from_none], self.reasoning_effort) + if self.vision is not None: + result["vision"] = from_union([from_bool, from_none], self.vision) return result @dataclass -class AccountGetQuotaResult: - quota_snapshots: dict[str, AccountQuotaSnapshot] - """Quota snapshots keyed by type (e.g., chat, completions, premium_interactions)""" +class AgentInfo: + description: str + """Description of the agent's purpose""" + + display_name: str + """Human-readable display name""" + + name: str + """Unique identifier of the custom agent""" @staticmethod - def from_dict(obj: Any) -> 'AccountGetQuotaResult': + def from_dict(obj: Any) -> 'AgentInfo': assert isinstance(obj, dict) - quota_snapshots = from_dict(AccountQuotaSnapshot.from_dict, obj.get("quotaSnapshots")) - return AccountGetQuotaResult(quota_snapshots) + description = from_str(obj.get("description")) + display_name = from_str(obj.get("displayName")) + name = from_str(obj.get("name")) + return AgentInfo(description, display_name, name) def to_dict(self) -> dict: result: dict = {} - result["quotaSnapshots"] = from_dict(lambda x: to_class(AccountQuotaSnapshot, x), self.quota_snapshots) + result["description"] = from_str(self.description) + result["displayName"] = from_str(self.display_name) + result["name"] = from_str(self.name) return result -class MCPConfigFilterMappingString(Enum): - HIDDEN_CHARACTERS = "hidden_characters" - MARKDOWN = "markdown" - NONE = "none" - -class MCPConfigType(Enum): - """Remote transport type. Defaults to "http" when omitted.""" +class MCPServerStatus(Enum): + """Connection status: connected, failed, needs-auth, pending, disabled, or not_configured""" - HTTP = "http" - LOCAL = "local" - SSE = "sse" - STDIO = "stdio" + CONNECTED = "connected" + DISABLED = "disabled" + FAILED = "failed" + NEEDS_AUTH = "needs-auth" + NOT_CONFIGURED = "not_configured" + PENDING = "pending" @dataclass -class MCPConfigServer: - """MCP server configuration (local/stdio or remote/http)""" - - args: list[str] | None = None - command: str | None = None - cwd: str | None = None - env: dict[str, str] | None = None - filter_mapping: dict[str, MCPConfigFilterMappingString] | MCPConfigFilterMappingString | None = None - is_default_server: bool | None = None - timeout: int | None = None - """Timeout in milliseconds for tool calls to this server.""" +class ToolCallResult: + text_result_for_llm: str + """Text result to send back to the LLM""" - tools: list[str] | None = None - """Tools to include. Defaults to all tools if not specified.""" + error: str | None = None + """Error message if the tool call failed""" - type: MCPConfigType | None = None - """Remote transport type. Defaults to "http" when omitted.""" + result_type: str | None = None + """Type of the tool result""" - headers: dict[str, str] | None = None - oauth_client_id: str | None = None - oauth_public_client: bool | None = None - url: str | None = None + tool_telemetry: dict[str, Any] | None = None + """Telemetry data from tool execution""" @staticmethod - def from_dict(obj: Any) -> 'MCPConfigServer': + def from_dict(obj: Any) -> 'ToolCallResult': assert isinstance(obj, dict) - args = from_union([lambda x: from_list(from_str, x), from_none], obj.get("args")) - command = from_union([from_str, from_none], obj.get("command")) - cwd = from_union([from_str, from_none], obj.get("cwd")) - env = from_union([lambda x: from_dict(from_str, x), from_none], obj.get("env")) - filter_mapping = from_union([lambda x: from_dict(MCPConfigFilterMappingString, x), MCPConfigFilterMappingString, from_none], obj.get("filterMapping")) - is_default_server = from_union([from_bool, from_none], obj.get("isDefaultServer")) - timeout = from_union([from_int, from_none], obj.get("timeout")) - tools = from_union([lambda x: from_list(from_str, x), from_none], obj.get("tools")) - type = from_union([MCPConfigType, from_none], obj.get("type")) - headers = from_union([lambda x: from_dict(from_str, x), from_none], obj.get("headers")) - oauth_client_id = from_union([from_str, from_none], obj.get("oauthClientId")) - oauth_public_client = from_union([from_bool, from_none], obj.get("oauthPublicClient")) - url = from_union([from_str, from_none], obj.get("url")) - return MCPConfigServer(args, command, cwd, env, filter_mapping, is_default_server, timeout, tools, type, headers, oauth_client_id, oauth_public_client, url) + text_result_for_llm = from_str(obj.get("textResultForLlm")) + error = from_union([from_str, from_none], obj.get("error")) + result_type = from_union([from_str, from_none], obj.get("resultType")) + tool_telemetry = from_union([lambda x: from_dict(lambda x: x, x), from_none], obj.get("toolTelemetry")) + return ToolCallResult(text_result_for_llm, error, result_type, tool_telemetry) def to_dict(self) -> dict: result: dict = {} - if self.args is not None: - result["args"] = from_union([lambda x: from_list(from_str, x), from_none], self.args) - if self.command is not None: - result["command"] = from_union([from_str, from_none], self.command) - if self.cwd is not None: - result["cwd"] = from_union([from_str, from_none], self.cwd) - if self.env is not None: - result["env"] = from_union([lambda x: from_dict(from_str, x), from_none], self.env) - if self.filter_mapping is not None: - result["filterMapping"] = from_union([lambda x: from_dict(lambda x: to_enum(MCPConfigFilterMappingString, x), x), lambda x: to_enum(MCPConfigFilterMappingString, x), from_none], self.filter_mapping) - if self.is_default_server is not None: - result["isDefaultServer"] = from_union([from_bool, from_none], self.is_default_server) - if self.timeout is not None: - result["timeout"] = from_union([from_int, from_none], self.timeout) - if self.tools is not None: - result["tools"] = from_union([lambda x: from_list(from_str, x), from_none], self.tools) - if self.type is not None: - result["type"] = from_union([lambda x: to_enum(MCPConfigType, x), from_none], self.type) - if self.headers is not None: - result["headers"] = from_union([lambda x: from_dict(from_str, x), from_none], self.headers) - if self.oauth_client_id is not None: - result["oauthClientId"] = from_union([from_str, from_none], self.oauth_client_id) - if self.oauth_public_client is not None: - result["oauthPublicClient"] = from_union([from_bool, from_none], self.oauth_public_client) - if self.url is not None: - result["url"] = from_union([from_str, from_none], self.url) + result["textResultForLlm"] = from_str(self.text_result_for_llm) + if self.error is not None: + result["error"] = from_union([from_str, from_none], self.error) + if self.result_type is not None: + result["resultType"] = from_union([from_str, from_none], self.result_type) + if self.tool_telemetry is not None: + result["toolTelemetry"] = from_union([lambda x: from_dict(lambda x: x, x), from_none], self.tool_telemetry) return result @dataclass -class MCPConfigList: - servers: dict[str, MCPConfigServer] - """All MCP servers from user config, keyed by name""" +class HandleToolCallResult: + success: bool + """Whether the tool call result was handled successfully""" @staticmethod - def from_dict(obj: Any) -> 'MCPConfigList': + def from_dict(obj: Any) -> 'HandleToolCallResult': assert isinstance(obj, dict) - servers = from_dict(MCPConfigServer.from_dict, obj.get("servers")) - return MCPConfigList(servers) + success = from_bool(obj.get("success")) + return HandleToolCallResult(success) def to_dict(self) -> dict: result: dict = {} - result["servers"] = from_dict(lambda x: to_class(MCPConfigServer, x), self.servers) + result["success"] = from_bool(self.success) return result +class UIElicitationStringEnumFieldType(Enum): + STRING = "string" + @dataclass -class MCPConfigAddConfig: - """MCP server configuration (local/stdio or remote/http)""" +class UIElicitationStringOneOfFieldOneOf: + const: str + title: str - args: list[str] | None = None - command: str | None = None - cwd: str | None = None - env: dict[str, str] | None = None - filter_mapping: dict[str, MCPConfigFilterMappingString] | MCPConfigFilterMappingString | None = None - is_default_server: bool | None = None - timeout: int | None = None - """Timeout in milliseconds for tool calls to this server.""" + @staticmethod + def from_dict(obj: Any) -> 'UIElicitationStringOneOfFieldOneOf': + assert isinstance(obj, dict) + const = from_str(obj.get("const")) + title = from_str(obj.get("title")) + return UIElicitationStringOneOfFieldOneOf(const, title) - tools: list[str] | None = None - """Tools to include. Defaults to all tools if not specified.""" + def to_dict(self) -> dict: + result: dict = {} + result["const"] = from_str(self.const) + result["title"] = from_str(self.title) + return result - type: MCPConfigType | None = None - """Remote transport type. Defaults to "http" when omitted.""" +class UIElicitationArrayEnumFieldType(Enum): + ARRAY = "array" - headers: dict[str, str] | None = None - oauth_client_id: str | None = None - oauth_public_client: bool | None = None - url: str | None = None +@dataclass +class PurpleUIElicitationArrayAnyOfFieldItemsAnyOf: + const: str + title: str @staticmethod - def from_dict(obj: Any) -> 'MCPConfigAddConfig': + def from_dict(obj: Any) -> 'PurpleUIElicitationArrayAnyOfFieldItemsAnyOf': assert isinstance(obj, dict) - args = from_union([lambda x: from_list(from_str, x), from_none], obj.get("args")) - command = from_union([from_str, from_none], obj.get("command")) - cwd = from_union([from_str, from_none], obj.get("cwd")) - env = from_union([lambda x: from_dict(from_str, x), from_none], obj.get("env")) - filter_mapping = from_union([lambda x: from_dict(MCPConfigFilterMappingString, x), MCPConfigFilterMappingString, from_none], obj.get("filterMapping")) - is_default_server = from_union([from_bool, from_none], obj.get("isDefaultServer")) - timeout = from_union([from_int, from_none], obj.get("timeout")) - tools = from_union([lambda x: from_list(from_str, x), from_none], obj.get("tools")) - type = from_union([MCPConfigType, from_none], obj.get("type")) - headers = from_union([lambda x: from_dict(from_str, x), from_none], obj.get("headers")) - oauth_client_id = from_union([from_str, from_none], obj.get("oauthClientId")) - oauth_public_client = from_union([from_bool, from_none], obj.get("oauthPublicClient")) - url = from_union([from_str, from_none], obj.get("url")) - return MCPConfigAddConfig(args, command, cwd, env, filter_mapping, is_default_server, timeout, tools, type, headers, oauth_client_id, oauth_public_client, url) + const = from_str(obj.get("const")) + title = from_str(obj.get("title")) + return PurpleUIElicitationArrayAnyOfFieldItemsAnyOf(const, title) def to_dict(self) -> dict: result: dict = {} - if self.args is not None: - result["args"] = from_union([lambda x: from_list(from_str, x), from_none], self.args) - if self.command is not None: - result["command"] = from_union([from_str, from_none], self.command) - if self.cwd is not None: - result["cwd"] = from_union([from_str, from_none], self.cwd) - if self.env is not None: - result["env"] = from_union([lambda x: from_dict(from_str, x), from_none], self.env) - if self.filter_mapping is not None: - result["filterMapping"] = from_union([lambda x: from_dict(lambda x: to_enum(MCPConfigFilterMappingString, x), x), lambda x: to_enum(MCPConfigFilterMappingString, x), from_none], self.filter_mapping) - if self.is_default_server is not None: - result["isDefaultServer"] = from_union([from_bool, from_none], self.is_default_server) - if self.timeout is not None: - result["timeout"] = from_union([from_int, from_none], self.timeout) - if self.tools is not None: - result["tools"] = from_union([lambda x: from_list(from_str, x), from_none], self.tools) - if self.type is not None: - result["type"] = from_union([lambda x: to_enum(MCPConfigType, x), from_none], self.type) - if self.headers is not None: - result["headers"] = from_union([lambda x: from_dict(from_str, x), from_none], self.headers) - if self.oauth_client_id is not None: - result["oauthClientId"] = from_union([from_str, from_none], self.oauth_client_id) - if self.oauth_public_client is not None: - result["oauthPublicClient"] = from_union([from_bool, from_none], self.oauth_public_client) - if self.url is not None: - result["url"] = from_union([from_str, from_none], self.url) + result["const"] = from_str(self.const) + result["title"] = from_str(self.title) return result -@dataclass -class MCPConfigAddRequest: - config: MCPConfigAddConfig - """MCP server configuration (local/stdio or remote/http)""" +class UIElicitationResponseAction(Enum): + """The user's response: accept (submitted), decline (rejected), or cancel (dismissed)""" - name: str - """Unique name for the MCP server""" + ACCEPT = "accept" + CANCEL = "cancel" + DECLINE = "decline" + +@dataclass +class UIElicitationResult: + success: bool + """Whether the response was accepted. False if the request was already resolved by another + client. + """ @staticmethod - def from_dict(obj: Any) -> 'MCPConfigAddRequest': + def from_dict(obj: Any) -> 'UIElicitationResult': assert isinstance(obj, dict) - config = MCPConfigAddConfig.from_dict(obj.get("config")) - name = from_str(obj.get("name")) - return MCPConfigAddRequest(config, name) + success = from_bool(obj.get("success")) + return UIElicitationResult(success) def to_dict(self) -> dict: result: dict = {} - result["config"] = to_class(MCPConfigAddConfig, self.config) - result["name"] = from_str(self.name) + result["success"] = from_bool(self.success) return result +class Kind(Enum): + APPROVED = "approved" + DENIED_BY_CONTENT_EXCLUSION_POLICY = "denied-by-content-exclusion-policy" + DENIED_BY_PERMISSION_REQUEST_HOOK = "denied-by-permission-request-hook" + DENIED_BY_RULES = "denied-by-rules" + DENIED_INTERACTIVELY_BY_USER = "denied-interactively-by-user" + DENIED_NO_APPROVAL_RULE_AND_COULD_NOT_REQUEST_FROM_USER = "denied-no-approval-rule-and-could-not-request-from-user" + @dataclass -class MCPConfigUpdateConfig: - """MCP server configuration (local/stdio or remote/http)""" +class PermissionRequestResult: + success: bool + """Whether the permission request was handled successfully""" - args: list[str] | None = None - command: str | None = None - cwd: str | None = None - env: dict[str, str] | None = None - filter_mapping: dict[str, MCPConfigFilterMappingString] | MCPConfigFilterMappingString | None = None - is_default_server: bool | None = None - timeout: int | None = None - """Timeout in milliseconds for tool calls to this server.""" + @staticmethod + def from_dict(obj: Any) -> 'PermissionRequestResult': + assert isinstance(obj, dict) + success = from_bool(obj.get("success")) + return PermissionRequestResult(success) - tools: list[str] | None = None - """Tools to include. Defaults to all tools if not specified.""" + def to_dict(self) -> dict: + result: dict = {} + result["success"] = from_bool(self.success) + return result - type: MCPConfigType | None = None - """Remote transport type. Defaults to "http" when omitted.""" +@dataclass +class PingResult: + message: str + """Echoed message (or default greeting)""" - headers: dict[str, str] | None = None - oauth_client_id: str | None = None - oauth_public_client: bool | None = None - url: str | None = None + protocol_version: int + """Server protocol version number""" + + timestamp: int + """Server timestamp in milliseconds""" @staticmethod - def from_dict(obj: Any) -> 'MCPConfigUpdateConfig': + def from_dict(obj: Any) -> 'PingResult': assert isinstance(obj, dict) - args = from_union([lambda x: from_list(from_str, x), from_none], obj.get("args")) - command = from_union([from_str, from_none], obj.get("command")) - cwd = from_union([from_str, from_none], obj.get("cwd")) - env = from_union([lambda x: from_dict(from_str, x), from_none], obj.get("env")) - filter_mapping = from_union([lambda x: from_dict(MCPConfigFilterMappingString, x), MCPConfigFilterMappingString, from_none], obj.get("filterMapping")) - is_default_server = from_union([from_bool, from_none], obj.get("isDefaultServer")) - timeout = from_union([from_int, from_none], obj.get("timeout")) - tools = from_union([lambda x: from_list(from_str, x), from_none], obj.get("tools")) - type = from_union([MCPConfigType, from_none], obj.get("type")) - headers = from_union([lambda x: from_dict(from_str, x), from_none], obj.get("headers")) - oauth_client_id = from_union([from_str, from_none], obj.get("oauthClientId")) - oauth_public_client = from_union([from_bool, from_none], obj.get("oauthPublicClient")) - url = from_union([from_str, from_none], obj.get("url")) - return MCPConfigUpdateConfig(args, command, cwd, env, filter_mapping, is_default_server, timeout, tools, type, headers, oauth_client_id, oauth_public_client, url) + message = from_str(obj.get("message")) + protocol_version = from_int(obj.get("protocolVersion")) + timestamp = from_int(obj.get("timestamp")) + return PingResult(message, protocol_version, timestamp) def to_dict(self) -> dict: result: dict = {} - if self.args is not None: - result["args"] = from_union([lambda x: from_list(from_str, x), from_none], self.args) - if self.command is not None: - result["command"] = from_union([from_str, from_none], self.command) - if self.cwd is not None: - result["cwd"] = from_union([from_str, from_none], self.cwd) - if self.env is not None: - result["env"] = from_union([lambda x: from_dict(from_str, x), from_none], self.env) - if self.filter_mapping is not None: - result["filterMapping"] = from_union([lambda x: from_dict(lambda x: to_enum(MCPConfigFilterMappingString, x), x), lambda x: to_enum(MCPConfigFilterMappingString, x), from_none], self.filter_mapping) - if self.is_default_server is not None: - result["isDefaultServer"] = from_union([from_bool, from_none], self.is_default_server) - if self.timeout is not None: - result["timeout"] = from_union([from_int, from_none], self.timeout) - if self.tools is not None: - result["tools"] = from_union([lambda x: from_list(from_str, x), from_none], self.tools) - if self.type is not None: - result["type"] = from_union([lambda x: to_enum(MCPConfigType, x), from_none], self.type) - if self.headers is not None: - result["headers"] = from_union([lambda x: from_dict(from_str, x), from_none], self.headers) - if self.oauth_client_id is not None: - result["oauthClientId"] = from_union([from_str, from_none], self.oauth_client_id) - if self.oauth_public_client is not None: - result["oauthPublicClient"] = from_union([from_bool, from_none], self.oauth_public_client) - if self.url is not None: - result["url"] = from_union([from_str, from_none], self.url) + result["message"] = from_str(self.message) + result["protocolVersion"] = from_int(self.protocol_version) + result["timestamp"] = from_int(self.timestamp) return result @dataclass -class MCPConfigUpdateRequest: - config: MCPConfigUpdateConfig - """MCP server configuration (local/stdio or remote/http)""" - - name: str - """Name of the MCP server to update""" +class PingRequest: + message: str | None = None + """Optional message to echo back""" @staticmethod - def from_dict(obj: Any) -> 'MCPConfigUpdateRequest': + def from_dict(obj: Any) -> 'PingRequest': assert isinstance(obj, dict) - config = MCPConfigUpdateConfig.from_dict(obj.get("config")) - name = from_str(obj.get("name")) - return MCPConfigUpdateRequest(config, name) + message = from_union([from_str, from_none], obj.get("message")) + return PingRequest(message) def to_dict(self) -> dict: result: dict = {} - result["config"] = to_class(MCPConfigUpdateConfig, self.config) - result["name"] = from_str(self.name) + if self.message is not None: + result["message"] = from_union([from_str, from_none], self.message) return result @dataclass -class MCPConfigRemoveRequest: - name: str - """Name of the MCP server to remove""" +class ModelBilling: + """Billing information""" + + multiplier: float + """Billing cost multiplier relative to the base rate""" @staticmethod - def from_dict(obj: Any) -> 'MCPConfigRemoveRequest': + def from_dict(obj: Any) -> 'ModelBilling': assert isinstance(obj, dict) - name = from_str(obj.get("name")) - return MCPConfigRemoveRequest(name) + multiplier = from_float(obj.get("multiplier")) + return ModelBilling(multiplier) def to_dict(self) -> dict: result: dict = {} - result["name"] = from_str(self.name) + result["multiplier"] = to_float(self.multiplier) return result -class MCPServerSource(Enum): - """Configuration source +@dataclass +class FluffyModelCapabilitiesLimitsVision: + """Vision-specific limits""" - Configuration source: user, workspace, plugin, or builtin - """ - BUILTIN = "builtin" - PLUGIN = "plugin" - USER = "user" - WORKSPACE = "workspace" + max_prompt_image_size: int + """Maximum image size in bytes""" -class DiscoveredMCPServerType(Enum): - """Server transport type: stdio, http, sse, or memory (local configs are normalized to stdio)""" + max_prompt_images: int + """Maximum number of images per prompt""" - HTTP = "http" - MEMORY = "memory" - SSE = "sse" - STDIO = "stdio" + supported_media_types: list[str] + """MIME types the model accepts""" -@dataclass -class DiscoveredMCPServer: - enabled: bool - """Whether the server is enabled (not in the disabled list)""" + @staticmethod + def from_dict(obj: Any) -> 'FluffyModelCapabilitiesLimitsVision': + assert isinstance(obj, dict) + max_prompt_image_size = from_int(obj.get("max_prompt_image_size")) + max_prompt_images = from_int(obj.get("max_prompt_images")) + supported_media_types = from_list(from_str, obj.get("supported_media_types")) + return FluffyModelCapabilitiesLimitsVision(max_prompt_image_size, max_prompt_images, supported_media_types) - name: str - """Server name (config key)""" + def to_dict(self) -> dict: + result: dict = {} + result["max_prompt_image_size"] = from_int(self.max_prompt_image_size) + result["max_prompt_images"] = from_int(self.max_prompt_images) + result["supported_media_types"] = from_list(from_str, self.supported_media_types) + return result - source: MCPServerSource - """Configuration source""" +@dataclass +class CapabilitiesSupports: + """Feature flags indicating what the model supports""" - type: DiscoveredMCPServerType | None = None - """Server transport type: stdio, http, sse, or memory (local configs are normalized to stdio)""" + reasoning_effort: bool | None = None + """Whether this model supports reasoning effort configuration""" + + vision: bool | None = None + """Whether this model supports vision/image input""" @staticmethod - def from_dict(obj: Any) -> 'DiscoveredMCPServer': + def from_dict(obj: Any) -> 'CapabilitiesSupports': assert isinstance(obj, dict) - enabled = from_bool(obj.get("enabled")) - name = from_str(obj.get("name")) - source = MCPServerSource(obj.get("source")) - type = from_union([DiscoveredMCPServerType, from_none], obj.get("type")) - return DiscoveredMCPServer(enabled, name, source, type) + reasoning_effort = from_union([from_bool, from_none], obj.get("reasoningEffort")) + vision = from_union([from_bool, from_none], obj.get("vision")) + return CapabilitiesSupports(reasoning_effort, vision) def to_dict(self) -> dict: result: dict = {} - result["enabled"] = from_bool(self.enabled) - result["name"] = from_str(self.name) - result["source"] = to_enum(MCPServerSource, self.source) - if self.type is not None: - result["type"] = from_union([lambda x: to_enum(DiscoveredMCPServerType, x), from_none], self.type) + if self.reasoning_effort is not None: + result["reasoningEffort"] = from_union([from_bool, from_none], self.reasoning_effort) + if self.vision is not None: + result["vision"] = from_union([from_bool, from_none], self.vision) return result @dataclass -class MCPDiscoverResult: - servers: list[DiscoveredMCPServer] - """MCP servers discovered from all sources""" +class ModelPolicy: + """Policy state (if applicable)""" + + state: str + """Current policy state for this model""" + + terms: str + """Usage terms or conditions for this model""" @staticmethod - def from_dict(obj: Any) -> 'MCPDiscoverResult': + def from_dict(obj: Any) -> 'ModelPolicy': assert isinstance(obj, dict) - servers = from_list(DiscoveredMCPServer.from_dict, obj.get("servers")) - return MCPDiscoverResult(servers) + state = from_str(obj.get("state")) + terms = from_str(obj.get("terms")) + return ModelPolicy(state, terms) def to_dict(self) -> dict: result: dict = {} - result["servers"] = from_list(lambda x: to_class(DiscoveredMCPServer, x), self.servers) + result["state"] = from_str(self.state) + result["terms"] = from_str(self.terms) return result @dataclass -class MCPDiscoverRequest: - working_directory: str | None = None - """Working directory used as context for discovery (e.g., plugin resolution)""" +class Tool: + description: str + """Description of what the tool does""" + + name: str + """Tool identifier (e.g., "bash", "grep", "str_replace_editor")""" + + instructions: str | None = None + """Optional instructions for how to use this tool effectively""" + + namespaced_name: str | None = None + """Optional namespaced name for declarative filtering (e.g., "playwright/navigate" for MCP + tools) + """ + parameters: dict[str, Any] | None = None + """JSON Schema for the tool's input parameters""" @staticmethod - def from_dict(obj: Any) -> 'MCPDiscoverRequest': + def from_dict(obj: Any) -> 'Tool': assert isinstance(obj, dict) - working_directory = from_union([from_str, from_none], obj.get("workingDirectory")) - return MCPDiscoverRequest(working_directory) + description = from_str(obj.get("description")) + name = from_str(obj.get("name")) + instructions = from_union([from_str, from_none], obj.get("instructions")) + namespaced_name = from_union([from_str, from_none], obj.get("namespacedName")) + parameters = from_union([lambda x: from_dict(lambda x: x, x), from_none], obj.get("parameters")) + return Tool(description, name, instructions, namespaced_name, parameters) def to_dict(self) -> dict: result: dict = {} - if self.working_directory is not None: - result["workingDirectory"] = from_union([from_str, from_none], self.working_directory) + result["description"] = from_str(self.description) + result["name"] = from_str(self.name) + if self.instructions is not None: + result["instructions"] = from_union([from_str, from_none], self.instructions) + if self.namespaced_name is not None: + result["namespacedName"] = from_union([from_str, from_none], self.namespaced_name) + if self.parameters is not None: + result["parameters"] = from_union([lambda x: from_dict(lambda x: x, x), from_none], self.parameters) return result @dataclass -class SkillsConfigSetDisabledSkillsRequest: - disabled_skills: list[str] - """List of skill names to disable""" +class ToolsListRequest: + model: str | None = None + """Optional model ID — when provided, the returned tool list reflects model-specific + overrides + """ @staticmethod - def from_dict(obj: Any) -> 'SkillsConfigSetDisabledSkillsRequest': + def from_dict(obj: Any) -> 'ToolsListRequest': assert isinstance(obj, dict) - disabled_skills = from_list(from_str, obj.get("disabledSkills")) - return SkillsConfigSetDisabledSkillsRequest(disabled_skills) + model = from_union([from_str, from_none], obj.get("model")) + return ToolsListRequest(model) def to_dict(self) -> dict: result: dict = {} - result["disabledSkills"] = from_list(from_str, self.disabled_skills) + if self.model is not None: + result["model"] = from_union([from_str, from_none], self.model) return result @dataclass -class ServerSkill: - description: str - """Description of what the skill does""" +class AccountQuotaSnapshot: + entitlement_requests: int + """Number of requests included in the entitlement""" - enabled: bool - """Whether the skill is currently enabled (based on global config)""" + overage: int + """Number of overage requests made this period""" - name: str - """Unique identifier for the skill""" + overage_allowed_with_exhausted_quota: bool + """Whether pay-per-request usage is allowed when quota is exhausted""" - source: str - """Source location type (e.g., project, personal-copilot, plugin, builtin)""" + remaining_percentage: float + """Percentage of entitlement remaining""" - user_invocable: bool - """Whether the skill can be invoked by the user as a slash command""" + used_requests: int + """Number of requests used so far this period""" - path: str | None = None - """Absolute path to the skill file""" + reset_date: datetime | None = None + """Date when the quota resets (ISO 8601)""" - project_path: str | None = None - """The project path this skill belongs to (only for project/inherited skills)""" + @staticmethod + def from_dict(obj: Any) -> 'AccountQuotaSnapshot': + assert isinstance(obj, dict) + entitlement_requests = from_int(obj.get("entitlementRequests")) + overage = from_int(obj.get("overage")) + overage_allowed_with_exhausted_quota = from_bool(obj.get("overageAllowedWithExhaustedQuota")) + remaining_percentage = from_float(obj.get("remainingPercentage")) + used_requests = from_int(obj.get("usedRequests")) + reset_date = from_union([from_datetime, from_none], obj.get("resetDate")) + return AccountQuotaSnapshot(entitlement_requests, overage, overage_allowed_with_exhausted_quota, remaining_percentage, used_requests, reset_date) + + def to_dict(self) -> dict: + result: dict = {} + result["entitlementRequests"] = from_int(self.entitlement_requests) + result["overage"] = from_int(self.overage) + result["overageAllowedWithExhaustedQuota"] = from_bool(self.overage_allowed_with_exhausted_quota) + result["remainingPercentage"] = to_float(self.remaining_percentage) + result["usedRequests"] = from_int(self.used_requests) + if self.reset_date is not None: + result["resetDate"] = from_union([lambda x: x.isoformat(), from_none], self.reset_date) + return result + +@dataclass +class MCPConfigRemoveRequest: + name: str + """Name of the MCP server to remove""" @staticmethod - def from_dict(obj: Any) -> 'ServerSkill': + def from_dict(obj: Any) -> 'MCPConfigRemoveRequest': assert isinstance(obj, dict) - description = from_str(obj.get("description")) - enabled = from_bool(obj.get("enabled")) name = from_str(obj.get("name")) - source = from_str(obj.get("source")) - user_invocable = from_bool(obj.get("userInvocable")) - path = from_union([from_str, from_none], obj.get("path")) - project_path = from_union([from_str, from_none], obj.get("projectPath")) - return ServerSkill(description, enabled, name, source, user_invocable, path, project_path) + return MCPConfigRemoveRequest(name) def to_dict(self) -> dict: result: dict = {} - result["description"] = from_str(self.description) - result["enabled"] = from_bool(self.enabled) result["name"] = from_str(self.name) - result["source"] = from_str(self.source) - result["userInvocable"] = from_bool(self.user_invocable) - if self.path is not None: - result["path"] = from_union([from_str, from_none], self.path) - if self.project_path is not None: - result["projectPath"] = from_union([from_str, from_none], self.project_path) return result @dataclass -class ServerSkillList: - skills: list[ServerSkill] - """All discovered skills across all sources""" +class MCPDiscoverRequest: + working_directory: str | None = None + """Working directory used as context for discovery (e.g., plugin resolution)""" @staticmethod - def from_dict(obj: Any) -> 'ServerSkillList': + def from_dict(obj: Any) -> 'MCPDiscoverRequest': assert isinstance(obj, dict) - skills = from_list(ServerSkill.from_dict, obj.get("skills")) - return ServerSkillList(skills) + working_directory = from_union([from_str, from_none], obj.get("workingDirectory")) + return MCPDiscoverRequest(working_directory) + + def to_dict(self) -> dict: + result: dict = {} + if self.working_directory is not None: + result["workingDirectory"] = from_union([from_str, from_none], self.working_directory) + return result + +@dataclass +class SkillsConfigSetDisabledSkillsRequest: + disabled_skills: list[str] + """List of skill names to disable""" + + @staticmethod + def from_dict(obj: Any) -> 'SkillsConfigSetDisabledSkillsRequest': + assert isinstance(obj, dict) + disabled_skills = from_list(from_str, obj.get("disabledSkills")) + return SkillsConfigSetDisabledSkillsRequest(disabled_skills) def to_dict(self) -> dict: result: dict = {} - result["skills"] = from_list(lambda x: to_class(ServerSkill, x), self.skills) + result["disabledSkills"] = from_list(from_str, self.disabled_skills) return result @dataclass @@ -981,32 +857,6 @@ class SessionFSSetProviderConventions(Enum): POSIX = "posix" WINDOWS = "windows" -@dataclass -class SessionFSSetProviderRequest: - conventions: SessionFSSetProviderConventions - """Path conventions used by this filesystem""" - - initial_cwd: str - """Initial working directory for sessions""" - - session_state_path: str - """Path within each session's SessionFs where the runtime stores files for that session""" - - @staticmethod - def from_dict(obj: Any) -> 'SessionFSSetProviderRequest': - assert isinstance(obj, dict) - conventions = SessionFSSetProviderConventions(obj.get("conventions")) - initial_cwd = from_str(obj.get("initialCwd")) - session_state_path = from_str(obj.get("sessionStatePath")) - return SessionFSSetProviderRequest(conventions, initial_cwd, session_state_path) - - def to_dict(self) -> dict: - result: dict = {} - result["conventions"] = to_enum(SessionFSSetProviderConventions, self.conventions) - result["initialCwd"] = from_str(self.initial_cwd) - result["sessionStatePath"] = from_str(self.session_state_path) - return result - # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class SessionsForkResult: @@ -1050,24 +900,7 @@ def to_dict(self) -> dict: return result @dataclass -class CurrentModel: - model_id: str | None = None - """Currently active model identifier""" - - @staticmethod - def from_dict(obj: Any) -> 'CurrentModel': - assert isinstance(obj, dict) - model_id = from_union([from_str, from_none], obj.get("modelId")) - return CurrentModel(model_id) - - def to_dict(self) -> dict: - result: dict = {} - if self.model_id is not None: - result["modelId"] = from_union([from_str, from_none], self.model_id) - return result - -@dataclass -class ModelSwitchToResult: +class ModelSwitchToResult: model_id: str | None = None """Currently active model identifier after the switch""" @@ -1084,7 +917,7 @@ def to_dict(self) -> dict: return result @dataclass -class ModelCapabilitiesOverrideLimitsVision: +class FluffyModelCapabilitiesOverrideLimitsVision: max_prompt_image_size: int | None = None """Maximum image size in bytes""" @@ -1095,12 +928,12 @@ class ModelCapabilitiesOverrideLimitsVision: """MIME types the model accepts""" @staticmethod - def from_dict(obj: Any) -> 'ModelCapabilitiesOverrideLimitsVision': + def from_dict(obj: Any) -> 'FluffyModelCapabilitiesOverrideLimitsVision': assert isinstance(obj, dict) max_prompt_image_size = from_union([from_int, from_none], obj.get("max_prompt_image_size")) max_prompt_images = from_union([from_int, from_none], obj.get("max_prompt_images")) supported_media_types = from_union([lambda x: from_list(from_str, x), from_none], obj.get("supported_media_types")) - return ModelCapabilitiesOverrideLimitsVision(max_prompt_image_size, max_prompt_images, supported_media_types) + return FluffyModelCapabilitiesOverrideLimitsVision(max_prompt_image_size, max_prompt_images, supported_media_types) def to_dict(self) -> dict: result: dict = {} @@ -1112,113 +945,6 @@ def to_dict(self) -> dict: result["supported_media_types"] = from_union([lambda x: from_list(from_str, x), from_none], self.supported_media_types) return result -@dataclass -class ModelCapabilitiesOverrideLimits: - """Token limits for prompts, outputs, and context window""" - - max_context_window_tokens: int | None = None - """Maximum total context window size in tokens""" - - max_output_tokens: int | None = None - max_prompt_tokens: int | None = None - vision: ModelCapabilitiesOverrideLimitsVision | None = None - - @staticmethod - def from_dict(obj: Any) -> 'ModelCapabilitiesOverrideLimits': - assert isinstance(obj, dict) - max_context_window_tokens = from_union([from_int, from_none], obj.get("max_context_window_tokens")) - max_output_tokens = from_union([from_int, from_none], obj.get("max_output_tokens")) - max_prompt_tokens = from_union([from_int, from_none], obj.get("max_prompt_tokens")) - vision = from_union([ModelCapabilitiesOverrideLimitsVision.from_dict, from_none], obj.get("vision")) - return ModelCapabilitiesOverrideLimits(max_context_window_tokens, max_output_tokens, max_prompt_tokens, vision) - - def to_dict(self) -> dict: - result: dict = {} - if self.max_context_window_tokens is not None: - result["max_context_window_tokens"] = from_union([from_int, from_none], self.max_context_window_tokens) - if self.max_output_tokens is not None: - result["max_output_tokens"] = from_union([from_int, from_none], self.max_output_tokens) - if self.max_prompt_tokens is not None: - result["max_prompt_tokens"] = from_union([from_int, from_none], self.max_prompt_tokens) - if self.vision is not None: - result["vision"] = from_union([lambda x: to_class(ModelCapabilitiesOverrideLimitsVision, x), from_none], self.vision) - return result - -@dataclass -class ModelCapabilitiesOverrideSupports: - """Feature flags indicating what the model supports""" - - reasoning_effort: bool | None = None - vision: bool | None = None - - @staticmethod - def from_dict(obj: Any) -> 'ModelCapabilitiesOverrideSupports': - assert isinstance(obj, dict) - reasoning_effort = from_union([from_bool, from_none], obj.get("reasoningEffort")) - vision = from_union([from_bool, from_none], obj.get("vision")) - return ModelCapabilitiesOverrideSupports(reasoning_effort, vision) - - def to_dict(self) -> dict: - result: dict = {} - if self.reasoning_effort is not None: - result["reasoningEffort"] = from_union([from_bool, from_none], self.reasoning_effort) - if self.vision is not None: - result["vision"] = from_union([from_bool, from_none], self.vision) - return result - -@dataclass -class ModelCapabilitiesOverride: - """Override individual model capabilities resolved by the runtime""" - - limits: ModelCapabilitiesOverrideLimits | None = None - """Token limits for prompts, outputs, and context window""" - - supports: ModelCapabilitiesOverrideSupports | None = None - """Feature flags indicating what the model supports""" - - @staticmethod - def from_dict(obj: Any) -> 'ModelCapabilitiesOverride': - assert isinstance(obj, dict) - limits = from_union([ModelCapabilitiesOverrideLimits.from_dict, from_none], obj.get("limits")) - supports = from_union([ModelCapabilitiesOverrideSupports.from_dict, from_none], obj.get("supports")) - return ModelCapabilitiesOverride(limits, supports) - - def to_dict(self) -> dict: - result: dict = {} - if self.limits is not None: - result["limits"] = from_union([lambda x: to_class(ModelCapabilitiesOverrideLimits, x), from_none], self.limits) - if self.supports is not None: - result["supports"] = from_union([lambda x: to_class(ModelCapabilitiesOverrideSupports, x), from_none], self.supports) - return result - -@dataclass -class ModelSwitchToRequest: - model_id: str - """Model identifier to switch to""" - - model_capabilities: ModelCapabilitiesOverride | None = None - """Override individual model capabilities resolved by the runtime""" - - reasoning_effort: str | None = None - """Reasoning effort level to use for the model""" - - @staticmethod - def from_dict(obj: Any) -> 'ModelSwitchToRequest': - assert isinstance(obj, dict) - model_id = from_str(obj.get("modelId")) - model_capabilities = from_union([ModelCapabilitiesOverride.from_dict, from_none], obj.get("modelCapabilities")) - reasoning_effort = from_union([from_str, from_none], obj.get("reasoningEffort")) - return ModelSwitchToRequest(model_id, model_capabilities, reasoning_effort) - - def to_dict(self) -> dict: - result: dict = {} - result["modelId"] = from_str(self.model_id) - if self.model_capabilities is not None: - result["modelCapabilities"] = from_union([lambda x: to_class(ModelCapabilitiesOverride, x), from_none], self.model_capabilities) - if self.reasoning_effort is not None: - result["reasoningEffort"] = from_union([from_str, from_none], self.reasoning_effort) - return result - class SessionMode(Enum): """The agent mode. Valid values: "interactive", "plan", "autopilot".""" @@ -1226,22 +952,6 @@ class SessionMode(Enum): INTERACTIVE = "interactive" PLAN = "plan" -@dataclass -class ModeSetRequest: - mode: SessionMode - """The agent mode. Valid values: "interactive", "plan", "autopilot".""" - - @staticmethod - def from_dict(obj: Any) -> 'ModeSetRequest': - assert isinstance(obj, dict) - mode = SessionMode(obj.get("mode")) - return ModeSetRequest(mode) - - def to_dict(self) -> dict: - result: dict = {} - result["mode"] = to_enum(SessionMode, self.mode) - return result - @dataclass class NameGetResult: name: str | None = None @@ -1325,101 +1035,6 @@ class SessionSyncLevel(Enum): REPO_AND_USER = "repo_and_user" USER = "user" -@dataclass -class Workspace: - id: UUID - branch: str | None = None - chronicle_sync_dismissed: bool | None = None - created_at: datetime | None = None - cwd: str | None = None - git_root: str | None = None - host_type: HostType | None = None - mc_last_event_id: str | None = None - mc_session_id: str | None = None - mc_task_id: str | None = None - name: str | None = None - pr_create_sync_dismissed: bool | None = None - repository: str | None = None - session_sync_level: SessionSyncLevel | None = None - summary: str | None = None - summary_count: int | None = None - updated_at: datetime | None = None - - @staticmethod - def from_dict(obj: Any) -> 'Workspace': - assert isinstance(obj, dict) - id = UUID(obj.get("id")) - branch = from_union([from_str, from_none], obj.get("branch")) - chronicle_sync_dismissed = from_union([from_bool, from_none], obj.get("chronicle_sync_dismissed")) - created_at = from_union([from_datetime, from_none], obj.get("created_at")) - cwd = from_union([from_str, from_none], obj.get("cwd")) - git_root = from_union([from_str, from_none], obj.get("git_root")) - host_type = from_union([HostType, from_none], obj.get("host_type")) - mc_last_event_id = from_union([from_str, from_none], obj.get("mc_last_event_id")) - mc_session_id = from_union([from_str, from_none], obj.get("mc_session_id")) - mc_task_id = from_union([from_str, from_none], obj.get("mc_task_id")) - name = from_union([from_str, from_none], obj.get("name")) - pr_create_sync_dismissed = from_union([from_bool, from_none], obj.get("pr_create_sync_dismissed")) - repository = from_union([from_str, from_none], obj.get("repository")) - session_sync_level = from_union([SessionSyncLevel, from_none], obj.get("session_sync_level")) - summary = from_union([from_str, from_none], obj.get("summary")) - summary_count = from_union([from_int, from_none], obj.get("summary_count")) - updated_at = from_union([from_datetime, from_none], obj.get("updated_at")) - return Workspace(id, branch, chronicle_sync_dismissed, created_at, cwd, git_root, host_type, mc_last_event_id, mc_session_id, mc_task_id, name, pr_create_sync_dismissed, repository, session_sync_level, summary, summary_count, updated_at) - - def to_dict(self) -> dict: - result: dict = {} - result["id"] = str(self.id) - if self.branch is not None: - result["branch"] = from_union([from_str, from_none], self.branch) - if self.chronicle_sync_dismissed is not None: - result["chronicle_sync_dismissed"] = from_union([from_bool, from_none], self.chronicle_sync_dismissed) - if self.created_at is not None: - result["created_at"] = from_union([lambda x: x.isoformat(), from_none], self.created_at) - if self.cwd is not None: - result["cwd"] = from_union([from_str, from_none], self.cwd) - if self.git_root is not None: - result["git_root"] = from_union([from_str, from_none], self.git_root) - if self.host_type is not None: - result["host_type"] = from_union([lambda x: to_enum(HostType, x), from_none], self.host_type) - if self.mc_last_event_id is not None: - result["mc_last_event_id"] = from_union([from_str, from_none], self.mc_last_event_id) - if self.mc_session_id is not None: - result["mc_session_id"] = from_union([from_str, from_none], self.mc_session_id) - if self.mc_task_id is not None: - result["mc_task_id"] = from_union([from_str, from_none], self.mc_task_id) - if self.name is not None: - result["name"] = from_union([from_str, from_none], self.name) - if self.pr_create_sync_dismissed is not None: - result["pr_create_sync_dismissed"] = from_union([from_bool, from_none], self.pr_create_sync_dismissed) - if self.repository is not None: - result["repository"] = from_union([from_str, from_none], self.repository) - if self.session_sync_level is not None: - result["session_sync_level"] = from_union([lambda x: to_enum(SessionSyncLevel, x), from_none], self.session_sync_level) - if self.summary is not None: - result["summary"] = from_union([from_str, from_none], self.summary) - if self.summary_count is not None: - result["summary_count"] = from_union([from_int, from_none], self.summary_count) - if self.updated_at is not None: - result["updated_at"] = from_union([lambda x: x.isoformat(), from_none], self.updated_at) - return result - -@dataclass -class WorkspacesGetWorkspaceResult: - workspace: Workspace | None = None - """Current workspace metadata, or null if not available""" - - @staticmethod - def from_dict(obj: Any) -> 'WorkspacesGetWorkspaceResult': - assert isinstance(obj, dict) - workspace = from_union([Workspace.from_dict, from_none], obj.get("workspace")) - return WorkspacesGetWorkspaceResult(workspace) - - def to_dict(self) -> dict: - result: dict = {} - result["workspace"] = from_union([lambda x: to_class(Workspace, x), from_none], self.workspace) - return result - @dataclass class WorkspacesListFilesResult: files: list[str] @@ -1489,6 +1104,23 @@ def to_dict(self) -> dict: result["path"] = from_str(self.path) return result +class InstructionsSourcesLocation(Enum): + """Where this source lives — used for UI grouping""" + + REPOSITORY = "repository" + USER = "user" + WORKING_DIRECTORY = "working-directory" + +class InstructionsSourcesType(Enum): + """Category of instruction source — used for merge logic""" + + CHILD_INSTRUCTIONS = "child-instructions" + HOME = "home" + MODEL = "model" + NESTED_AGENTS = "nested-agents" + REPO = "repo" + VSCODE = "vscode" + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class FleetStartResult: @@ -1525,7 +1157,7 @@ def to_dict(self) -> dict: return result @dataclass -class Agent: +class AgentListAgent: description: str """Description of the agent's purpose""" @@ -1536,12 +1168,12 @@ class Agent: """Unique identifier of the custom agent""" @staticmethod - def from_dict(obj: Any) -> 'Agent': + def from_dict(obj: Any) -> 'AgentListAgent': assert isinstance(obj, dict) description = from_str(obj.get("description")) display_name = from_str(obj.get("displayName")) name = from_str(obj.get("name")) - return Agent(description, display_name, name) + return AgentListAgent(description, display_name, name) def to_dict(self) -> dict: result: dict = {} @@ -1550,25 +1182,10 @@ 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 AgentList: - agents: list[Agent] - """Available custom agents""" - - @staticmethod - def from_dict(obj: Any) -> 'AgentList': - assert isinstance(obj, dict) - agents = from_list(Agent.from_dict, obj.get("agents")) - return AgentList(agents) - - def to_dict(self) -> dict: - result: dict = {} - result["agents"] = from_list(lambda x: to_class(Agent, x), self.agents) - return result +class AgentSelectResultAgent: + """The newly selected custom agent""" -@dataclass -class AgentGetCurrentResultAgent: description: str """Description of the agent's purpose""" @@ -1579,12 +1196,12 @@ class AgentGetCurrentResultAgent: """Unique identifier of the custom agent""" @staticmethod - def from_dict(obj: Any) -> 'AgentGetCurrentResultAgent': + def from_dict(obj: Any) -> 'AgentSelectResultAgent': assert isinstance(obj, dict) description = from_str(obj.get("description")) display_name = from_str(obj.get("displayName")) name = from_str(obj.get("name")) - return AgentGetCurrentResultAgent(description, display_name, name) + return AgentSelectResultAgent(description, display_name, name) def to_dict(self) -> dict: result: dict = {} @@ -1595,74 +1212,12 @@ def to_dict(self) -> dict: # Experimental: this type is part of an experimental API and may change or be removed. @dataclass -class AgentGetCurrentResult: - agent: AgentGetCurrentResultAgent | None = None - """Currently selected custom agent, or null if using the default agent""" +class AgentSelectRequest: + name: str + """Name of the custom agent to select""" @staticmethod - def from_dict(obj: Any) -> 'AgentGetCurrentResult': - assert isinstance(obj, dict) - agent = from_union([AgentGetCurrentResultAgent.from_dict, from_none], obj.get("agent")) - return AgentGetCurrentResult(agent) - - def to_dict(self) -> dict: - result: dict = {} - result["agent"] = from_union([lambda x: to_class(AgentGetCurrentResultAgent, x), from_none], self.agent) - return result - -@dataclass -class AgentSelectAgent: - """The newly selected custom agent""" - - description: str - """Description of the agent's purpose""" - - display_name: str - """Human-readable display name""" - - name: str - """Unique identifier of the custom agent""" - - @staticmethod - def from_dict(obj: Any) -> 'AgentSelectAgent': - assert isinstance(obj, dict) - description = from_str(obj.get("description")) - display_name = from_str(obj.get("displayName")) - name = from_str(obj.get("name")) - return AgentSelectAgent(description, display_name, name) - - def to_dict(self) -> dict: - result: dict = {} - result["description"] = from_str(self.description) - result["displayName"] = from_str(self.display_name) - 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 AgentSelectResult: - agent: AgentSelectAgent - """The newly selected custom agent""" - - @staticmethod - def from_dict(obj: Any) -> 'AgentSelectResult': - assert isinstance(obj, dict) - agent = AgentSelectAgent.from_dict(obj.get("agent")) - return AgentSelectResult(agent) - - def to_dict(self) -> dict: - result: dict = {} - result["agent"] = to_class(AgentSelectAgent, self.agent) - return result - -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class AgentSelectRequest: - name: str - """Name of the custom agent to select""" - - @staticmethod - def from_dict(obj: Any) -> 'AgentSelectRequest': + def from_dict(obj: Any) -> 'AgentSelectRequest': assert isinstance(obj, dict) name = from_str(obj.get("name")) return AgentSelectRequest(name) @@ -1673,7 +1228,7 @@ def to_dict(self) -> dict: return result @dataclass -class AgentReloadAgent: +class AgentReloadResultAgent: description: str """Description of the agent's purpose""" @@ -1684,12 +1239,12 @@ class AgentReloadAgent: """Unique identifier of the custom agent""" @staticmethod - def from_dict(obj: Any) -> 'AgentReloadAgent': + def from_dict(obj: Any) -> 'AgentReloadResultAgent': assert isinstance(obj, dict) description = from_str(obj.get("description")) display_name = from_str(obj.get("displayName")) name = from_str(obj.get("name")) - return AgentReloadAgent(description, display_name, name) + return AgentReloadResultAgent(description, display_name, name) def to_dict(self) -> dict: result: dict = {} @@ -1698,23 +1253,6 @@ 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 AgentReloadResult: - agents: list[AgentReloadAgent] - """Reloaded custom agents""" - - @staticmethod - def from_dict(obj: Any) -> 'AgentReloadResult': - assert isinstance(obj, dict) - agents = from_list(AgentReloadAgent.from_dict, obj.get("agents")) - return AgentReloadResult(agents) - - def to_dict(self) -> dict: - result: dict = {} - result["agents"] = from_list(lambda x: to_class(AgentReloadAgent, x), self.agents) - return result - @dataclass class Skill: description: str @@ -1757,23 +1295,6 @@ def to_dict(self) -> dict: result["path"] = from_union([from_str, from_none], self.path) return result -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class SkillList: - skills: list[Skill] - """Available skills""" - - @staticmethod - def from_dict(obj: Any) -> 'SkillList': - assert isinstance(obj, dict) - skills = from_list(Skill.from_dict, obj.get("skills")) - return SkillList(skills) - - def to_dict(self) -> dict: - result: 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 SkillsEnableRequest: @@ -1808,65 +1329,6 @@ def to_dict(self) -> dict: result["name"] = from_str(self.name) return result -class MCPServerStatus(Enum): - """Connection status: connected, failed, needs-auth, pending, disabled, or not_configured""" - - CONNECTED = "connected" - DISABLED = "disabled" - FAILED = "failed" - NEEDS_AUTH = "needs-auth" - NOT_CONFIGURED = "not_configured" - PENDING = "pending" - -@dataclass -class MCPServer: - name: str - """Server name (config key)""" - - status: MCPServerStatus - """Connection status: connected, failed, needs-auth, pending, disabled, or not_configured""" - - error: str | None = None - """Error message if the server failed to connect""" - - source: MCPServerSource | None = None - """Configuration source: user, workspace, plugin, or builtin""" - - @staticmethod - def from_dict(obj: Any) -> 'MCPServer': - assert isinstance(obj, dict) - name = from_str(obj.get("name")) - status = MCPServerStatus(obj.get("status")) - error = from_union([from_str, from_none], obj.get("error")) - source = from_union([MCPServerSource, from_none], obj.get("source")) - return MCPServer(name, status, error, source) - - def to_dict(self) -> dict: - result: dict = {} - result["name"] = from_str(self.name) - result["status"] = to_enum(MCPServerStatus, self.status) - if self.error is not None: - result["error"] = from_union([from_str, from_none], self.error) - if self.source is not None: - result["source"] = from_union([lambda x: to_enum(MCPServerSource, x), from_none], self.source) - return result - -@dataclass -class MCPServerList: - servers: list[MCPServer] - """Configured MCP servers""" - - @staticmethod - def from_dict(obj: Any) -> 'MCPServerList': - assert isinstance(obj, dict) - servers = from_list(MCPServer.from_dict, obj.get("servers")) - return MCPServerList(servers) - - def to_dict(self) -> dict: - result: dict = {} - result["servers"] = from_list(lambda x: to_class(MCPServer, x), self.servers) - return result - @dataclass class MCPEnableRequest: server_name: str @@ -1931,23 +1393,6 @@ def to_dict(self) -> dict: result["version"] = from_union([from_str, from_none], self.version) return result -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class PluginList: - plugins: list[Plugin] - """Installed plugins""" - - @staticmethod - def from_dict(obj: Any) -> 'PluginList': - assert isinstance(obj, dict) - plugins = from_list(Plugin.from_dict, obj.get("plugins")) - return PluginList(plugins) - - def to_dict(self) -> dict: - result: dict = {} - result["plugins"] = from_list(lambda x: to_class(Plugin, x), self.plugins) - return result - class ExtensionSource(Enum): """Discovery source: project (.github/extensions/) or user (~/.copilot/extensions/)""" @@ -1962,60 +1407,6 @@ class ExtensionStatus(Enum): RUNNING = "running" STARTING = "starting" -@dataclass -class Extension: - id: str - """Source-qualified ID (e.g., 'project:my-ext', 'user:auth-helper')""" - - name: str - """Extension name (directory name)""" - - source: ExtensionSource - """Discovery source: project (.github/extensions/) or user (~/.copilot/extensions/)""" - - status: ExtensionStatus - """Current status: running, disabled, failed, or starting""" - - pid: int | None = None - """Process ID if the extension is running""" - - @staticmethod - def from_dict(obj: Any) -> 'Extension': - assert isinstance(obj, dict) - id = from_str(obj.get("id")) - name = from_str(obj.get("name")) - source = ExtensionSource(obj.get("source")) - status = ExtensionStatus(obj.get("status")) - pid = from_union([from_int, from_none], obj.get("pid")) - return Extension(id, name, source, status, pid) - - def to_dict(self) -> dict: - result: dict = {} - result["id"] = from_str(self.id) - result["name"] = from_str(self.name) - result["source"] = to_enum(ExtensionSource, self.source) - result["status"] = to_enum(ExtensionStatus, self.status) - if self.pid is not None: - result["pid"] = from_union([from_int, from_none], self.pid) - return result - -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class ExtensionList: - extensions: list[Extension] - """Discovered extensions and their current status""" - - @staticmethod - def from_dict(obj: Any) -> 'ExtensionList': - assert isinstance(obj, dict) - extensions = from_list(Extension.from_dict, obj.get("extensions")) - return ExtensionList(extensions) - - def to_dict(self) -> dict: - result: dict = {} - result["extensions"] = from_list(lambda x: to_class(Extension, x), self.extensions) - return result - # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ExtensionsEnableRequest: @@ -2051,15 +1442,15 @@ def to_dict(self) -> dict: return result @dataclass -class HandleToolCallResult: +class CommandsHandlePendingCommandResult: success: bool - """Whether the tool call result was handled successfully""" + """Whether the command was handled successfully""" @staticmethod - def from_dict(obj: Any) -> 'HandleToolCallResult': + def from_dict(obj: Any) -> 'CommandsHandlePendingCommandResult': assert isinstance(obj, dict) success = from_bool(obj.get("success")) - return HandleToolCallResult(success) + return CommandsHandlePendingCommandResult(success) def to_dict(self) -> dict: result: dict = {} @@ -2067,847 +1458,2314 @@ def to_dict(self) -> dict: return result @dataclass -class ToolCallResult: - text_result_for_llm: str - """Text result to send back to the LLM""" +class CommandsHandlePendingCommandRequest: + request_id: str + """Request ID from the command invocation event""" error: str | None = None - """Error message if the tool call failed""" - - result_type: str | None = None - """Type of the tool result""" - - tool_telemetry: dict[str, Any] | None = None - """Telemetry data from tool execution""" + """Error message if the command handler failed""" @staticmethod - def from_dict(obj: Any) -> 'ToolCallResult': + def from_dict(obj: Any) -> 'CommandsHandlePendingCommandRequest': assert isinstance(obj, dict) - text_result_for_llm = from_str(obj.get("textResultForLlm")) + request_id = from_str(obj.get("requestId")) error = from_union([from_str, from_none], obj.get("error")) - result_type = from_union([from_str, from_none], obj.get("resultType")) - tool_telemetry = from_union([lambda x: from_dict(lambda x: x, x), from_none], obj.get("toolTelemetry")) - return ToolCallResult(text_result_for_llm, error, result_type, tool_telemetry) + return CommandsHandlePendingCommandRequest(request_id, error) def to_dict(self) -> dict: result: dict = {} - result["textResultForLlm"] = from_str(self.text_result_for_llm) + result["requestId"] = from_str(self.request_id) if self.error is not None: result["error"] = from_union([from_str, from_none], self.error) - if self.result_type is not None: - result["resultType"] = from_union([from_str, from_none], self.result_type) - if self.tool_telemetry is not None: - result["toolTelemetry"] = from_union([lambda x: from_dict(lambda x: x, x), from_none], self.tool_telemetry) return result +class UIElicitationSchemaPropertyStringFormat(Enum): + DATE = "date" + DATE_TIME = "date-time" + EMAIL = "email" + URI = "uri" + @dataclass -class ToolsHandlePendingToolCallRequest: - request_id: str - """Request ID of the pending tool call""" - - error: str | None = None - """Error message if the tool call failed""" - - result: ToolCallResult | str | None = None - """Tool call result (string or expanded result object)""" +class FluffyUIElicitationArrayAnyOfFieldItemsAnyOf: + const: str + title: str @staticmethod - def from_dict(obj: Any) -> 'ToolsHandlePendingToolCallRequest': + def from_dict(obj: Any) -> 'FluffyUIElicitationArrayAnyOfFieldItemsAnyOf': assert isinstance(obj, dict) - request_id = from_str(obj.get("requestId")) - error = from_union([from_str, from_none], obj.get("error")) - result = from_union([ToolCallResult.from_dict, from_str, from_none], obj.get("result")) - return ToolsHandlePendingToolCallRequest(request_id, error, result) + const = from_str(obj.get("const")) + title = from_str(obj.get("title")) + return FluffyUIElicitationArrayAnyOfFieldItemsAnyOf(const, title) def to_dict(self) -> dict: result: dict = {} - result["requestId"] = from_str(self.request_id) - if self.error is not None: - result["error"] = from_union([from_str, from_none], self.error) - if self.result is not None: - result["result"] = from_union([lambda x: to_class(ToolCallResult, x), from_str, from_none], self.result) + result["const"] = from_str(self.const) + result["title"] = from_str(self.title) return result @dataclass -class CommandsHandlePendingCommandResult: - success: bool - """Whether the command was handled successfully""" +class UIElicitationSchemaPropertyOneOf: + const: str + title: str @staticmethod - def from_dict(obj: Any) -> 'CommandsHandlePendingCommandResult': + def from_dict(obj: Any) -> 'UIElicitationSchemaPropertyOneOf': assert isinstance(obj, dict) - success = from_bool(obj.get("success")) - return CommandsHandlePendingCommandResult(success) + const = from_str(obj.get("const")) + title = from_str(obj.get("title")) + return UIElicitationSchemaPropertyOneOf(const, title) def to_dict(self) -> dict: result: dict = {} - result["success"] = from_bool(self.success) + result["const"] = from_str(self.const) + result["title"] = from_str(self.title) return result -@dataclass -class CommandsHandlePendingCommandRequest: - request_id: str - """Request ID from the command invocation event""" +class UIElicitationSchemaPropertyNumberType(Enum): + ARRAY = "array" + BOOLEAN = "boolean" + INTEGER = "integer" + NUMBER = "number" + STRING = "string" - error: str | None = None - """Error message if the command handler failed""" +class RequestedSchemaType(Enum): + OBJECT = "object" + +@dataclass +class LogResult: + event_id: UUID + """The unique identifier of the emitted session event""" @staticmethod - def from_dict(obj: Any) -> 'CommandsHandlePendingCommandRequest': + def from_dict(obj: Any) -> 'LogResult': assert isinstance(obj, dict) - request_id = from_str(obj.get("requestId")) - error = from_union([from_str, from_none], obj.get("error")) - return CommandsHandlePendingCommandRequest(request_id, error) + event_id = UUID(obj.get("eventId")) + return LogResult(event_id) def to_dict(self) -> dict: result: dict = {} - result["requestId"] = from_str(self.request_id) - if self.error is not None: - result["error"] = from_union([from_str, from_none], self.error) + result["eventId"] = str(self.event_id) return result -class UIElicitationResponseAction(Enum): - """The user's response: accept (submitted), decline (rejected), or cancel (dismissed)""" +class SessionLogLevel(Enum): + """Log severity level. Determines how the message is displayed in the timeline. Defaults to + "info". + """ + ERROR = "error" + INFO = "info" + WARNING = "warning" - ACCEPT = "accept" - CANCEL = "cancel" - DECLINE = "decline" +@dataclass +class ShellExecResult: + process_id: str + """Unique identifier for tracking streamed output""" + + @staticmethod + def from_dict(obj: Any) -> 'ShellExecResult': + assert isinstance(obj, dict) + process_id = from_str(obj.get("processId")) + return ShellExecResult(process_id) + + def to_dict(self) -> dict: + result: dict = {} + result["processId"] = from_str(self.process_id) + return result @dataclass -class UIElicitationResponse: - """The elicitation response (accept with form values, decline, or cancel)""" +class ShellExecRequest: + command: str + """Shell command to execute""" - action: UIElicitationResponseAction - """The user's response: accept (submitted), decline (rejected), or cancel (dismissed)""" + cwd: str | None = None + """Working directory (defaults to session working directory)""" - content: dict[str, float | bool | list[str] | str] | None = None - """The form values submitted by the user (present when action is 'accept')""" + timeout: int | None = None + """Timeout in milliseconds (default: 30000)""" @staticmethod - def from_dict(obj: Any) -> 'UIElicitationResponse': + def from_dict(obj: Any) -> 'ShellExecRequest': assert isinstance(obj, dict) - action = UIElicitationResponseAction(obj.get("action")) - content = from_union([lambda x: from_dict(lambda x: from_union([from_float, from_bool, lambda x: from_list(from_str, x), from_str], x), x), from_none], obj.get("content")) - return UIElicitationResponse(action, content) + command = from_str(obj.get("command")) + cwd = from_union([from_str, from_none], obj.get("cwd")) + timeout = from_union([from_int, from_none], obj.get("timeout")) + return ShellExecRequest(command, cwd, timeout) def to_dict(self) -> dict: result: dict = {} - result["action"] = to_enum(UIElicitationResponseAction, self.action) - if self.content is not None: - result["content"] = from_union([lambda x: from_dict(lambda x: from_union([to_float, from_bool, lambda x: from_list(from_str, x), from_str], x), x), from_none], self.content) + result["command"] = from_str(self.command) + if self.cwd is not None: + result["cwd"] = from_union([from_str, from_none], self.cwd) + if self.timeout is not None: + result["timeout"] = from_union([from_int, from_none], self.timeout) return result -class UIElicitationSchemaPropertyStringFormat(Enum): - DATE = "date" - DATE_TIME = "date-time" - EMAIL = "email" - URI = "uri" - @dataclass -class UIElicitationArrayAnyOfFieldItemsAnyOf: - const: str - title: str +class ShellKillResult: + killed: bool + """Whether the signal was sent successfully""" @staticmethod - def from_dict(obj: Any) -> 'UIElicitationArrayAnyOfFieldItemsAnyOf': + def from_dict(obj: Any) -> 'ShellKillResult': assert isinstance(obj, dict) - const = from_str(obj.get("const")) - title = from_str(obj.get("title")) - return UIElicitationArrayAnyOfFieldItemsAnyOf(const, title) + killed = from_bool(obj.get("killed")) + return ShellKillResult(killed) def to_dict(self) -> dict: result: dict = {} - result["const"] = from_str(self.const) - result["title"] = from_str(self.title) + result["killed"] = from_bool(self.killed) return result -class ItemsType(Enum): - STRING = "string" +class ShellKillSignal(Enum): + """Signal to send (default: SIGTERM)""" + + SIGINT = "SIGINT" + SIGKILL = "SIGKILL" + SIGTERM = "SIGTERM" @dataclass -class UIElicitationArrayFieldItems: - enum: list[str] | None = None - type: ItemsType | None = None - any_of: list[UIElicitationArrayAnyOfFieldItemsAnyOf] | None = None +class HistoryCompactContextWindow: + """Post-compaction context window usage breakdown""" + + current_tokens: int + """Current total tokens in the context window (system + conversation + tool definitions)""" + + messages_length: int + """Current number of messages in the conversation""" + + token_limit: int + """Maximum token count for the model's context window""" + + conversation_tokens: int | None = None + """Token count from non-system messages (user, assistant, tool)""" + + system_tokens: int | None = None + """Token count from system message(s)""" + + tool_definitions_tokens: int | None = None + """Token count from tool definitions""" @staticmethod - def from_dict(obj: Any) -> 'UIElicitationArrayFieldItems': + def from_dict(obj: Any) -> 'HistoryCompactContextWindow': assert isinstance(obj, dict) - enum = from_union([lambda x: from_list(from_str, x), from_none], obj.get("enum")) - type = from_union([ItemsType, from_none], obj.get("type")) - any_of = from_union([lambda x: from_list(UIElicitationArrayAnyOfFieldItemsAnyOf.from_dict, x), from_none], obj.get("anyOf")) - return UIElicitationArrayFieldItems(enum, type, any_of) + current_tokens = from_int(obj.get("currentTokens")) + messages_length = from_int(obj.get("messagesLength")) + token_limit = from_int(obj.get("tokenLimit")) + conversation_tokens = from_union([from_int, from_none], obj.get("conversationTokens")) + system_tokens = from_union([from_int, from_none], obj.get("systemTokens")) + tool_definitions_tokens = from_union([from_int, from_none], obj.get("toolDefinitionsTokens")) + return HistoryCompactContextWindow(current_tokens, messages_length, token_limit, conversation_tokens, system_tokens, tool_definitions_tokens) def to_dict(self) -> dict: result: dict = {} - if self.enum is not None: - result["enum"] = from_union([lambda x: from_list(from_str, x), from_none], self.enum) - if self.type is not None: - result["type"] = from_union([lambda x: to_enum(ItemsType, x), from_none], self.type) - if self.any_of is not None: - result["anyOf"] = from_union([lambda x: from_list(lambda x: to_class(UIElicitationArrayAnyOfFieldItemsAnyOf, x), x), from_none], self.any_of) + result["currentTokens"] = from_int(self.current_tokens) + result["messagesLength"] = from_int(self.messages_length) + result["tokenLimit"] = from_int(self.token_limit) + if self.conversation_tokens is not None: + result["conversationTokens"] = from_union([from_int, from_none], self.conversation_tokens) + if self.system_tokens is not None: + result["systemTokens"] = from_union([from_int, from_none], self.system_tokens) + if self.tool_definitions_tokens is not None: + result["toolDefinitionsTokens"] = from_union([from_int, from_none], self.tool_definitions_tokens) return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass -class UIElicitationStringOneOfFieldOneOf: - const: str - title: str +class HistoryTruncateResult: + events_removed: int + """Number of events that were removed""" @staticmethod - def from_dict(obj: Any) -> 'UIElicitationStringOneOfFieldOneOf': + def from_dict(obj: Any) -> 'HistoryTruncateResult': assert isinstance(obj, dict) - const = from_str(obj.get("const")) - title = from_str(obj.get("title")) - return UIElicitationStringOneOfFieldOneOf(const, title) + events_removed = from_int(obj.get("eventsRemoved")) + return HistoryTruncateResult(events_removed) def to_dict(self) -> dict: result: dict = {} - result["const"] = from_str(self.const) - result["title"] = from_str(self.title) + result["eventsRemoved"] = from_int(self.events_removed) return result -class UIElicitationSchemaPropertyNumberType(Enum): - ARRAY = "array" - BOOLEAN = "boolean" - INTEGER = "integer" - NUMBER = "number" - STRING = "string" - +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass -class UIElicitationSchemaProperty: - type: UIElicitationSchemaPropertyNumberType - default: float | bool | list[str] | str | None = None - description: str | None = None - enum: list[str] | None = None - enum_names: list[str] | None = None - title: str | None = None - one_of: list[UIElicitationStringOneOfFieldOneOf] | None = None - items: UIElicitationArrayFieldItems | None = None - max_items: float | None = None - min_items: float | None = None - format: UIElicitationSchemaPropertyStringFormat | None = None - max_length: float | None = None - min_length: float | None = None - maximum: float | None = None - minimum: float | None = None +class HistoryTruncateRequest: + event_id: str + """Event ID to truncate to. This event and all events after it are removed from the session.""" @staticmethod - def from_dict(obj: Any) -> 'UIElicitationSchemaProperty': + def from_dict(obj: Any) -> 'HistoryTruncateRequest': assert isinstance(obj, dict) - type = UIElicitationSchemaPropertyNumberType(obj.get("type")) - default = from_union([from_float, from_bool, lambda x: from_list(from_str, x), from_str, from_none], obj.get("default")) - description = from_union([from_str, from_none], obj.get("description")) - enum = from_union([lambda x: from_list(from_str, x), from_none], obj.get("enum")) - enum_names = from_union([lambda x: from_list(from_str, x), from_none], obj.get("enumNames")) - title = from_union([from_str, from_none], obj.get("title")) - one_of = from_union([lambda x: from_list(UIElicitationStringOneOfFieldOneOf.from_dict, x), from_none], obj.get("oneOf")) - items = from_union([UIElicitationArrayFieldItems.from_dict, from_none], obj.get("items")) - max_items = from_union([from_float, from_none], obj.get("maxItems")) - min_items = from_union([from_float, from_none], obj.get("minItems")) - format = from_union([UIElicitationSchemaPropertyStringFormat, from_none], obj.get("format")) - max_length = from_union([from_float, from_none], obj.get("maxLength")) - min_length = from_union([from_float, from_none], obj.get("minLength")) - maximum = from_union([from_float, from_none], obj.get("maximum")) - minimum = from_union([from_float, from_none], obj.get("minimum")) - return UIElicitationSchemaProperty(type, default, description, enum, enum_names, title, one_of, items, max_items, min_items, format, max_length, min_length, maximum, minimum) + event_id = from_str(obj.get("eventId")) + return HistoryTruncateRequest(event_id) + + def to_dict(self) -> dict: + result: dict = {} + result["eventId"] = from_str(self.event_id) + return result + +@dataclass +class UsageMetricsCodeChanges: + """Aggregated code change metrics""" + + files_modified_count: int + """Number of distinct files modified""" + + lines_added: int + """Total lines of code added""" + + lines_removed: int + """Total lines of code removed""" + + @staticmethod + def from_dict(obj: Any) -> 'UsageMetricsCodeChanges': + assert isinstance(obj, dict) + files_modified_count = from_int(obj.get("filesModifiedCount")) + lines_added = from_int(obj.get("linesAdded")) + lines_removed = from_int(obj.get("linesRemoved")) + return UsageMetricsCodeChanges(files_modified_count, lines_added, lines_removed) + + def to_dict(self) -> dict: + result: dict = {} + result["filesModifiedCount"] = from_int(self.files_modified_count) + result["linesAdded"] = from_int(self.lines_added) + result["linesRemoved"] = from_int(self.lines_removed) + return result + +@dataclass +class UsageMetricsModelMetricRequests: + """Request count and cost metrics for this model""" + + cost: float + """User-initiated premium request cost (with multiplier applied)""" + + count: int + """Number of API requests made with this model""" + + @staticmethod + def from_dict(obj: Any) -> 'UsageMetricsModelMetricRequests': + assert isinstance(obj, dict) + cost = from_float(obj.get("cost")) + count = from_int(obj.get("count")) + return UsageMetricsModelMetricRequests(cost, count) + + def to_dict(self) -> dict: + result: dict = {} + result["cost"] = to_float(self.cost) + result["count"] = from_int(self.count) + return result + +@dataclass +class UsageMetricsModelMetricUsage: + """Token usage metrics for this model""" + + cache_read_tokens: int + """Total tokens read from prompt cache""" + + cache_write_tokens: int + """Total tokens written to prompt cache""" + + input_tokens: int + """Total input tokens consumed""" + + output_tokens: int + """Total output tokens produced""" + + reasoning_tokens: int | None = None + """Total output tokens used for reasoning""" + + @staticmethod + def from_dict(obj: Any) -> 'UsageMetricsModelMetricUsage': + assert isinstance(obj, dict) + cache_read_tokens = from_int(obj.get("cacheReadTokens")) + cache_write_tokens = from_int(obj.get("cacheWriteTokens")) + input_tokens = from_int(obj.get("inputTokens")) + output_tokens = from_int(obj.get("outputTokens")) + reasoning_tokens = from_union([from_int, from_none], obj.get("reasoningTokens")) + return UsageMetricsModelMetricUsage(cache_read_tokens, cache_write_tokens, input_tokens, output_tokens, reasoning_tokens) + + def to_dict(self) -> dict: + result: dict = {} + result["cacheReadTokens"] = from_int(self.cache_read_tokens) + result["cacheWriteTokens"] = from_int(self.cache_write_tokens) + result["inputTokens"] = from_int(self.input_tokens) + result["outputTokens"] = from_int(self.output_tokens) + if self.reasoning_tokens is not None: + result["reasoningTokens"] = from_union([from_int, from_none], self.reasoning_tokens) + return result + +@dataclass +class SessionFSReadFileResult: + content: str + """File content as UTF-8 string""" + + @staticmethod + def from_dict(obj: Any) -> 'SessionFSReadFileResult': + assert isinstance(obj, dict) + content = from_str(obj.get("content")) + return SessionFSReadFileResult(content) + + def to_dict(self) -> dict: + result: dict = {} + result["content"] = from_str(self.content) + return result + +@dataclass +class SessionFSReadFileRequest: + path: str + """Path using SessionFs conventions""" + + session_id: str + """Target session identifier""" + + @staticmethod + def from_dict(obj: Any) -> 'SessionFSReadFileRequest': + assert isinstance(obj, dict) + path = from_str(obj.get("path")) + session_id = from_str(obj.get("sessionId")) + return SessionFSReadFileRequest(path, session_id) + + def to_dict(self) -> dict: + result: dict = {} + result["path"] = from_str(self.path) + result["sessionId"] = from_str(self.session_id) + return result + +@dataclass +class SessionFSWriteFileRequest: + content: str + """Content to write""" + + path: str + """Path using SessionFs conventions""" + + session_id: str + """Target session identifier""" + + mode: int | None = None + """Optional POSIX-style mode for newly created files""" + + @staticmethod + def from_dict(obj: Any) -> 'SessionFSWriteFileRequest': + assert isinstance(obj, dict) + content = from_str(obj.get("content")) + path = from_str(obj.get("path")) + session_id = from_str(obj.get("sessionId")) + mode = from_union([from_int, from_none], obj.get("mode")) + return SessionFSWriteFileRequest(content, path, session_id, mode) + + def to_dict(self) -> dict: + result: dict = {} + result["content"] = from_str(self.content) + result["path"] = from_str(self.path) + result["sessionId"] = from_str(self.session_id) + if self.mode is not None: + result["mode"] = from_union([from_int, from_none], self.mode) + return result + +@dataclass +class SessionFSAppendFileRequest: + content: str + """Content to append""" + + path: str + """Path using SessionFs conventions""" + + session_id: str + """Target session identifier""" + + mode: int | None = None + """Optional POSIX-style mode for newly created files""" + + @staticmethod + def from_dict(obj: Any) -> 'SessionFSAppendFileRequest': + assert isinstance(obj, dict) + content = from_str(obj.get("content")) + path = from_str(obj.get("path")) + session_id = from_str(obj.get("sessionId")) + mode = from_union([from_int, from_none], obj.get("mode")) + return SessionFSAppendFileRequest(content, path, session_id, mode) + + def to_dict(self) -> dict: + result: dict = {} + result["content"] = from_str(self.content) + result["path"] = from_str(self.path) + result["sessionId"] = from_str(self.session_id) + if self.mode is not None: + result["mode"] = from_union([from_int, from_none], self.mode) + return result + +@dataclass +class SessionFSExistsResult: + exists: bool + """Whether the path exists""" + + @staticmethod + def from_dict(obj: Any) -> 'SessionFSExistsResult': + assert isinstance(obj, dict) + exists = from_bool(obj.get("exists")) + return SessionFSExistsResult(exists) + + def to_dict(self) -> dict: + result: dict = {} + result["exists"] = from_bool(self.exists) + return result + +@dataclass +class SessionFSExistsRequest: + path: str + """Path using SessionFs conventions""" + + session_id: str + """Target session identifier""" + + @staticmethod + def from_dict(obj: Any) -> 'SessionFSExistsRequest': + assert isinstance(obj, dict) + path = from_str(obj.get("path")) + session_id = from_str(obj.get("sessionId")) + return SessionFSExistsRequest(path, session_id) + + def to_dict(self) -> dict: + result: dict = {} + result["path"] = from_str(self.path) + result["sessionId"] = from_str(self.session_id) + return result + +@dataclass +class SessionFSStatResult: + birthtime: datetime + """ISO 8601 timestamp of creation""" + + is_directory: bool + """Whether the path is a directory""" + + is_file: bool + """Whether the path is a file""" + + mtime: datetime + """ISO 8601 timestamp of last modification""" + + size: int + """File size in bytes""" + + @staticmethod + def from_dict(obj: Any) -> 'SessionFSStatResult': + assert isinstance(obj, dict) + birthtime = from_datetime(obj.get("birthtime")) + is_directory = from_bool(obj.get("isDirectory")) + is_file = from_bool(obj.get("isFile")) + mtime = from_datetime(obj.get("mtime")) + size = from_int(obj.get("size")) + return SessionFSStatResult(birthtime, is_directory, is_file, mtime, size) + + def to_dict(self) -> dict: + result: dict = {} + result["birthtime"] = self.birthtime.isoformat() + result["isDirectory"] = from_bool(self.is_directory) + result["isFile"] = from_bool(self.is_file) + result["mtime"] = self.mtime.isoformat() + result["size"] = from_int(self.size) + return result + +@dataclass +class SessionFSStatRequest: + path: str + """Path using SessionFs conventions""" + + session_id: str + """Target session identifier""" + + @staticmethod + def from_dict(obj: Any) -> 'SessionFSStatRequest': + assert isinstance(obj, dict) + path = from_str(obj.get("path")) + session_id = from_str(obj.get("sessionId")) + return SessionFSStatRequest(path, session_id) + + def to_dict(self) -> dict: + result: dict = {} + result["path"] = from_str(self.path) + result["sessionId"] = from_str(self.session_id) + return result + +@dataclass +class SessionFSMkdirRequest: + path: str + """Path using SessionFs conventions""" + + session_id: str + """Target session identifier""" + + mode: int | None = None + """Optional POSIX-style mode for newly created directories""" + + recursive: bool | None = None + """Create parent directories as needed""" + + @staticmethod + def from_dict(obj: Any) -> 'SessionFSMkdirRequest': + assert isinstance(obj, dict) + path = from_str(obj.get("path")) + session_id = from_str(obj.get("sessionId")) + mode = from_union([from_int, from_none], obj.get("mode")) + recursive = from_union([from_bool, from_none], obj.get("recursive")) + return SessionFSMkdirRequest(path, session_id, mode, recursive) + + def to_dict(self) -> dict: + result: dict = {} + result["path"] = from_str(self.path) + result["sessionId"] = from_str(self.session_id) + if self.mode is not None: + result["mode"] = from_union([from_int, from_none], self.mode) + if self.recursive is not None: + result["recursive"] = from_union([from_bool, from_none], self.recursive) + return result + +@dataclass +class SessionFSReaddirResult: + entries: list[str] + """Entry names in the directory""" + + @staticmethod + def from_dict(obj: Any) -> 'SessionFSReaddirResult': + assert isinstance(obj, dict) + entries = from_list(from_str, obj.get("entries")) + return SessionFSReaddirResult(entries) + + def to_dict(self) -> dict: + result: dict = {} + result["entries"] = from_list(from_str, self.entries) + return result + +@dataclass +class SessionFSReaddirRequest: + path: str + """Path using SessionFs conventions""" + + session_id: str + """Target session identifier""" + + @staticmethod + def from_dict(obj: Any) -> 'SessionFSReaddirRequest': + assert isinstance(obj, dict) + path = from_str(obj.get("path")) + session_id = from_str(obj.get("sessionId")) + return SessionFSReaddirRequest(path, session_id) + + def to_dict(self) -> dict: + result: dict = {} + result["path"] = from_str(self.path) + result["sessionId"] = from_str(self.session_id) + return result + +class SessionFSReaddirWithTypesEntryType(Enum): + """Entry type""" + + DIRECTORY = "directory" + FILE = "file" + +@dataclass +class SessionFSReaddirWithTypesRequest: + path: str + """Path using SessionFs conventions""" + + session_id: str + """Target session identifier""" + + @staticmethod + def from_dict(obj: Any) -> 'SessionFSReaddirWithTypesRequest': + assert isinstance(obj, dict) + path = from_str(obj.get("path")) + session_id = from_str(obj.get("sessionId")) + return SessionFSReaddirWithTypesRequest(path, session_id) + + def to_dict(self) -> dict: + result: dict = {} + result["path"] = from_str(self.path) + result["sessionId"] = from_str(self.session_id) + return result + +@dataclass +class SessionFSRmRequest: + path: str + """Path using SessionFs conventions""" + + session_id: str + """Target session identifier""" + + force: bool | None = None + """Ignore errors if the path does not exist""" + + recursive: bool | None = None + """Remove directories and their contents recursively""" + + @staticmethod + def from_dict(obj: Any) -> 'SessionFSRmRequest': + assert isinstance(obj, dict) + path = from_str(obj.get("path")) + session_id = from_str(obj.get("sessionId")) + force = from_union([from_bool, from_none], obj.get("force")) + recursive = from_union([from_bool, from_none], obj.get("recursive")) + return SessionFSRmRequest(path, session_id, force, recursive) + + def to_dict(self) -> dict: + result: dict = {} + result["path"] = from_str(self.path) + result["sessionId"] = from_str(self.session_id) + if self.force is not None: + result["force"] = from_union([from_bool, from_none], self.force) + if self.recursive is not None: + result["recursive"] = from_union([from_bool, from_none], self.recursive) + return result + +@dataclass +class SessionFSRenameRequest: + dest: str + """Destination path using SessionFs conventions""" + + session_id: str + """Target session identifier""" + + src: str + """Source path using SessionFs conventions""" + + @staticmethod + def from_dict(obj: Any) -> 'SessionFSRenameRequest': + assert isinstance(obj, dict) + dest = from_str(obj.get("dest")) + session_id = from_str(obj.get("sessionId")) + src = from_str(obj.get("src")) + return SessionFSRenameRequest(dest, session_id, src) + + def to_dict(self) -> dict: + result: dict = {} + result["dest"] = from_str(self.dest) + result["sessionId"] = from_str(self.session_id) + result["src"] = from_str(self.src) + return result + +@dataclass +class ModelCapabilitiesLimits: + """Token limits for prompts, outputs, and context window""" + + max_context_window_tokens: int | None = None + """Maximum total context window size in tokens""" + + max_output_tokens: int | None = None + """Maximum number of output/completion tokens""" + + max_prompt_tokens: int | None = None + """Maximum number of prompt/input tokens""" + + vision: PurpleModelCapabilitiesLimitsVision | None = None + """Vision-specific limits""" + + @staticmethod + def from_dict(obj: Any) -> 'ModelCapabilitiesLimits': + assert isinstance(obj, dict) + max_context_window_tokens = from_union([from_int, from_none], obj.get("max_context_window_tokens")) + max_output_tokens = from_union([from_int, from_none], obj.get("max_output_tokens")) + max_prompt_tokens = from_union([from_int, from_none], obj.get("max_prompt_tokens")) + vision = from_union([PurpleModelCapabilitiesLimitsVision.from_dict, from_none], obj.get("vision")) + return ModelCapabilitiesLimits(max_context_window_tokens, max_output_tokens, max_prompt_tokens, vision) + + def to_dict(self) -> dict: + result: dict = {} + if self.max_context_window_tokens is not None: + result["max_context_window_tokens"] = from_union([from_int, from_none], self.max_context_window_tokens) + if self.max_output_tokens is not None: + result["max_output_tokens"] = from_union([from_int, from_none], self.max_output_tokens) + if self.max_prompt_tokens is not None: + result["max_prompt_tokens"] = from_union([from_int, from_none], self.max_prompt_tokens) + if self.vision is not None: + result["vision"] = from_union([lambda x: to_class(PurpleModelCapabilitiesLimitsVision, x), from_none], self.vision) + return result + +@dataclass +class MCPServerConfig: + """MCP server configuration (local/stdio or remote/http)""" + + args: list[str] | None = None + command: str | None = None + cwd: str | None = None + env: dict[str, str] | None = None + filter_mapping: dict[str, FilterMappingString] | FilterMappingString | None = None + is_default_server: bool | None = None + timeout: int | None = None + """Timeout in milliseconds for tool calls to this server.""" + + tools: list[str] | None = None + """Tools to include. Defaults to all tools if not specified.""" + + type: MCPServerConfigType | None = None + """Remote transport type. Defaults to "http" when omitted.""" + + headers: dict[str, str] | None = None + oauth_client_id: str | None = None + oauth_public_client: bool | None = None + url: str | None = None + + @staticmethod + def from_dict(obj: Any) -> 'MCPServerConfig': + assert isinstance(obj, dict) + args = from_union([lambda x: from_list(from_str, x), from_none], obj.get("args")) + command = from_union([from_str, from_none], obj.get("command")) + cwd = from_union([from_str, from_none], obj.get("cwd")) + env = from_union([lambda x: from_dict(from_str, x), from_none], obj.get("env")) + filter_mapping = from_union([lambda x: from_dict(FilterMappingString, x), FilterMappingString, from_none], obj.get("filterMapping")) + is_default_server = from_union([from_bool, from_none], obj.get("isDefaultServer")) + timeout = from_union([from_int, from_none], obj.get("timeout")) + tools = from_union([lambda x: from_list(from_str, x), from_none], obj.get("tools")) + type = from_union([MCPServerConfigType, from_none], obj.get("type")) + headers = from_union([lambda x: from_dict(from_str, x), from_none], obj.get("headers")) + oauth_client_id = from_union([from_str, from_none], obj.get("oauthClientId")) + oauth_public_client = from_union([from_bool, from_none], obj.get("oauthPublicClient")) + url = from_union([from_str, from_none], obj.get("url")) + return MCPServerConfig(args, command, cwd, env, filter_mapping, is_default_server, timeout, tools, type, headers, oauth_client_id, oauth_public_client, url) + + def to_dict(self) -> dict: + result: dict = {} + if self.args is not None: + result["args"] = from_union([lambda x: from_list(from_str, x), from_none], self.args) + if self.command is not None: + result["command"] = from_union([from_str, from_none], self.command) + if self.cwd is not None: + result["cwd"] = from_union([from_str, from_none], self.cwd) + if self.env is not None: + result["env"] = from_union([lambda x: from_dict(from_str, x), from_none], self.env) + if self.filter_mapping is not None: + result["filterMapping"] = from_union([lambda x: from_dict(lambda x: to_enum(FilterMappingString, x), x), lambda x: to_enum(FilterMappingString, x), from_none], self.filter_mapping) + if self.is_default_server is not None: + result["isDefaultServer"] = from_union([from_bool, from_none], self.is_default_server) + if self.timeout is not None: + result["timeout"] = from_union([from_int, from_none], self.timeout) + if self.tools is not None: + result["tools"] = from_union([lambda x: from_list(from_str, x), from_none], self.tools) + if self.type is not None: + result["type"] = from_union([lambda x: to_enum(MCPServerConfigType, x), from_none], self.type) + if self.headers is not None: + result["headers"] = from_union([lambda x: from_dict(from_str, x), from_none], self.headers) + if self.oauth_client_id is not None: + result["oauthClientId"] = from_union([from_str, from_none], self.oauth_client_id) + if self.oauth_public_client is not None: + result["oauthPublicClient"] = from_union([from_bool, from_none], self.oauth_public_client) + if self.url is not None: + result["url"] = from_union([from_str, from_none], self.url) + return result + +@dataclass +class MCPServerConfigValue: + """MCP server configuration (local/stdio or remote/http)""" + + args: list[str] | None = None + command: str | None = None + cwd: str | None = None + env: dict[str, str] | None = None + filter_mapping: dict[str, FilterMappingString] | FilterMappingString | None = None + is_default_server: bool | None = None + timeout: int | None = None + """Timeout in milliseconds for tool calls to this server.""" + + tools: list[str] | None = None + """Tools to include. Defaults to all tools if not specified.""" + + type: MCPServerConfigType | None = None + """Remote transport type. Defaults to "http" when omitted.""" + + headers: dict[str, str] | None = None + oauth_client_id: str | None = None + oauth_public_client: bool | None = None + url: str | None = None + + @staticmethod + def from_dict(obj: Any) -> 'MCPServerConfigValue': + assert isinstance(obj, dict) + args = from_union([lambda x: from_list(from_str, x), from_none], obj.get("args")) + command = from_union([from_str, from_none], obj.get("command")) + cwd = from_union([from_str, from_none], obj.get("cwd")) + env = from_union([lambda x: from_dict(from_str, x), from_none], obj.get("env")) + filter_mapping = from_union([lambda x: from_dict(FilterMappingString, x), FilterMappingString, from_none], obj.get("filterMapping")) + is_default_server = from_union([from_bool, from_none], obj.get("isDefaultServer")) + timeout = from_union([from_int, from_none], obj.get("timeout")) + tools = from_union([lambda x: from_list(from_str, x), from_none], obj.get("tools")) + type = from_union([MCPServerConfigType, from_none], obj.get("type")) + headers = from_union([lambda x: from_dict(from_str, x), from_none], obj.get("headers")) + oauth_client_id = from_union([from_str, from_none], obj.get("oauthClientId")) + oauth_public_client = from_union([from_bool, from_none], obj.get("oauthPublicClient")) + url = from_union([from_str, from_none], obj.get("url")) + return MCPServerConfigValue(args, command, cwd, env, filter_mapping, is_default_server, timeout, tools, type, headers, oauth_client_id, oauth_public_client, url) + + def to_dict(self) -> dict: + result: dict = {} + if self.args is not None: + result["args"] = from_union([lambda x: from_list(from_str, x), from_none], self.args) + if self.command is not None: + result["command"] = from_union([from_str, from_none], self.command) + if self.cwd is not None: + result["cwd"] = from_union([from_str, from_none], self.cwd) + if self.env is not None: + result["env"] = from_union([lambda x: from_dict(from_str, x), from_none], self.env) + if self.filter_mapping is not None: + result["filterMapping"] = from_union([lambda x: from_dict(lambda x: to_enum(FilterMappingString, x), x), lambda x: to_enum(FilterMappingString, x), from_none], self.filter_mapping) + if self.is_default_server is not None: + result["isDefaultServer"] = from_union([from_bool, from_none], self.is_default_server) + if self.timeout is not None: + result["timeout"] = from_union([from_int, from_none], self.timeout) + if self.tools is not None: + result["tools"] = from_union([lambda x: from_list(from_str, x), from_none], self.tools) + if self.type is not None: + result["type"] = from_union([lambda x: to_enum(MCPServerConfigType, x), from_none], self.type) + if self.headers is not None: + result["headers"] = from_union([lambda x: from_dict(from_str, x), from_none], self.headers) + if self.oauth_client_id is not None: + result["oauthClientId"] = from_union([from_str, from_none], self.oauth_client_id) + if self.oauth_public_client is not None: + result["oauthPublicClient"] = from_union([from_bool, from_none], self.oauth_public_client) + if self.url is not None: + result["url"] = from_union([from_str, from_none], self.url) + return result + +@dataclass +class MCPConfigAddRequestMCPServerConfig: + """MCP server configuration (local/stdio or remote/http)""" + + args: list[str] | None = None + command: str | None = None + cwd: str | None = None + env: dict[str, str] | None = None + filter_mapping: dict[str, FilterMappingString] | FilterMappingString | None = None + is_default_server: bool | None = None + timeout: int | None = None + """Timeout in milliseconds for tool calls to this server.""" + + tools: list[str] | None = None + """Tools to include. Defaults to all tools if not specified.""" + + type: MCPServerConfigType | None = None + """Remote transport type. Defaults to "http" when omitted.""" + + headers: dict[str, str] | None = None + oauth_client_id: str | None = None + oauth_public_client: bool | None = None + url: str | None = None + + @staticmethod + def from_dict(obj: Any) -> 'MCPConfigAddRequestMCPServerConfig': + assert isinstance(obj, dict) + args = from_union([lambda x: from_list(from_str, x), from_none], obj.get("args")) + command = from_union([from_str, from_none], obj.get("command")) + cwd = from_union([from_str, from_none], obj.get("cwd")) + env = from_union([lambda x: from_dict(from_str, x), from_none], obj.get("env")) + filter_mapping = from_union([lambda x: from_dict(FilterMappingString, x), FilterMappingString, from_none], obj.get("filterMapping")) + is_default_server = from_union([from_bool, from_none], obj.get("isDefaultServer")) + timeout = from_union([from_int, from_none], obj.get("timeout")) + tools = from_union([lambda x: from_list(from_str, x), from_none], obj.get("tools")) + type = from_union([MCPServerConfigType, from_none], obj.get("type")) + headers = from_union([lambda x: from_dict(from_str, x), from_none], obj.get("headers")) + oauth_client_id = from_union([from_str, from_none], obj.get("oauthClientId")) + oauth_public_client = from_union([from_bool, from_none], obj.get("oauthPublicClient")) + url = from_union([from_str, from_none], obj.get("url")) + return MCPConfigAddRequestMCPServerConfig(args, command, cwd, env, filter_mapping, is_default_server, timeout, tools, type, headers, oauth_client_id, oauth_public_client, url) + + def to_dict(self) -> dict: + result: dict = {} + if self.args is not None: + result["args"] = from_union([lambda x: from_list(from_str, x), from_none], self.args) + if self.command is not None: + result["command"] = from_union([from_str, from_none], self.command) + if self.cwd is not None: + result["cwd"] = from_union([from_str, from_none], self.cwd) + if self.env is not None: + result["env"] = from_union([lambda x: from_dict(from_str, x), from_none], self.env) + if self.filter_mapping is not None: + result["filterMapping"] = from_union([lambda x: from_dict(lambda x: to_enum(FilterMappingString, x), x), lambda x: to_enum(FilterMappingString, x), from_none], self.filter_mapping) + if self.is_default_server is not None: + result["isDefaultServer"] = from_union([from_bool, from_none], self.is_default_server) + if self.timeout is not None: + result["timeout"] = from_union([from_int, from_none], self.timeout) + if self.tools is not None: + result["tools"] = from_union([lambda x: from_list(from_str, x), from_none], self.tools) + if self.type is not None: + result["type"] = from_union([lambda x: to_enum(MCPServerConfigType, x), from_none], self.type) + if self.headers is not None: + result["headers"] = from_union([lambda x: from_dict(from_str, x), from_none], self.headers) + if self.oauth_client_id is not None: + result["oauthClientId"] = from_union([from_str, from_none], self.oauth_client_id) + if self.oauth_public_client is not None: + result["oauthPublicClient"] = from_union([from_bool, from_none], self.oauth_public_client) + if self.url is not None: + result["url"] = from_union([from_str, from_none], self.url) + return result + +@dataclass +class MCPConfigUpdateRequestMCPServerConfig: + """MCP server configuration (local/stdio or remote/http)""" + + args: list[str] | None = None + command: str | None = None + cwd: str | None = None + env: dict[str, str] | None = None + filter_mapping: dict[str, FilterMappingString] | FilterMappingString | None = None + is_default_server: bool | None = None + timeout: int | None = None + """Timeout in milliseconds for tool calls to this server.""" + + tools: list[str] | None = None + """Tools to include. Defaults to all tools if not specified.""" + + type: MCPServerConfigType | None = None + """Remote transport type. Defaults to "http" when omitted.""" + + headers: dict[str, str] | None = None + oauth_client_id: str | None = None + oauth_public_client: bool | None = None + url: str | None = None + + @staticmethod + def from_dict(obj: Any) -> 'MCPConfigUpdateRequestMCPServerConfig': + assert isinstance(obj, dict) + args = from_union([lambda x: from_list(from_str, x), from_none], obj.get("args")) + command = from_union([from_str, from_none], obj.get("command")) + cwd = from_union([from_str, from_none], obj.get("cwd")) + env = from_union([lambda x: from_dict(from_str, x), from_none], obj.get("env")) + filter_mapping = from_union([lambda x: from_dict(FilterMappingString, x), FilterMappingString, from_none], obj.get("filterMapping")) + is_default_server = from_union([from_bool, from_none], obj.get("isDefaultServer")) + timeout = from_union([from_int, from_none], obj.get("timeout")) + tools = from_union([lambda x: from_list(from_str, x), from_none], obj.get("tools")) + type = from_union([MCPServerConfigType, from_none], obj.get("type")) + headers = from_union([lambda x: from_dict(from_str, x), from_none], obj.get("headers")) + oauth_client_id = from_union([from_str, from_none], obj.get("oauthClientId")) + oauth_public_client = from_union([from_bool, from_none], obj.get("oauthPublicClient")) + url = from_union([from_str, from_none], obj.get("url")) + return MCPConfigUpdateRequestMCPServerConfig(args, command, cwd, env, filter_mapping, is_default_server, timeout, tools, type, headers, oauth_client_id, oauth_public_client, url) + + def to_dict(self) -> dict: + result: dict = {} + if self.args is not None: + result["args"] = from_union([lambda x: from_list(from_str, x), from_none], self.args) + if self.command is not None: + result["command"] = from_union([from_str, from_none], self.command) + if self.cwd is not None: + result["cwd"] = from_union([from_str, from_none], self.cwd) + if self.env is not None: + result["env"] = from_union([lambda x: from_dict(from_str, x), from_none], self.env) + if self.filter_mapping is not None: + result["filterMapping"] = from_union([lambda x: from_dict(lambda x: to_enum(FilterMappingString, x), x), lambda x: to_enum(FilterMappingString, x), from_none], self.filter_mapping) + if self.is_default_server is not None: + result["isDefaultServer"] = from_union([from_bool, from_none], self.is_default_server) + if self.timeout is not None: + result["timeout"] = from_union([from_int, from_none], self.timeout) + if self.tools is not None: + result["tools"] = from_union([lambda x: from_list(from_str, x), from_none], self.tools) + if self.type is not None: + result["type"] = from_union([lambda x: to_enum(MCPServerConfigType, x), from_none], self.type) + if self.headers is not None: + result["headers"] = from_union([lambda x: from_dict(from_str, x), from_none], self.headers) + if self.oauth_client_id is not None: + result["oauthClientId"] = from_union([from_str, from_none], self.oauth_client_id) + if self.oauth_public_client is not None: + result["oauthPublicClient"] = from_union([from_bool, from_none], self.oauth_public_client) + if self.url is not None: + result["url"] = from_union([from_str, from_none], self.url) + return result + +@dataclass +class DiscoveredMCPServer: + enabled: bool + """Whether the server is enabled (not in the disabled list)""" + + name: str + """Server name (config key)""" + + source: MCPServerSource + """Configuration source""" + + type: DiscoveredMCPServerType | None = None + """Server transport type: stdio, http, sse, or memory (local configs are normalized to stdio)""" + + @staticmethod + def from_dict(obj: Any) -> 'DiscoveredMCPServer': + assert isinstance(obj, dict) + enabled = from_bool(obj.get("enabled")) + name = from_str(obj.get("name")) + source = MCPServerSource(obj.get("source")) + type = from_union([DiscoveredMCPServerType, from_none], obj.get("type")) + return DiscoveredMCPServer(enabled, name, source, type) + + def to_dict(self) -> dict: + result: dict = {} + result["enabled"] = from_bool(self.enabled) + result["name"] = from_str(self.name) + result["source"] = to_enum(MCPServerSource, self.source) + if self.type is not None: + result["type"] = from_union([lambda x: to_enum(DiscoveredMCPServerType, x), from_none], self.type) + return result + +@dataclass +class ServerElement: + enabled: bool + """Whether the server is enabled (not in the disabled list)""" + + name: str + """Server name (config key)""" + + source: MCPServerSource + """Configuration source""" + + type: DiscoveredMCPServerType | None = None + """Server transport type: stdio, http, sse, or memory (local configs are normalized to stdio)""" + + @staticmethod + def from_dict(obj: Any) -> 'ServerElement': + assert isinstance(obj, dict) + enabled = from_bool(obj.get("enabled")) + name = from_str(obj.get("name")) + source = MCPServerSource(obj.get("source")) + type = from_union([DiscoveredMCPServerType, from_none], obj.get("type")) + return ServerElement(enabled, name, source, type) + + def to_dict(self) -> dict: + result: dict = {} + result["enabled"] = from_bool(self.enabled) + result["name"] = from_str(self.name) + result["source"] = to_enum(MCPServerSource, self.source) + if self.type is not None: + result["type"] = from_union([lambda x: to_enum(DiscoveredMCPServerType, x), from_none], self.type) + return result + +@dataclass +class ServerSkillList: + skills: list[SkillElement] + """All discovered skills across all sources""" + + @staticmethod + def from_dict(obj: Any) -> 'ServerSkillList': + assert isinstance(obj, dict) + skills = from_list(SkillElement.from_dict, obj.get("skills")) + return ServerSkillList(skills) + + def to_dict(self) -> dict: + result: dict = {} + result["skills"] = from_list(lambda x: to_class(SkillElement, x), self.skills) + return result + +@dataclass +class ModelCapabilitiesOverrideLimits: + """Token limits for prompts, outputs, and context window""" + + max_context_window_tokens: int | None = None + """Maximum total context window size in tokens""" + + max_output_tokens: int | None = None + max_prompt_tokens: int | None = None + vision: PurpleModelCapabilitiesOverrideLimitsVision | None = None + + @staticmethod + def from_dict(obj: Any) -> 'ModelCapabilitiesOverrideLimits': + assert isinstance(obj, dict) + max_context_window_tokens = from_union([from_int, from_none], obj.get("max_context_window_tokens")) + max_output_tokens = from_union([from_int, from_none], obj.get("max_output_tokens")) + max_prompt_tokens = from_union([from_int, from_none], obj.get("max_prompt_tokens")) + vision = from_union([PurpleModelCapabilitiesOverrideLimitsVision.from_dict, from_none], obj.get("vision")) + return ModelCapabilitiesOverrideLimits(max_context_window_tokens, max_output_tokens, max_prompt_tokens, vision) + + def to_dict(self) -> dict: + result: dict = {} + if self.max_context_window_tokens is not None: + result["max_context_window_tokens"] = from_union([from_int, from_none], self.max_context_window_tokens) + if self.max_output_tokens is not None: + result["max_output_tokens"] = from_union([from_int, from_none], self.max_output_tokens) + if self.max_prompt_tokens is not None: + result["max_prompt_tokens"] = from_union([from_int, from_none], self.max_prompt_tokens) + if self.vision is not None: + result["vision"] = from_union([lambda x: to_class(PurpleModelCapabilitiesOverrideLimitsVision, x), from_none], self.vision) + return result + +@dataclass +class MCPServer: + name: str + """Server name (config key)""" + + status: MCPServerStatus + """Connection status: connected, failed, needs-auth, pending, disabled, or not_configured""" + + error: str | None = None + """Error message if the server failed to connect""" + + source: MCPServerSource | None = None + """Configuration source: user, workspace, plugin, or builtin""" + + @staticmethod + def from_dict(obj: Any) -> 'MCPServer': + assert isinstance(obj, dict) + name = from_str(obj.get("name")) + status = MCPServerStatus(obj.get("status")) + error = from_union([from_str, from_none], obj.get("error")) + source = from_union([MCPServerSource, from_none], obj.get("source")) + return MCPServer(name, status, error, source) + + def to_dict(self) -> dict: + result: dict = {} + result["name"] = from_str(self.name) + result["status"] = to_enum(MCPServerStatus, self.status) + if self.error is not None: + result["error"] = from_union([from_str, from_none], self.error) + if self.source is not None: + result["source"] = from_union([lambda x: to_enum(MCPServerSource, x), from_none], self.source) + return result + +@dataclass +class UIElicitationStringEnumField: + enum: list[str] + type: UIElicitationStringEnumFieldType + default: str | None = None + description: str | None = None + enum_names: list[str] | None = None + title: str | None = None + + @staticmethod + def from_dict(obj: Any) -> 'UIElicitationStringEnumField': + assert isinstance(obj, dict) + enum = from_list(from_str, obj.get("enum")) + type = UIElicitationStringEnumFieldType(obj.get("type")) + default = from_union([from_str, from_none], obj.get("default")) + description = from_union([from_str, from_none], obj.get("description")) + enum_names = from_union([lambda x: from_list(from_str, x), from_none], obj.get("enumNames")) + title = from_union([from_str, from_none], obj.get("title")) + return UIElicitationStringEnumField(enum, type, default, description, enum_names, title) + + def to_dict(self) -> dict: + result: dict = {} + result["enum"] = from_list(from_str, self.enum) + result["type"] = to_enum(UIElicitationStringEnumFieldType, self.type) + if self.default is not None: + result["default"] = from_union([from_str, from_none], self.default) + if self.description is not None: + result["description"] = from_union([from_str, from_none], self.description) + if self.enum_names is not None: + result["enumNames"] = from_union([lambda x: from_list(from_str, x), from_none], self.enum_names) + if self.title is not None: + result["title"] = from_union([from_str, from_none], self.title) + return result + +@dataclass +class UIElicitationArrayEnumFieldItems: + enum: list[str] + type: UIElicitationStringEnumFieldType + + @staticmethod + def from_dict(obj: Any) -> 'UIElicitationArrayEnumFieldItems': + assert isinstance(obj, dict) + enum = from_list(from_str, obj.get("enum")) + type = UIElicitationStringEnumFieldType(obj.get("type")) + return UIElicitationArrayEnumFieldItems(enum, type) + + def to_dict(self) -> dict: + result: dict = {} + result["enum"] = from_list(from_str, self.enum) + result["type"] = to_enum(UIElicitationStringEnumFieldType, self.type) + return result + +@dataclass +class UIElicitationStringOneOfField: + one_of: list[UIElicitationStringOneOfFieldOneOf] + type: UIElicitationStringEnumFieldType + default: str | None = None + description: str | None = None + title: str | None = None + + @staticmethod + def from_dict(obj: Any) -> 'UIElicitationStringOneOfField': + assert isinstance(obj, dict) + one_of = from_list(UIElicitationStringOneOfFieldOneOf.from_dict, obj.get("oneOf")) + type = UIElicitationStringEnumFieldType(obj.get("type")) + default = from_union([from_str, from_none], obj.get("default")) + description = from_union([from_str, from_none], obj.get("description")) + title = from_union([from_str, from_none], obj.get("title")) + return UIElicitationStringOneOfField(one_of, type, default, description, title) + + def to_dict(self) -> dict: + result: dict = {} + result["oneOf"] = from_list(lambda x: to_class(UIElicitationStringOneOfFieldOneOf, x), self.one_of) + result["type"] = to_enum(UIElicitationStringEnumFieldType, self.type) + if self.default is not None: + result["default"] = from_union([from_str, from_none], self.default) + if self.description is not None: + result["description"] = from_union([from_str, from_none], self.description) + if self.title is not None: + result["title"] = from_union([from_str, from_none], self.title) + return result + +@dataclass +class UIElicitationArrayAnyOfFieldItems: + any_of: list[PurpleUIElicitationArrayAnyOfFieldItemsAnyOf] + + @staticmethod + def from_dict(obj: Any) -> 'UIElicitationArrayAnyOfFieldItems': + assert isinstance(obj, dict) + any_of = from_list(PurpleUIElicitationArrayAnyOfFieldItemsAnyOf.from_dict, obj.get("anyOf")) + return UIElicitationArrayAnyOfFieldItems(any_of) + + def to_dict(self) -> dict: + result: dict = {} + result["anyOf"] = from_list(lambda x: to_class(PurpleUIElicitationArrayAnyOfFieldItemsAnyOf, x), self.any_of) + return result + +@dataclass +class UIElicitationResponse: + """The elicitation response (accept with form values, decline, or cancel)""" + + action: UIElicitationResponseAction + """The user's response: accept (submitted), decline (rejected), or cancel (dismissed)""" + + content: dict[str, float | bool | list[str] | str] | None = None + """The form values submitted by the user (present when action is 'accept')""" + + @staticmethod + def from_dict(obj: Any) -> 'UIElicitationResponse': + assert isinstance(obj, dict) + action = UIElicitationResponseAction(obj.get("action")) + content = from_union([lambda x: from_dict(lambda x: from_union([from_float, from_bool, lambda x: from_list(from_str, x), from_str], x), x), from_none], obj.get("content")) + return UIElicitationResponse(action, content) + + def to_dict(self) -> dict: + result: dict = {} + result["action"] = to_enum(UIElicitationResponseAction, self.action) + if self.content is not None: + result["content"] = from_union([lambda x: from_dict(lambda x: from_union([to_float, from_bool, lambda x: from_list(from_str, x), from_str], x), x), from_none], self.content) + return result + +@dataclass +class PermissionDecision: + kind: Kind + """The permission request was approved + + Denied because approval rules explicitly blocked it + + Denied because no approval rule matched and user confirmation was unavailable + + Denied by the user during an interactive prompt + + Denied by the organization's content exclusion policy + + Denied by a permission request hook registered by an extension or plugin + """ + rules: list[Any] | None = None + """Rules that denied the request""" + + feedback: str | None = None + """Optional feedback from the user explaining the denial""" + + message: str | None = None + """Human-readable explanation of why the path was excluded + + Optional message from the hook explaining the denial + """ + path: str | None = None + """File path that triggered the exclusion""" + + interrupt: bool | None = None + """Whether to interrupt the current agent turn""" + + @staticmethod + def from_dict(obj: Any) -> 'PermissionDecision': + assert isinstance(obj, dict) + kind = Kind(obj.get("kind")) + rules = from_union([lambda x: from_list(lambda x: x, x), from_none], obj.get("rules")) + feedback = from_union([from_str, from_none], obj.get("feedback")) + message = from_union([from_str, from_none], obj.get("message")) + path = from_union([from_str, from_none], obj.get("path")) + interrupt = from_union([from_bool, from_none], obj.get("interrupt")) + return PermissionDecision(kind, rules, feedback, message, path, interrupt) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = to_enum(Kind, self.kind) + if self.rules is not None: + result["rules"] = from_union([lambda x: from_list(lambda x: x, x), from_none], self.rules) + if self.feedback is not None: + result["feedback"] = from_union([from_str, from_none], self.feedback) + if self.message is not None: + result["message"] = from_union([from_str, from_none], self.message) + if self.path is not None: + result["path"] = from_union([from_str, from_none], self.path) + if self.interrupt is not None: + result["interrupt"] = from_union([from_bool, from_none], self.interrupt) + return result + +@dataclass +class CapabilitiesLimits: + """Token limits for prompts, outputs, and context window""" + + max_context_window_tokens: int | None = None + """Maximum total context window size in tokens""" + + max_output_tokens: int | None = None + """Maximum number of output/completion tokens""" + + max_prompt_tokens: int | None = None + """Maximum number of prompt/input tokens""" + + vision: FluffyModelCapabilitiesLimitsVision | None = None + """Vision-specific limits""" + + @staticmethod + def from_dict(obj: Any) -> 'CapabilitiesLimits': + assert isinstance(obj, dict) + max_context_window_tokens = from_union([from_int, from_none], obj.get("max_context_window_tokens")) + max_output_tokens = from_union([from_int, from_none], obj.get("max_output_tokens")) + max_prompt_tokens = from_union([from_int, from_none], obj.get("max_prompt_tokens")) + vision = from_union([FluffyModelCapabilitiesLimitsVision.from_dict, from_none], obj.get("vision")) + return CapabilitiesLimits(max_context_window_tokens, max_output_tokens, max_prompt_tokens, vision) + + def to_dict(self) -> dict: + result: dict = {} + if self.max_context_window_tokens is not None: + result["max_context_window_tokens"] = from_union([from_int, from_none], self.max_context_window_tokens) + if self.max_output_tokens is not None: + result["max_output_tokens"] = from_union([from_int, from_none], self.max_output_tokens) + if self.max_prompt_tokens is not None: + result["max_prompt_tokens"] = from_union([from_int, from_none], self.max_prompt_tokens) + if self.vision is not None: + result["vision"] = from_union([lambda x: to_class(FluffyModelCapabilitiesLimitsVision, x), from_none], self.vision) + return result + +@dataclass +class ToolList: + tools: list[Tool] + """List of available built-in tools with metadata""" + + @staticmethod + def from_dict(obj: Any) -> 'ToolList': + assert isinstance(obj, dict) + tools = from_list(Tool.from_dict, obj.get("tools")) + return ToolList(tools) + + def to_dict(self) -> dict: + result: dict = {} + result["tools"] = from_list(lambda x: to_class(Tool, x), self.tools) + return result + +@dataclass +class ToolsHandlePendingToolCallRequest: + request_id: str + """Request ID of the pending tool call""" + + error: str | None = None + """Error message if the tool call failed""" + + result: ToolCallResult | str | None = None + """Tool call result (string or expanded result object)""" + + @staticmethod + def from_dict(obj: Any) -> 'ToolsHandlePendingToolCallRequest': + assert isinstance(obj, dict) + request_id = from_str(obj.get("requestId")) + error = from_union([from_str, from_none], obj.get("error")) + result = from_union([ToolCallResult.from_dict, from_str, from_none], obj.get("result")) + return ToolsHandlePendingToolCallRequest(request_id, error, result) + + def to_dict(self) -> dict: + result: dict = {} + result["requestId"] = from_str(self.request_id) + if self.error is not None: + result["error"] = from_union([from_str, from_none], self.error) + if self.result is not None: + result["result"] = from_union([lambda x: to_class(ToolCallResult, x), from_str, from_none], self.result) + return result + +@dataclass +class AccountGetQuotaResult: + quota_snapshots: dict[str, AccountQuotaSnapshot] + """Quota snapshots keyed by type (e.g., chat, completions, premium_interactions)""" + + @staticmethod + def from_dict(obj: Any) -> 'AccountGetQuotaResult': + assert isinstance(obj, dict) + quota_snapshots = from_dict(AccountQuotaSnapshot.from_dict, obj.get("quotaSnapshots")) + return AccountGetQuotaResult(quota_snapshots) def to_dict(self) -> dict: result: dict = {} - result["type"] = to_enum(UIElicitationSchemaPropertyNumberType, self.type) - if self.default is not None: - result["default"] = from_union([to_float, from_bool, lambda x: from_list(from_str, x), from_str, from_none], self.default) + result["quotaSnapshots"] = from_dict(lambda x: to_class(AccountQuotaSnapshot, x), self.quota_snapshots) + return result + +@dataclass +class SessionFSSetProviderRequest: + conventions: SessionFSSetProviderConventions + """Path conventions used by this filesystem""" + + initial_cwd: str + """Initial working directory for sessions""" + + session_state_path: str + """Path within each session's SessionFs where the runtime stores files for that session""" + + @staticmethod + def from_dict(obj: Any) -> 'SessionFSSetProviderRequest': + assert isinstance(obj, dict) + conventions = SessionFSSetProviderConventions(obj.get("conventions")) + initial_cwd = from_str(obj.get("initialCwd")) + session_state_path = from_str(obj.get("sessionStatePath")) + return SessionFSSetProviderRequest(conventions, initial_cwd, session_state_path) + + def to_dict(self) -> dict: + result: dict = {} + result["conventions"] = to_enum(SessionFSSetProviderConventions, self.conventions) + result["initialCwd"] = from_str(self.initial_cwd) + result["sessionStatePath"] = from_str(self.session_state_path) + return result + +@dataclass +class ModelCapabilitiesLimitsClass: + """Token limits for prompts, outputs, and context window""" + + max_context_window_tokens: int | None = None + """Maximum total context window size in tokens""" + + max_output_tokens: int | None = None + max_prompt_tokens: int | None = None + vision: FluffyModelCapabilitiesOverrideLimitsVision | None = None + + @staticmethod + def from_dict(obj: Any) -> 'ModelCapabilitiesLimitsClass': + assert isinstance(obj, dict) + max_context_window_tokens = from_union([from_int, from_none], obj.get("max_context_window_tokens")) + max_output_tokens = from_union([from_int, from_none], obj.get("max_output_tokens")) + max_prompt_tokens = from_union([from_int, from_none], obj.get("max_prompt_tokens")) + vision = from_union([FluffyModelCapabilitiesOverrideLimitsVision.from_dict, from_none], obj.get("vision")) + return ModelCapabilitiesLimitsClass(max_context_window_tokens, max_output_tokens, max_prompt_tokens, vision) + + def to_dict(self) -> dict: + result: dict = {} + if self.max_context_window_tokens is not None: + result["max_context_window_tokens"] = from_union([from_int, from_none], self.max_context_window_tokens) + if self.max_output_tokens is not None: + result["max_output_tokens"] = from_union([from_int, from_none], self.max_output_tokens) + if self.max_prompt_tokens is not None: + result["max_prompt_tokens"] = from_union([from_int, from_none], self.max_prompt_tokens) + if self.vision is not None: + result["vision"] = from_union([lambda x: to_class(FluffyModelCapabilitiesOverrideLimitsVision, x), from_none], self.vision) + return result + +@dataclass +class ModeSetRequest: + mode: SessionMode + """The agent mode. Valid values: "interactive", "plan", "autopilot".""" + + @staticmethod + def from_dict(obj: Any) -> 'ModeSetRequest': + assert isinstance(obj, dict) + mode = SessionMode(obj.get("mode")) + return ModeSetRequest(mode) + + def to_dict(self) -> dict: + result: dict = {} + result["mode"] = to_enum(SessionMode, self.mode) + return result + +@dataclass +class Workspace: + id: UUID + branch: str | None = None + chronicle_sync_dismissed: bool | None = None + created_at: datetime | None = None + cwd: str | None = None + git_root: str | None = None + host_type: HostType | None = None + mc_last_event_id: str | None = None + mc_session_id: str | None = None + mc_task_id: str | None = None + name: str | None = None + pr_create_sync_dismissed: bool | None = None + repository: str | None = None + session_sync_level: SessionSyncLevel | None = None + summary: str | None = None + summary_count: int | None = None + updated_at: datetime | None = None + + @staticmethod + def from_dict(obj: Any) -> 'Workspace': + assert isinstance(obj, dict) + id = UUID(obj.get("id")) + branch = from_union([from_str, from_none], obj.get("branch")) + chronicle_sync_dismissed = from_union([from_bool, from_none], obj.get("chronicle_sync_dismissed")) + created_at = from_union([from_datetime, from_none], obj.get("created_at")) + cwd = from_union([from_str, from_none], obj.get("cwd")) + git_root = from_union([from_str, from_none], obj.get("git_root")) + host_type = from_union([HostType, from_none], obj.get("host_type")) + mc_last_event_id = from_union([from_str, from_none], obj.get("mc_last_event_id")) + mc_session_id = from_union([from_str, from_none], obj.get("mc_session_id")) + mc_task_id = from_union([from_str, from_none], obj.get("mc_task_id")) + name = from_union([from_str, from_none], obj.get("name")) + pr_create_sync_dismissed = from_union([from_bool, from_none], obj.get("pr_create_sync_dismissed")) + repository = from_union([from_str, from_none], obj.get("repository")) + session_sync_level = from_union([SessionSyncLevel, from_none], obj.get("session_sync_level")) + summary = from_union([from_str, from_none], obj.get("summary")) + summary_count = from_union([from_int, from_none], obj.get("summary_count")) + updated_at = from_union([from_datetime, from_none], obj.get("updated_at")) + return Workspace(id, branch, chronicle_sync_dismissed, created_at, cwd, git_root, host_type, mc_last_event_id, mc_session_id, mc_task_id, name, pr_create_sync_dismissed, repository, session_sync_level, summary, summary_count, updated_at) + + def to_dict(self) -> dict: + result: dict = {} + result["id"] = str(self.id) + if self.branch is not None: + result["branch"] = from_union([from_str, from_none], self.branch) + if self.chronicle_sync_dismissed is not None: + result["chronicle_sync_dismissed"] = from_union([from_bool, from_none], self.chronicle_sync_dismissed) + if self.created_at is not None: + result["created_at"] = from_union([lambda x: x.isoformat(), from_none], self.created_at) + if self.cwd is not None: + result["cwd"] = from_union([from_str, from_none], self.cwd) + if self.git_root is not None: + result["git_root"] = from_union([from_str, from_none], self.git_root) + if self.host_type is not None: + result["host_type"] = from_union([lambda x: to_enum(HostType, x), from_none], self.host_type) + if self.mc_last_event_id is not None: + result["mc_last_event_id"] = from_union([from_str, from_none], self.mc_last_event_id) + if self.mc_session_id is not None: + result["mc_session_id"] = from_union([from_str, from_none], self.mc_session_id) + if self.mc_task_id is not None: + result["mc_task_id"] = from_union([from_str, from_none], self.mc_task_id) + if self.name is not None: + result["name"] = from_union([from_str, from_none], self.name) + if self.pr_create_sync_dismissed is not None: + result["pr_create_sync_dismissed"] = from_union([from_bool, from_none], self.pr_create_sync_dismissed) + if self.repository is not None: + result["repository"] = from_union([from_str, from_none], self.repository) + if self.session_sync_level is not None: + result["session_sync_level"] = from_union([lambda x: to_enum(SessionSyncLevel, x), from_none], self.session_sync_level) + if self.summary is not None: + result["summary"] = from_union([from_str, from_none], self.summary) + if self.summary_count is not None: + result["summary_count"] = from_union([from_int, from_none], self.summary_count) + if self.updated_at is not None: + result["updated_at"] = from_union([lambda x: x.isoformat(), from_none], self.updated_at) + return result + +@dataclass +class InstructionsSources: + content: str + """Raw content of the instruction file""" + + id: str + """Unique identifier for this source (used for toggling)""" + + label: str + """Human-readable label""" + + location: InstructionsSourcesLocation + """Where this source lives — used for UI grouping""" + + source_path: str + """File path relative to repo or absolute for home""" + + type: InstructionsSourcesType + """Category of instruction source — used for merge logic""" + + apply_to: str | None = None + """Glob pattern from frontmatter — when set, this instruction applies only to matching files""" + + description: str | None = None + """Short description (body after frontmatter) for use in instruction tables""" + + @staticmethod + def from_dict(obj: Any) -> 'InstructionsSources': + assert isinstance(obj, dict) + content = from_str(obj.get("content")) + id = from_str(obj.get("id")) + label = from_str(obj.get("label")) + location = InstructionsSourcesLocation(obj.get("location")) + source_path = from_str(obj.get("sourcePath")) + type = InstructionsSourcesType(obj.get("type")) + apply_to = from_union([from_str, from_none], obj.get("applyTo")) + description = from_union([from_str, from_none], obj.get("description")) + return InstructionsSources(content, id, label, location, source_path, type, apply_to, description) + + def to_dict(self) -> dict: + result: dict = {} + result["content"] = from_str(self.content) + result["id"] = from_str(self.id) + result["label"] = from_str(self.label) + result["location"] = to_enum(InstructionsSourcesLocation, self.location) + result["sourcePath"] = from_str(self.source_path) + result["type"] = to_enum(InstructionsSourcesType, self.type) + if self.apply_to is not None: + result["applyTo"] = from_union([from_str, from_none], self.apply_to) if self.description is not None: result["description"] = from_union([from_str, from_none], self.description) - if self.enum is not None: - result["enum"] = from_union([lambda x: from_list(from_str, x), from_none], self.enum) - if self.enum_names is not None: - result["enumNames"] = from_union([lambda x: from_list(from_str, x), from_none], self.enum_names) - if self.title is not None: - result["title"] = from_union([from_str, from_none], self.title) - if self.one_of is not None: - result["oneOf"] = from_union([lambda x: from_list(lambda x: to_class(UIElicitationStringOneOfFieldOneOf, x), x), from_none], self.one_of) - if self.items is not None: - result["items"] = from_union([lambda x: to_class(UIElicitationArrayFieldItems, x), from_none], self.items) - if self.max_items is not None: - result["maxItems"] = from_union([to_float, from_none], self.max_items) - if self.min_items is not None: - result["minItems"] = from_union([to_float, from_none], self.min_items) - if self.format is not None: - result["format"] = from_union([lambda x: to_enum(UIElicitationSchemaPropertyStringFormat, x), from_none], self.format) - if self.max_length is not None: - result["maxLength"] = from_union([to_float, from_none], self.max_length) - if self.min_length is not None: - result["minLength"] = from_union([to_float, from_none], self.min_length) - if self.maximum is not None: - result["maximum"] = from_union([to_float, from_none], self.maximum) - if self.minimum is not None: - result["minimum"] = from_union([to_float, from_none], self.minimum) return result -class RequestedSchemaType(Enum): - OBJECT = "object" +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class AgentList: + agents: list[AgentListAgent] + """Available custom agents""" + + @staticmethod + def from_dict(obj: Any) -> 'AgentList': + assert isinstance(obj, dict) + agents = from_list(AgentListAgent.from_dict, obj.get("agents")) + return AgentList(agents) + + def to_dict(self) -> dict: + result: dict = {} + result["agents"] = from_list(lambda x: to_class(AgentListAgent, x), self.agents) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class AgentSelectResult: + agent: AgentSelectResultAgent + """The newly selected custom agent""" + + @staticmethod + def from_dict(obj: Any) -> 'AgentSelectResult': + assert isinstance(obj, dict) + agent = AgentSelectResultAgent.from_dict(obj.get("agent")) + return AgentSelectResult(agent) + + def to_dict(self) -> dict: + result: dict = {} + result["agent"] = to_class(AgentSelectResultAgent, self.agent) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class AgentGetCurrentResult: + agent: AgentReloadResultAgent | None = None + """Currently selected custom agent, or null if using the default agent""" + + @staticmethod + def from_dict(obj: Any) -> 'AgentGetCurrentResult': + assert isinstance(obj, dict) + agent = from_union([AgentReloadResultAgent.from_dict, from_none], obj.get("agent")) + return AgentGetCurrentResult(agent) + + def to_dict(self) -> dict: + result: dict = {} + if self.agent is not None: + result["agent"] = from_union([lambda x: to_class(AgentReloadResultAgent, x), from_none], self.agent) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class AgentReloadResult: + agents: list[AgentReloadResultAgent] + """Reloaded custom agents""" + + @staticmethod + def from_dict(obj: Any) -> 'AgentReloadResult': + assert isinstance(obj, dict) + agents = from_list(AgentReloadResultAgent.from_dict, obj.get("agents")) + return AgentReloadResult(agents) + + def to_dict(self) -> dict: + result: dict = {} + result["agents"] = from_list(lambda x: to_class(AgentReloadResultAgent, x), self.agents) + return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass -class UIElicitationSchema: - """JSON Schema describing the form fields to present to the user""" +class SkillList: + skills: list[Skill] + """Available skills""" - properties: dict[str, UIElicitationSchemaProperty] - """Form field definitions, keyed by field name""" + @staticmethod + def from_dict(obj: Any) -> 'SkillList': + assert isinstance(obj, dict) + skills = from_list(Skill.from_dict, obj.get("skills")) + return SkillList(skills) - type: RequestedSchemaType - """Schema type indicator (always 'object')""" + def to_dict(self) -> dict: + result: dict = {} + result["skills"] = from_list(lambda x: to_class(Skill, x), self.skills) + return result - required: list[str] | None = None - """List of required field names""" +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class PluginList: + plugins: list[Plugin] + """Installed plugins""" @staticmethod - def from_dict(obj: Any) -> 'UIElicitationSchema': + def from_dict(obj: Any) -> 'PluginList': assert isinstance(obj, dict) - properties = from_dict(UIElicitationSchemaProperty.from_dict, obj.get("properties")) - type = RequestedSchemaType(obj.get("type")) - required = from_union([lambda x: from_list(from_str, x), from_none], obj.get("required")) - return UIElicitationSchema(properties, type, required) + plugins = from_list(Plugin.from_dict, obj.get("plugins")) + return PluginList(plugins) def to_dict(self) -> dict: result: dict = {} - result["properties"] = from_dict(lambda x: to_class(UIElicitationSchemaProperty, x), self.properties) - result["type"] = to_enum(RequestedSchemaType, self.type) - if self.required is not None: - result["required"] = from_union([lambda x: from_list(from_str, x), from_none], self.required) + result["plugins"] = from_list(lambda x: to_class(Plugin, x), self.plugins) return result @dataclass -class UIElicitationRequest: - message: str - """Message describing what information is needed from the user""" +class Extension: + id: str + """Source-qualified ID (e.g., 'project:my-ext', 'user:auth-helper')""" - requested_schema: UIElicitationSchema - """JSON Schema describing the form fields to present to the user""" + name: str + """Extension name (directory name)""" + + source: ExtensionSource + """Discovery source: project (.github/extensions/) or user (~/.copilot/extensions/)""" + + status: ExtensionStatus + """Current status: running, disabled, failed, or starting""" + + pid: int | None = None + """Process ID if the extension is running""" @staticmethod - def from_dict(obj: Any) -> 'UIElicitationRequest': + def from_dict(obj: Any) -> 'Extension': assert isinstance(obj, dict) - message = from_str(obj.get("message")) - requested_schema = UIElicitationSchema.from_dict(obj.get("requestedSchema")) - return UIElicitationRequest(message, requested_schema) + id = from_str(obj.get("id")) + name = from_str(obj.get("name")) + source = ExtensionSource(obj.get("source")) + status = ExtensionStatus(obj.get("status")) + pid = from_union([from_int, from_none], obj.get("pid")) + return Extension(id, name, source, status, pid) def to_dict(self) -> dict: result: dict = {} - result["message"] = from_str(self.message) - result["requestedSchema"] = to_class(UIElicitationSchema, self.requested_schema) + result["id"] = from_str(self.id) + result["name"] = from_str(self.name) + result["source"] = to_enum(ExtensionSource, self.source) + result["status"] = to_enum(ExtensionStatus, self.status) + if self.pid is not None: + result["pid"] = from_union([from_int, from_none], self.pid) return result @dataclass -class UIElicitationResult: - success: bool - """Whether the response was accepted. False if the request was already resolved by another - client. - """ +class UIElicitationArrayFieldItems: + enum: list[str] | None = None + type: UIElicitationStringEnumFieldType | None = None + any_of: list[FluffyUIElicitationArrayAnyOfFieldItemsAnyOf] | None = None @staticmethod - def from_dict(obj: Any) -> 'UIElicitationResult': + def from_dict(obj: Any) -> 'UIElicitationArrayFieldItems': assert isinstance(obj, dict) - success = from_bool(obj.get("success")) - return UIElicitationResult(success) + enum = from_union([lambda x: from_list(from_str, x), from_none], obj.get("enum")) + type = from_union([UIElicitationStringEnumFieldType, from_none], obj.get("type")) + any_of = from_union([lambda x: from_list(FluffyUIElicitationArrayAnyOfFieldItemsAnyOf.from_dict, x), from_none], obj.get("anyOf")) + return UIElicitationArrayFieldItems(enum, type, any_of) def to_dict(self) -> dict: result: dict = {} - result["success"] = from_bool(self.success) + if self.enum is not None: + result["enum"] = from_union([lambda x: from_list(from_str, x), from_none], self.enum) + if self.type is not None: + result["type"] = from_union([lambda x: to_enum(UIElicitationStringEnumFieldType, x), from_none], self.type) + if self.any_of is not None: + result["anyOf"] = from_union([lambda x: from_list(lambda x: to_class(FluffyUIElicitationArrayAnyOfFieldItemsAnyOf, x), x), from_none], self.any_of) return result @dataclass -class UIHandlePendingElicitationRequest: - request_id: str - """The unique request ID from the elicitation.requested event""" +class LogRequest: + message: str + """Human-readable message""" - result: UIElicitationResponse - """The elicitation response (accept with form values, decline, or cancel)""" + ephemeral: bool | None = None + """When true, the message is transient and not persisted to the session event log on disk""" + + level: SessionLogLevel | None = None + """Log severity level. Determines how the message is displayed in the timeline. Defaults to + "info". + """ + url: str | None = None + """Optional URL the user can open in their browser for more details""" @staticmethod - def from_dict(obj: Any) -> 'UIHandlePendingElicitationRequest': + def from_dict(obj: Any) -> 'LogRequest': assert isinstance(obj, dict) - request_id = from_str(obj.get("requestId")) - result = UIElicitationResponse.from_dict(obj.get("result")) - return UIHandlePendingElicitationRequest(request_id, result) + message = from_str(obj.get("message")) + ephemeral = from_union([from_bool, from_none], obj.get("ephemeral")) + level = from_union([SessionLogLevel, from_none], obj.get("level")) + url = from_union([from_str, from_none], obj.get("url")) + return LogRequest(message, ephemeral, level, url) def to_dict(self) -> dict: result: dict = {} - result["requestId"] = from_str(self.request_id) - result["result"] = to_class(UIElicitationResponse, self.result) + result["message"] = from_str(self.message) + if self.ephemeral is not None: + result["ephemeral"] = from_union([from_bool, from_none], self.ephemeral) + if self.level is not None: + result["level"] = from_union([lambda x: to_enum(SessionLogLevel, x), from_none], self.level) + if self.url is not None: + result["url"] = from_union([from_str, from_none], self.url) return result @dataclass -class PermissionRequestResult: - success: bool - """Whether the permission request was handled successfully""" +class ShellKillRequest: + process_id: str + """Process identifier returned by shell.exec""" + + signal: ShellKillSignal | None = None + """Signal to send (default: SIGTERM)""" @staticmethod - def from_dict(obj: Any) -> 'PermissionRequestResult': + def from_dict(obj: Any) -> 'ShellKillRequest': assert isinstance(obj, dict) - success = from_bool(obj.get("success")) - return PermissionRequestResult(success) + process_id = from_str(obj.get("processId")) + signal = from_union([ShellKillSignal, from_none], obj.get("signal")) + return ShellKillRequest(process_id, signal) def to_dict(self) -> dict: result: dict = {} - result["success"] = from_bool(self.success) + result["processId"] = from_str(self.process_id) + if self.signal is not None: + result["signal"] = from_union([lambda x: to_enum(ShellKillSignal, x), from_none], self.signal) return result -class Kind(Enum): - APPROVED = "approved" - DENIED_BY_CONTENT_EXCLUSION_POLICY = "denied-by-content-exclusion-policy" - DENIED_BY_PERMISSION_REQUEST_HOOK = "denied-by-permission-request-hook" - DENIED_BY_RULES = "denied-by-rules" - DENIED_INTERACTIVELY_BY_USER = "denied-interactively-by-user" - DENIED_NO_APPROVAL_RULE_AND_COULD_NOT_REQUEST_FROM_USER = "denied-no-approval-rule-and-could-not-request-from-user" - +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass -class PermissionDecision: - kind: Kind - """The permission request was approved - - Denied because approval rules explicitly blocked it - - Denied because no approval rule matched and user confirmation was unavailable +class HistoryCompactResult: + messages_removed: int + """Number of messages removed during compaction""" - Denied by the user during an interactive prompt + success: bool + """Whether compaction completed successfully""" - Denied by the organization's content exclusion policy + tokens_removed: int + """Number of tokens freed by compaction""" - Denied by a permission request hook registered by an extension or plugin - """ - rules: list[Any] | None = None - """Rules that denied the request""" + context_window: HistoryCompactContextWindow | None = None + """Post-compaction context window usage breakdown""" - feedback: str | None = None - """Optional feedback from the user explaining the denial""" + @staticmethod + def from_dict(obj: Any) -> 'HistoryCompactResult': + assert isinstance(obj, dict) + messages_removed = from_int(obj.get("messagesRemoved")) + success = from_bool(obj.get("success")) + tokens_removed = from_int(obj.get("tokensRemoved")) + context_window = from_union([HistoryCompactContextWindow.from_dict, from_none], obj.get("contextWindow")) + return HistoryCompactResult(messages_removed, success, tokens_removed, context_window) - message: str | None = None - """Human-readable explanation of why the path was excluded + def to_dict(self) -> dict: + result: dict = {} + result["messagesRemoved"] = from_int(self.messages_removed) + result["success"] = from_bool(self.success) + result["tokensRemoved"] = from_int(self.tokens_removed) + if self.context_window is not None: + result["contextWindow"] = from_union([lambda x: to_class(HistoryCompactContextWindow, x), from_none], self.context_window) + return result - Optional message from the hook explaining the denial - """ - path: str | None = None - """File path that triggered the exclusion""" +@dataclass +class UsageMetricsModelMetric: + requests: UsageMetricsModelMetricRequests + """Request count and cost metrics for this model""" - interrupt: bool | None = None - """Whether to interrupt the current agent turn""" + usage: UsageMetricsModelMetricUsage + """Token usage metrics for this model""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecision': + def from_dict(obj: Any) -> 'UsageMetricsModelMetric': assert isinstance(obj, dict) - kind = Kind(obj.get("kind")) - rules = from_union([lambda x: from_list(lambda x: x, x), from_none], obj.get("rules")) - feedback = from_union([from_str, from_none], obj.get("feedback")) - message = from_union([from_str, from_none], obj.get("message")) - path = from_union([from_str, from_none], obj.get("path")) - interrupt = from_union([from_bool, from_none], obj.get("interrupt")) - return PermissionDecision(kind, rules, feedback, message, path, interrupt) + requests = UsageMetricsModelMetricRequests.from_dict(obj.get("requests")) + usage = UsageMetricsModelMetricUsage.from_dict(obj.get("usage")) + return UsageMetricsModelMetric(requests, usage) def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(Kind, self.kind) - if self.rules is not None: - result["rules"] = from_union([lambda x: from_list(lambda x: x, x), from_none], self.rules) - if self.feedback is not None: - result["feedback"] = from_union([from_str, from_none], self.feedback) - if self.message is not None: - result["message"] = from_union([from_str, from_none], self.message) - if self.path is not None: - result["path"] = from_union([from_str, from_none], self.path) - if self.interrupt is not None: - result["interrupt"] = from_union([from_bool, from_none], self.interrupt) + result["requests"] = to_class(UsageMetricsModelMetricRequests, self.requests) + result["usage"] = to_class(UsageMetricsModelMetricUsage, self.usage) return result @dataclass -class PermissionDecisionRequest: - request_id: str - """Request ID of the pending permission request""" +class SessionFSReaddirWithTypesEntry: + name: str + """Entry name""" - result: PermissionDecision + type: SessionFSReaddirWithTypesEntryType + """Entry type""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionRequest': + def from_dict(obj: Any) -> 'SessionFSReaddirWithTypesEntry': assert isinstance(obj, dict) - request_id = from_str(obj.get("requestId")) - result = PermissionDecision.from_dict(obj.get("result")) - return PermissionDecisionRequest(request_id, result) + name = from_str(obj.get("name")) + type = SessionFSReaddirWithTypesEntryType(obj.get("type")) + return SessionFSReaddirWithTypesEntry(name, type) def to_dict(self) -> dict: result: dict = {} - result["requestId"] = from_str(self.request_id) - result["result"] = to_class(PermissionDecision, self.result) + result["name"] = from_str(self.name) + result["type"] = to_enum(SessionFSReaddirWithTypesEntryType, self.type) return result @dataclass -class LogResult: - event_id: UUID - """The unique identifier of the emitted session event""" +class MCPConfigList: + servers: dict[str, MCPServerConfigValue] + """All MCP servers from user config, keyed by name""" @staticmethod - def from_dict(obj: Any) -> 'LogResult': + def from_dict(obj: Any) -> 'MCPConfigList': assert isinstance(obj, dict) - event_id = UUID(obj.get("eventId")) - return LogResult(event_id) + servers = from_dict(MCPServerConfigValue.from_dict, obj.get("servers")) + return MCPConfigList(servers) def to_dict(self) -> dict: result: dict = {} - result["eventId"] = str(self.event_id) + result["servers"] = from_dict(lambda x: to_class(MCPServerConfigValue, x), self.servers) return result -class SessionLogLevel(Enum): - """Log severity level. Determines how the message is displayed in the timeline. Defaults to - "info". - """ - ERROR = "error" - INFO = "info" - WARNING = "warning" - @dataclass -class LogRequest: - message: str - """Human-readable message""" - - ephemeral: bool | None = None - """When true, the message is transient and not persisted to the session event log on disk""" +class MCPConfigAddRequest: + config: MCPConfigAddRequestMCPServerConfig + """MCP server configuration (local/stdio or remote/http)""" - level: SessionLogLevel | None = None - """Log severity level. Determines how the message is displayed in the timeline. Defaults to - "info". - """ - url: str | None = None - """Optional URL the user can open in their browser for more details""" + name: str + """Unique name for the MCP server""" @staticmethod - def from_dict(obj: Any) -> 'LogRequest': + def from_dict(obj: Any) -> 'MCPConfigAddRequest': assert isinstance(obj, dict) - message = from_str(obj.get("message")) - ephemeral = from_union([from_bool, from_none], obj.get("ephemeral")) - level = from_union([SessionLogLevel, from_none], obj.get("level")) - url = from_union([from_str, from_none], obj.get("url")) - return LogRequest(message, ephemeral, level, url) + config = MCPConfigAddRequestMCPServerConfig.from_dict(obj.get("config")) + name = from_str(obj.get("name")) + return MCPConfigAddRequest(config, name) def to_dict(self) -> dict: result: dict = {} - result["message"] = from_str(self.message) - if self.ephemeral is not None: - result["ephemeral"] = from_union([from_bool, from_none], self.ephemeral) - if self.level is not None: - result["level"] = from_union([lambda x: to_enum(SessionLogLevel, x), from_none], self.level) - if self.url is not None: - result["url"] = from_union([from_str, from_none], self.url) + result["config"] = to_class(MCPConfigAddRequestMCPServerConfig, self.config) + result["name"] = from_str(self.name) return result @dataclass -class ShellExecResult: - process_id: str - """Unique identifier for tracking streamed output""" +class MCPConfigUpdateRequest: + config: MCPConfigUpdateRequestMCPServerConfig + """MCP server configuration (local/stdio or remote/http)""" + + name: str + """Name of the MCP server to update""" @staticmethod - def from_dict(obj: Any) -> 'ShellExecResult': + def from_dict(obj: Any) -> 'MCPConfigUpdateRequest': assert isinstance(obj, dict) - process_id = from_str(obj.get("processId")) - return ShellExecResult(process_id) + config = MCPConfigUpdateRequestMCPServerConfig.from_dict(obj.get("config")) + name = from_str(obj.get("name")) + return MCPConfigUpdateRequest(config, name) def to_dict(self) -> dict: result: dict = {} - result["processId"] = from_str(self.process_id) + result["config"] = to_class(MCPConfigUpdateRequestMCPServerConfig, self.config) + result["name"] = from_str(self.name) return result @dataclass -class ShellExecRequest: - command: str - """Shell command to execute""" - - cwd: str | None = None - """Working directory (defaults to session working directory)""" - - timeout: int | None = None - """Timeout in milliseconds (default: 30000)""" +class MCPDiscoverResult: + servers: list[ServerElement] + """MCP servers discovered from all sources""" @staticmethod - def from_dict(obj: Any) -> 'ShellExecRequest': + def from_dict(obj: Any) -> 'MCPDiscoverResult': assert isinstance(obj, dict) - command = from_str(obj.get("command")) - cwd = from_union([from_str, from_none], obj.get("cwd")) - timeout = from_union([from_int, from_none], obj.get("timeout")) - return ShellExecRequest(command, cwd, timeout) + servers = from_list(ServerElement.from_dict, obj.get("servers")) + return MCPDiscoverResult(servers) def to_dict(self) -> dict: result: dict = {} - result["command"] = from_str(self.command) - if self.cwd is not None: - result["cwd"] = from_union([from_str, from_none], self.cwd) - if self.timeout is not None: - result["timeout"] = from_union([from_int, from_none], self.timeout) + result["servers"] = from_list(lambda x: to_class(ServerElement, x), self.servers) return result @dataclass -class ShellKillResult: - killed: bool - """Whether the signal was sent successfully""" +class ModelCapabilitiesOverride: + """Override individual model capabilities resolved by the runtime""" + + limits: ModelCapabilitiesOverrideLimits | None = None + """Token limits for prompts, outputs, and context window""" + + supports: ModelCapabilitiesOverrideSupports | None = None + """Feature flags indicating what the model supports""" @staticmethod - def from_dict(obj: Any) -> 'ShellKillResult': + def from_dict(obj: Any) -> 'ModelCapabilitiesOverride': assert isinstance(obj, dict) - killed = from_bool(obj.get("killed")) - return ShellKillResult(killed) + limits = from_union([ModelCapabilitiesOverrideLimits.from_dict, from_none], obj.get("limits")) + supports = from_union([ModelCapabilitiesOverrideSupports.from_dict, from_none], obj.get("supports")) + return ModelCapabilitiesOverride(limits, supports) def to_dict(self) -> dict: result: dict = {} - result["killed"] = from_bool(self.killed) + if self.limits is not None: + result["limits"] = from_union([lambda x: to_class(ModelCapabilitiesOverrideLimits, x), from_none], self.limits) + if self.supports is not None: + result["supports"] = from_union([lambda x: to_class(ModelCapabilitiesOverrideSupports, x), from_none], self.supports) return result -class ShellKillSignal(Enum): - """Signal to send (default: SIGTERM)""" - - SIGINT = "SIGINT" - SIGKILL = "SIGKILL" - SIGTERM = "SIGTERM" - @dataclass -class ShellKillRequest: - process_id: str - """Process identifier returned by shell.exec""" - - signal: ShellKillSignal | None = None - """Signal to send (default: SIGTERM)""" +class MCPServerList: + servers: list[MCPServer] + """Configured MCP servers""" @staticmethod - def from_dict(obj: Any) -> 'ShellKillRequest': + def from_dict(obj: Any) -> 'MCPServerList': assert isinstance(obj, dict) - process_id = from_str(obj.get("processId")) - signal = from_union([ShellKillSignal, from_none], obj.get("signal")) - return ShellKillRequest(process_id, signal) + servers = from_list(MCPServer.from_dict, obj.get("servers")) + return MCPServerList(servers) def to_dict(self) -> dict: result: dict = {} - result["processId"] = from_str(self.process_id) - if self.signal is not None: - result["signal"] = from_union([lambda x: to_enum(ShellKillSignal, x), from_none], self.signal) + result["servers"] = from_list(lambda x: to_class(MCPServer, x), self.servers) return result @dataclass -class HistoryCompactContextWindow: - """Post-compaction context window usage breakdown""" - - current_tokens: int - """Current total tokens in the context window (system + conversation + tool definitions)""" - - messages_length: int - """Current number of messages in the conversation""" - - token_limit: int - """Maximum token count for the model's context window""" - - conversation_tokens: int | None = None - """Token count from non-system messages (user, assistant, tool)""" - - system_tokens: int | None = None - """Token count from system message(s)""" - - tool_definitions_tokens: int | None = None - """Token count from tool definitions""" +class UIElicitationArrayEnumField: + items: UIElicitationArrayEnumFieldItems + type: UIElicitationArrayEnumFieldType + default: list[str] | None = None + description: str | None = None + max_items: float | None = None + min_items: float | None = None + title: str | None = None @staticmethod - def from_dict(obj: Any) -> 'HistoryCompactContextWindow': + def from_dict(obj: Any) -> 'UIElicitationArrayEnumField': assert isinstance(obj, dict) - current_tokens = from_int(obj.get("currentTokens")) - messages_length = from_int(obj.get("messagesLength")) - token_limit = from_int(obj.get("tokenLimit")) - conversation_tokens = from_union([from_int, from_none], obj.get("conversationTokens")) - system_tokens = from_union([from_int, from_none], obj.get("systemTokens")) - tool_definitions_tokens = from_union([from_int, from_none], obj.get("toolDefinitionsTokens")) - return HistoryCompactContextWindow(current_tokens, messages_length, token_limit, conversation_tokens, system_tokens, tool_definitions_tokens) + items = UIElicitationArrayEnumFieldItems.from_dict(obj.get("items")) + type = UIElicitationArrayEnumFieldType(obj.get("type")) + default = from_union([lambda x: from_list(from_str, x), from_none], obj.get("default")) + description = from_union([from_str, from_none], obj.get("description")) + max_items = from_union([from_float, from_none], obj.get("maxItems")) + min_items = from_union([from_float, from_none], obj.get("minItems")) + title = from_union([from_str, from_none], obj.get("title")) + return UIElicitationArrayEnumField(items, type, default, description, max_items, min_items, title) def to_dict(self) -> dict: result: dict = {} - result["currentTokens"] = from_int(self.current_tokens) - result["messagesLength"] = from_int(self.messages_length) - result["tokenLimit"] = from_int(self.token_limit) - if self.conversation_tokens is not None: - result["conversationTokens"] = from_union([from_int, from_none], self.conversation_tokens) - if self.system_tokens is not None: - result["systemTokens"] = from_union([from_int, from_none], self.system_tokens) - if self.tool_definitions_tokens is not None: - result["toolDefinitionsTokens"] = from_union([from_int, from_none], self.tool_definitions_tokens) + result["items"] = to_class(UIElicitationArrayEnumFieldItems, self.items) + result["type"] = to_enum(UIElicitationArrayEnumFieldType, self.type) + if self.default is not None: + result["default"] = from_union([lambda x: from_list(from_str, x), from_none], self.default) + if self.description is not None: + result["description"] = from_union([from_str, from_none], self.description) + if self.max_items is not None: + result["maxItems"] = from_union([to_float, from_none], self.max_items) + if self.min_items is not None: + result["minItems"] = from_union([to_float, from_none], self.min_items) + if self.title is not None: + result["title"] = from_union([from_str, from_none], self.title) return result -# Experimental: this type is part of an experimental API and may change or be removed. @dataclass -class HistoryCompactResult: - messages_removed: int - """Number of messages removed during compaction""" - - success: bool - """Whether compaction completed successfully""" - - tokens_removed: int - """Number of tokens freed by compaction""" - - context_window: HistoryCompactContextWindow | None = None - """Post-compaction context window usage breakdown""" +class UIElicitationArrayAnyOfField: + items: UIElicitationArrayAnyOfFieldItems + type: UIElicitationArrayEnumFieldType + default: list[str] | None = None + description: str | None = None + max_items: float | None = None + min_items: float | None = None + title: str | None = None @staticmethod - def from_dict(obj: Any) -> 'HistoryCompactResult': + def from_dict(obj: Any) -> 'UIElicitationArrayAnyOfField': assert isinstance(obj, dict) - messages_removed = from_int(obj.get("messagesRemoved")) - success = from_bool(obj.get("success")) - tokens_removed = from_int(obj.get("tokensRemoved")) - context_window = from_union([HistoryCompactContextWindow.from_dict, from_none], obj.get("contextWindow")) - return HistoryCompactResult(messages_removed, success, tokens_removed, context_window) + items = UIElicitationArrayAnyOfFieldItems.from_dict(obj.get("items")) + type = UIElicitationArrayEnumFieldType(obj.get("type")) + default = from_union([lambda x: from_list(from_str, x), from_none], obj.get("default")) + description = from_union([from_str, from_none], obj.get("description")) + max_items = from_union([from_float, from_none], obj.get("maxItems")) + min_items = from_union([from_float, from_none], obj.get("minItems")) + title = from_union([from_str, from_none], obj.get("title")) + return UIElicitationArrayAnyOfField(items, type, default, description, max_items, min_items, title) def to_dict(self) -> dict: result: dict = {} - result["messagesRemoved"] = from_int(self.messages_removed) - result["success"] = from_bool(self.success) - result["tokensRemoved"] = from_int(self.tokens_removed) - if self.context_window is not None: - result["contextWindow"] = from_union([lambda x: to_class(HistoryCompactContextWindow, x), from_none], self.context_window) + result["items"] = to_class(UIElicitationArrayAnyOfFieldItems, self.items) + result["type"] = to_enum(UIElicitationArrayEnumFieldType, self.type) + if self.default is not None: + result["default"] = from_union([lambda x: from_list(from_str, x), from_none], self.default) + if self.description is not None: + result["description"] = from_union([from_str, from_none], self.description) + if self.max_items is not None: + result["maxItems"] = from_union([to_float, from_none], self.max_items) + if self.min_items is not None: + result["minItems"] = from_union([to_float, from_none], self.min_items) + if self.title is not None: + result["title"] = from_union([from_str, from_none], self.title) return result -# Experimental: this type is part of an experimental API and may change or be removed. @dataclass -class HistoryTruncateResult: - events_removed: int - """Number of events that were removed""" +class UIHandlePendingElicitationRequest: + request_id: str + """The unique request ID from the elicitation.requested event""" + + result: UIElicitationResponse + """The elicitation response (accept with form values, decline, or cancel)""" @staticmethod - def from_dict(obj: Any) -> 'HistoryTruncateResult': + def from_dict(obj: Any) -> 'UIHandlePendingElicitationRequest': assert isinstance(obj, dict) - events_removed = from_int(obj.get("eventsRemoved")) - return HistoryTruncateResult(events_removed) + request_id = from_str(obj.get("requestId")) + result = UIElicitationResponse.from_dict(obj.get("result")) + return UIHandlePendingElicitationRequest(request_id, result) def to_dict(self) -> dict: result: dict = {} - result["eventsRemoved"] = from_int(self.events_removed) + result["requestId"] = from_str(self.request_id) + result["result"] = to_class(UIElicitationResponse, self.result) return result -# Experimental: this type is part of an experimental API and may change or be removed. @dataclass -class HistoryTruncateRequest: - event_id: str - """Event ID to truncate to. This event and all events after it are removed from the session.""" +class PermissionDecisionRequest: + request_id: str + """Request ID of the pending permission request""" + + result: PermissionDecision @staticmethod - def from_dict(obj: Any) -> 'HistoryTruncateRequest': + def from_dict(obj: Any) -> 'PermissionDecisionRequest': assert isinstance(obj, dict) - event_id = from_str(obj.get("eventId")) - return HistoryTruncateRequest(event_id) + request_id = from_str(obj.get("requestId")) + result = PermissionDecision.from_dict(obj.get("result")) + return PermissionDecisionRequest(request_id, result) def to_dict(self) -> dict: result: dict = {} - result["eventId"] = from_str(self.event_id) + result["requestId"] = from_str(self.request_id) + result["result"] = to_class(PermissionDecision, self.result) return result @dataclass -class UsageMetricsCodeChanges: - """Aggregated code change metrics""" - - files_modified_count: int - """Number of distinct files modified""" +class ModelCapabilitiesClass: + """Override individual model capabilities resolved by the runtime""" - lines_added: int - """Total lines of code added""" + limits: ModelCapabilitiesLimitsClass | None = None + """Token limits for prompts, outputs, and context window""" - lines_removed: int - """Total lines of code removed""" + supports: ModelCapabilitiesOverrideSupports | None = None + """Feature flags indicating what the model supports""" @staticmethod - def from_dict(obj: Any) -> 'UsageMetricsCodeChanges': + def from_dict(obj: Any) -> 'ModelCapabilitiesClass': assert isinstance(obj, dict) - files_modified_count = from_int(obj.get("filesModifiedCount")) - lines_added = from_int(obj.get("linesAdded")) - lines_removed = from_int(obj.get("linesRemoved")) - return UsageMetricsCodeChanges(files_modified_count, lines_added, lines_removed) + limits = from_union([ModelCapabilitiesLimitsClass.from_dict, from_none], obj.get("limits")) + supports = from_union([ModelCapabilitiesOverrideSupports.from_dict, from_none], obj.get("supports")) + return ModelCapabilitiesClass(limits, supports) def to_dict(self) -> dict: result: dict = {} - result["filesModifiedCount"] = from_int(self.files_modified_count) - result["linesAdded"] = from_int(self.lines_added) - result["linesRemoved"] = from_int(self.lines_removed) + if self.limits is not None: + result["limits"] = from_union([lambda x: to_class(ModelCapabilitiesLimitsClass, x), from_none], self.limits) + if self.supports is not None: + result["supports"] = from_union([lambda x: to_class(ModelCapabilitiesOverrideSupports, x), from_none], self.supports) return result @dataclass -class UsageMetricsModelMetricRequests: - """Request count and cost metrics for this model""" - - cost: float - """User-initiated premium request cost (with multiplier applied)""" - - count: int - """Number of API requests made with this model""" +class WorkspacesGetWorkspaceResult: + workspace: Workspace | None = None + """Current workspace metadata, or null if not available""" @staticmethod - def from_dict(obj: Any) -> 'UsageMetricsModelMetricRequests': + def from_dict(obj: Any) -> 'WorkspacesGetWorkspaceResult': assert isinstance(obj, dict) - cost = from_float(obj.get("cost")) - count = from_int(obj.get("count")) - return UsageMetricsModelMetricRequests(cost, count) + workspace = from_union([Workspace.from_dict, from_none], obj.get("workspace")) + return WorkspacesGetWorkspaceResult(workspace) def to_dict(self) -> dict: result: dict = {} - result["cost"] = to_float(self.cost) - result["count"] = from_int(self.count) + result["workspace"] = from_union([lambda x: to_class(Workspace, x), from_none], self.workspace) return result @dataclass -class UsageMetricsModelMetricUsage: - """Token usage metrics for this model""" - - cache_read_tokens: int - """Total tokens read from prompt cache""" +class InstructionsGetSourcesResult: + sources: list[InstructionsSources] + """Instruction sources for the session""" - cache_write_tokens: int - """Total tokens written to prompt cache""" - - input_tokens: int - """Total input tokens consumed""" + @staticmethod + def from_dict(obj: Any) -> 'InstructionsGetSourcesResult': + assert isinstance(obj, dict) + sources = from_list(InstructionsSources.from_dict, obj.get("sources")) + return InstructionsGetSourcesResult(sources) - output_tokens: int - """Total output tokens produced""" + def to_dict(self) -> dict: + result: dict = {} + result["sources"] = from_list(lambda x: to_class(InstructionsSources, x), self.sources) + return result - reasoning_tokens: int | None = None - """Total output tokens used for reasoning""" +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class ExtensionList: + extensions: list[Extension] + """Discovered extensions and their current status""" @staticmethod - def from_dict(obj: Any) -> 'UsageMetricsModelMetricUsage': + def from_dict(obj: Any) -> 'ExtensionList': assert isinstance(obj, dict) - cache_read_tokens = from_int(obj.get("cacheReadTokens")) - cache_write_tokens = from_int(obj.get("cacheWriteTokens")) - input_tokens = from_int(obj.get("inputTokens")) - output_tokens = from_int(obj.get("outputTokens")) - reasoning_tokens = from_union([from_int, from_none], obj.get("reasoningTokens")) - return UsageMetricsModelMetricUsage(cache_read_tokens, cache_write_tokens, input_tokens, output_tokens, reasoning_tokens) + extensions = from_list(Extension.from_dict, obj.get("extensions")) + return ExtensionList(extensions) def to_dict(self) -> dict: result: dict = {} - result["cacheReadTokens"] = from_int(self.cache_read_tokens) - result["cacheWriteTokens"] = from_int(self.cache_write_tokens) - result["inputTokens"] = from_int(self.input_tokens) - result["outputTokens"] = from_int(self.output_tokens) - if self.reasoning_tokens is not None: - result["reasoningTokens"] = from_union([from_int, from_none], self.reasoning_tokens) + result["extensions"] = from_list(lambda x: to_class(Extension, x), self.extensions) return result @dataclass -class UsageMetricsModelMetric: - requests: UsageMetricsModelMetricRequests - """Request count and cost metrics for this model""" - - usage: UsageMetricsModelMetricUsage - """Token usage metrics for this model""" +class UIElicitationSchemaProperty: + type: UIElicitationSchemaPropertyNumberType + default: float | bool | list[str] | str | None = None + description: str | None = None + enum: list[str] | None = None + enum_names: list[str] | None = None + title: str | None = None + one_of: list[UIElicitationSchemaPropertyOneOf] | None = None + items: UIElicitationArrayFieldItems | None = None + max_items: float | None = None + min_items: float | None = None + format: UIElicitationSchemaPropertyStringFormat | None = None + max_length: float | None = None + min_length: float | None = None + maximum: float | None = None + minimum: float | None = None @staticmethod - def from_dict(obj: Any) -> 'UsageMetricsModelMetric': + def from_dict(obj: Any) -> 'UIElicitationSchemaProperty': assert isinstance(obj, dict) - requests = UsageMetricsModelMetricRequests.from_dict(obj.get("requests")) - usage = UsageMetricsModelMetricUsage.from_dict(obj.get("usage")) - return UsageMetricsModelMetric(requests, usage) + type = UIElicitationSchemaPropertyNumberType(obj.get("type")) + default = from_union([from_float, from_bool, lambda x: from_list(from_str, x), from_str, from_none], obj.get("default")) + description = from_union([from_str, from_none], obj.get("description")) + enum = from_union([lambda x: from_list(from_str, x), from_none], obj.get("enum")) + enum_names = from_union([lambda x: from_list(from_str, x), from_none], obj.get("enumNames")) + title = from_union([from_str, from_none], obj.get("title")) + one_of = from_union([lambda x: from_list(UIElicitationSchemaPropertyOneOf.from_dict, x), from_none], obj.get("oneOf")) + items = from_union([UIElicitationArrayFieldItems.from_dict, from_none], obj.get("items")) + max_items = from_union([from_float, from_none], obj.get("maxItems")) + min_items = from_union([from_float, from_none], obj.get("minItems")) + format = from_union([UIElicitationSchemaPropertyStringFormat, from_none], obj.get("format")) + max_length = from_union([from_float, from_none], obj.get("maxLength")) + min_length = from_union([from_float, from_none], obj.get("minLength")) + maximum = from_union([from_float, from_none], obj.get("maximum")) + minimum = from_union([from_float, from_none], obj.get("minimum")) + return UIElicitationSchemaProperty(type, default, description, enum, enum_names, title, one_of, items, max_items, min_items, format, max_length, min_length, maximum, minimum) def to_dict(self) -> dict: result: dict = {} - result["requests"] = to_class(UsageMetricsModelMetricRequests, self.requests) - result["usage"] = to_class(UsageMetricsModelMetricUsage, self.usage) + result["type"] = to_enum(UIElicitationSchemaPropertyNumberType, self.type) + if self.default is not None: + result["default"] = from_union([to_float, from_bool, lambda x: from_list(from_str, x), from_str, from_none], self.default) + if self.description is not None: + result["description"] = from_union([from_str, from_none], self.description) + if self.enum is not None: + result["enum"] = from_union([lambda x: from_list(from_str, x), from_none], self.enum) + if self.enum_names is not None: + result["enumNames"] = from_union([lambda x: from_list(from_str, x), from_none], self.enum_names) + if self.title is not None: + result["title"] = from_union([from_str, from_none], self.title) + if self.one_of is not None: + result["oneOf"] = from_union([lambda x: from_list(lambda x: to_class(UIElicitationSchemaPropertyOneOf, x), x), from_none], self.one_of) + if self.items is not None: + result["items"] = from_union([lambda x: to_class(UIElicitationArrayFieldItems, x), from_none], self.items) + if self.max_items is not None: + result["maxItems"] = from_union([to_float, from_none], self.max_items) + if self.min_items is not None: + result["minItems"] = from_union([to_float, from_none], self.min_items) + if self.format is not None: + result["format"] = from_union([lambda x: to_enum(UIElicitationSchemaPropertyStringFormat, x), from_none], self.format) + if self.max_length is not None: + result["maxLength"] = from_union([to_float, from_none], self.max_length) + if self.min_length is not None: + result["minLength"] = from_union([to_float, from_none], self.min_length) + if self.maximum is not None: + result["maximum"] = from_union([to_float, from_none], self.maximum) + if self.minimum is not None: + result["minimum"] = from_union([to_float, from_none], self.minimum) return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -2944,418 +3802,408 @@ class UsageGetMetricsResult: @staticmethod def from_dict(obj: Any) -> 'UsageGetMetricsResult': assert isinstance(obj, dict) - code_changes = UsageMetricsCodeChanges.from_dict(obj.get("codeChanges")) - last_call_input_tokens = from_int(obj.get("lastCallInputTokens")) - last_call_output_tokens = from_int(obj.get("lastCallOutputTokens")) - model_metrics = from_dict(UsageMetricsModelMetric.from_dict, obj.get("modelMetrics")) - session_start_time = from_int(obj.get("sessionStartTime")) - total_api_duration_ms = from_float(obj.get("totalApiDurationMs")) - total_premium_request_cost = from_float(obj.get("totalPremiumRequestCost")) - total_user_requests = from_int(obj.get("totalUserRequests")) - current_model = from_union([from_str, from_none], obj.get("currentModel")) - return UsageGetMetricsResult(code_changes, last_call_input_tokens, last_call_output_tokens, model_metrics, session_start_time, total_api_duration_ms, total_premium_request_cost, total_user_requests, current_model) + code_changes = UsageMetricsCodeChanges.from_dict(obj.get("codeChanges")) + last_call_input_tokens = from_int(obj.get("lastCallInputTokens")) + last_call_output_tokens = from_int(obj.get("lastCallOutputTokens")) + model_metrics = from_dict(UsageMetricsModelMetric.from_dict, obj.get("modelMetrics")) + session_start_time = from_int(obj.get("sessionStartTime")) + total_api_duration_ms = from_float(obj.get("totalApiDurationMs")) + total_premium_request_cost = from_float(obj.get("totalPremiumRequestCost")) + total_user_requests = from_int(obj.get("totalUserRequests")) + current_model = from_union([from_str, from_none], obj.get("currentModel")) + return UsageGetMetricsResult(code_changes, last_call_input_tokens, last_call_output_tokens, model_metrics, session_start_time, total_api_duration_ms, total_premium_request_cost, total_user_requests, current_model) + + def to_dict(self) -> dict: + result: dict = {} + result["codeChanges"] = to_class(UsageMetricsCodeChanges, self.code_changes) + result["lastCallInputTokens"] = from_int(self.last_call_input_tokens) + result["lastCallOutputTokens"] = from_int(self.last_call_output_tokens) + result["modelMetrics"] = from_dict(lambda x: to_class(UsageMetricsModelMetric, x), self.model_metrics) + result["sessionStartTime"] = from_int(self.session_start_time) + result["totalApiDurationMs"] = to_float(self.total_api_duration_ms) + result["totalPremiumRequestCost"] = to_float(self.total_premium_request_cost) + result["totalUserRequests"] = from_int(self.total_user_requests) + if self.current_model is not None: + result["currentModel"] = from_union([from_str, from_none], self.current_model) + return result + +@dataclass +class SessionFSReaddirWithTypesResult: + entries: list[SessionFSReaddirWithTypesEntry] + """Directory entries with type information""" + + @staticmethod + def from_dict(obj: Any) -> 'SessionFSReaddirWithTypesResult': + assert isinstance(obj, dict) + entries = from_list(SessionFSReaddirWithTypesEntry.from_dict, obj.get("entries")) + return SessionFSReaddirWithTypesResult(entries) + + def to_dict(self) -> dict: + result: dict = {} + result["entries"] = from_list(lambda x: to_class(SessionFSReaddirWithTypesEntry, x), self.entries) + return result + +@dataclass +class UIElicitationSchema: + """JSON Schema describing the form fields to present to the user""" + + properties: dict[str, UIElicitationSchemaProperty] + """Form field definitions, keyed by field name""" + + type: RequestedSchemaType + """Schema type indicator (always 'object')""" + + required: list[str] | None = None + """List of required field names""" + + @staticmethod + def from_dict(obj: Any) -> 'UIElicitationSchema': + assert isinstance(obj, dict) + properties = from_dict(UIElicitationSchemaProperty.from_dict, obj.get("properties")) + type = RequestedSchemaType(obj.get("type")) + required = from_union([lambda x: from_list(from_str, x), from_none], obj.get("required")) + return UIElicitationSchema(properties, type, required) def to_dict(self) -> dict: result: dict = {} - result["codeChanges"] = to_class(UsageMetricsCodeChanges, self.code_changes) - result["lastCallInputTokens"] = from_int(self.last_call_input_tokens) - result["lastCallOutputTokens"] = from_int(self.last_call_output_tokens) - result["modelMetrics"] = from_dict(lambda x: to_class(UsageMetricsModelMetric, x), self.model_metrics) - result["sessionStartTime"] = from_int(self.session_start_time) - result["totalApiDurationMs"] = to_float(self.total_api_duration_ms) - result["totalPremiumRequestCost"] = to_float(self.total_premium_request_cost) - result["totalUserRequests"] = from_int(self.total_user_requests) - if self.current_model is not None: - result["currentModel"] = from_union([from_str, from_none], self.current_model) + result["properties"] = from_dict(lambda x: to_class(UIElicitationSchemaProperty, x), self.properties) + result["type"] = to_enum(RequestedSchemaType, self.type) + if self.required is not None: + result["required"] = from_union([lambda x: from_list(from_str, x), from_none], self.required) return result @dataclass -class SessionFSReadFileResult: - content: str - """File content as UTF-8 string""" +class UIElicitationRequest: + message: str + """Message describing what information is needed from the user""" + + requested_schema: UIElicitationSchema + """JSON Schema describing the form fields to present to the user""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSReadFileResult': + def from_dict(obj: Any) -> 'UIElicitationRequest': assert isinstance(obj, dict) - content = from_str(obj.get("content")) - return SessionFSReadFileResult(content) + message = from_str(obj.get("message")) + requested_schema = UIElicitationSchema.from_dict(obj.get("requestedSchema")) + return UIElicitationRequest(message, requested_schema) def to_dict(self) -> dict: result: dict = {} - result["content"] = from_str(self.content) + result["message"] = from_str(self.message) + result["requestedSchema"] = to_class(UIElicitationSchema, self.requested_schema) return result @dataclass -class SessionFSReadFileRequest: - path: str - """Path using SessionFs conventions""" +class ModelCapabilities: + """Model capabilities and limits""" - session_id: str - """Target session identifier""" + limits: ModelCapabilitiesLimits | None = None + """Token limits for prompts, outputs, and context window""" + + supports: ModelCapabilitiesSupports | None = None + """Feature flags indicating what the model supports""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSReadFileRequest': + def from_dict(obj: Any) -> 'ModelCapabilities': assert isinstance(obj, dict) - path = from_str(obj.get("path")) - session_id = from_str(obj.get("sessionId")) - return SessionFSReadFileRequest(path, session_id) + limits = from_union([ModelCapabilitiesLimits.from_dict, from_none], obj.get("limits")) + supports = from_union([ModelCapabilitiesSupports.from_dict, from_none], obj.get("supports")) + return ModelCapabilities(limits, supports) def to_dict(self) -> dict: result: dict = {} - result["path"] = from_str(self.path) - result["sessionId"] = from_str(self.session_id) + if self.limits is not None: + result["limits"] = from_union([lambda x: to_class(ModelCapabilitiesLimits, x), from_none], self.limits) + if self.supports is not None: + result["supports"] = from_union([lambda x: to_class(ModelCapabilitiesSupports, x), from_none], self.supports) return result @dataclass -class SessionFSWriteFileRequest: - content: str - """Content to write""" - - path: str - """Path using SessionFs conventions""" +class CapabilitiesClass: + """Model capabilities and limits""" - session_id: str - """Target session identifier""" + limits: CapabilitiesLimits | None = None + """Token limits for prompts, outputs, and context window""" - mode: int | None = None - """Optional POSIX-style mode for newly created files""" + supports: CapabilitiesSupports | None = None + """Feature flags indicating what the model supports""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSWriteFileRequest': + def from_dict(obj: Any) -> 'CapabilitiesClass': assert isinstance(obj, dict) - content = from_str(obj.get("content")) - path = from_str(obj.get("path")) - session_id = from_str(obj.get("sessionId")) - mode = from_union([from_int, from_none], obj.get("mode")) - return SessionFSWriteFileRequest(content, path, session_id, mode) + limits = from_union([CapabilitiesLimits.from_dict, from_none], obj.get("limits")) + supports = from_union([CapabilitiesSupports.from_dict, from_none], obj.get("supports")) + return CapabilitiesClass(limits, supports) def to_dict(self) -> dict: result: dict = {} - result["content"] = from_str(self.content) - result["path"] = from_str(self.path) - result["sessionId"] = from_str(self.session_id) - if self.mode is not None: - result["mode"] = from_union([from_int, from_none], self.mode) + if self.limits is not None: + result["limits"] = from_union([lambda x: to_class(CapabilitiesLimits, x), from_none], self.limits) + if self.supports is not None: + result["supports"] = from_union([lambda x: to_class(CapabilitiesSupports, x), from_none], self.supports) return result @dataclass -class SessionFSAppendFileRequest: - content: str - """Content to append""" +class Model: + capabilities: CapabilitiesClass + """Model capabilities and limits""" - path: str - """Path using SessionFs conventions""" + id: str + """Model identifier (e.g., "claude-sonnet-4.5")""" - session_id: str - """Target session identifier""" + name: str + """Display name""" - mode: int | None = None - """Optional POSIX-style mode for newly created files""" + billing: ModelBilling | None = None + """Billing information""" + + default_reasoning_effort: str | None = None + """Default reasoning effort level (only present if model supports reasoning effort)""" + + policy: ModelPolicy | None = None + """Policy state (if applicable)""" + + supported_reasoning_efforts: list[str] | None = None + """Supported reasoning effort levels (only present if model supports reasoning effort)""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSAppendFileRequest': + def from_dict(obj: Any) -> 'Model': assert isinstance(obj, dict) - content = from_str(obj.get("content")) - path = from_str(obj.get("path")) - session_id = from_str(obj.get("sessionId")) - mode = from_union([from_int, from_none], obj.get("mode")) - return SessionFSAppendFileRequest(content, path, session_id, mode) + capabilities = CapabilitiesClass.from_dict(obj.get("capabilities")) + id = from_str(obj.get("id")) + name = from_str(obj.get("name")) + billing = from_union([ModelBilling.from_dict, from_none], obj.get("billing")) + default_reasoning_effort = from_union([from_str, from_none], obj.get("defaultReasoningEffort")) + policy = from_union([ModelPolicy.from_dict, from_none], obj.get("policy")) + supported_reasoning_efforts = from_union([lambda x: from_list(from_str, x), from_none], obj.get("supportedReasoningEfforts")) + return Model(capabilities, id, name, billing, default_reasoning_effort, policy, supported_reasoning_efforts) def to_dict(self) -> dict: result: dict = {} - result["content"] = from_str(self.content) - result["path"] = from_str(self.path) - result["sessionId"] = from_str(self.session_id) - if self.mode is not None: - result["mode"] = from_union([from_int, from_none], self.mode) + result["capabilities"] = to_class(CapabilitiesClass, self.capabilities) + result["id"] = from_str(self.id) + result["name"] = from_str(self.name) + if self.billing is not None: + result["billing"] = from_union([lambda x: to_class(ModelBilling, x), from_none], self.billing) + if self.default_reasoning_effort is not None: + result["defaultReasoningEffort"] = from_union([from_str, from_none], self.default_reasoning_effort) + if self.policy is not None: + result["policy"] = from_union([lambda x: to_class(ModelPolicy, x), from_none], self.policy) + if self.supported_reasoning_efforts is not None: + result["supportedReasoningEfforts"] = from_union([lambda x: from_list(from_str, x), from_none], self.supported_reasoning_efforts) return result @dataclass -class SessionFSExistsResult: - exists: bool - """Whether the path exists""" +class ModelList: + models: list[Model] + """List of available models with full metadata""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSExistsResult': + def from_dict(obj: Any) -> 'ModelList': assert isinstance(obj, dict) - exists = from_bool(obj.get("exists")) - return SessionFSExistsResult(exists) + models = from_list(Model.from_dict, obj.get("models")) + return ModelList(models) def to_dict(self) -> dict: result: dict = {} - result["exists"] = from_bool(self.exists) + result["models"] = from_list(lambda x: to_class(Model, x), self.models) return result @dataclass -class SessionFSExistsRequest: - path: str - """Path using SessionFs conventions""" +class ModelSwitchToRequest: + model_id: str + """Model identifier to switch to""" - session_id: str - """Target session identifier""" + model_capabilities: ModelCapabilitiesClass | None = None + """Override individual model capabilities resolved by the runtime""" + + reasoning_effort: str | None = None + """Reasoning effort level to use for the model""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSExistsRequest': + def from_dict(obj: Any) -> 'ModelSwitchToRequest': assert isinstance(obj, dict) - path = from_str(obj.get("path")) - session_id = from_str(obj.get("sessionId")) - return SessionFSExistsRequest(path, session_id) + model_id = from_str(obj.get("modelId")) + model_capabilities = from_union([ModelCapabilitiesClass.from_dict, from_none], obj.get("modelCapabilities")) + reasoning_effort = from_union([from_str, from_none], obj.get("reasoningEffort")) + return ModelSwitchToRequest(model_id, model_capabilities, reasoning_effort) def to_dict(self) -> dict: result: dict = {} - result["path"] = from_str(self.path) - result["sessionId"] = from_str(self.session_id) + result["modelId"] = from_str(self.model_id) + if self.model_capabilities is not None: + result["modelCapabilities"] = from_union([lambda x: to_class(ModelCapabilitiesClass, x), from_none], self.model_capabilities) + if self.reasoning_effort is not None: + result["reasoningEffort"] = from_union([from_str, from_none], self.reasoning_effort) return result -@dataclass -class SessionFSStatResult: - birthtime: datetime - """ISO 8601 timestamp of creation""" +def model_capabilities_from_dict(s: Any) -> ModelCapabilities: + return ModelCapabilities.from_dict(s) - is_directory: bool - """Whether the path is a directory""" +def model_capabilities_to_dict(x: ModelCapabilities) -> Any: + return to_class(ModelCapabilities, x) - is_file: bool - """Whether the path is a file""" +def model_capabilities_limits_vision_from_dict(s: Any) -> ModelCapabilitiesLimitsVision: + return ModelCapabilitiesLimitsVision.from_dict(s) - mtime: datetime - """ISO 8601 timestamp of last modification""" +def model_capabilities_limits_vision_to_dict(x: ModelCapabilitiesLimitsVision) -> Any: + return to_class(ModelCapabilitiesLimitsVision, x) - size: int - """File size in bytes""" +def mcp_server_config_from_dict(s: Any) -> MCPServerConfig: + return MCPServerConfig.from_dict(s) - @staticmethod - def from_dict(obj: Any) -> 'SessionFSStatResult': - assert isinstance(obj, dict) - birthtime = from_datetime(obj.get("birthtime")) - is_directory = from_bool(obj.get("isDirectory")) - is_file = from_bool(obj.get("isFile")) - mtime = from_datetime(obj.get("mtime")) - size = from_int(obj.get("size")) - return SessionFSStatResult(birthtime, is_directory, is_file, mtime, size) +def mcp_server_config_to_dict(x: MCPServerConfig) -> Any: + return to_class(MCPServerConfig, x) - def to_dict(self) -> dict: - result: dict = {} - result["birthtime"] = self.birthtime.isoformat() - result["isDirectory"] = from_bool(self.is_directory) - result["isFile"] = from_bool(self.is_file) - result["mtime"] = self.mtime.isoformat() - result["size"] = from_int(self.size) - return result +def filter_mapping_from_dict(s: Any) -> dict[str, FilterMappingString] | FilterMappingString: + return from_union([lambda x: from_dict(FilterMappingString, x), FilterMappingString], s) -@dataclass -class SessionFSStatRequest: - path: str - """Path using SessionFs conventions""" +def filter_mapping_to_dict(x: dict[str, FilterMappingString] | FilterMappingString) -> Any: + return from_union([lambda x: from_dict(lambda x: to_enum(FilterMappingString, x), x), lambda x: to_enum(FilterMappingString, x)], x) - session_id: str - """Target session identifier""" +def discovered_mcp_server_from_dict(s: Any) -> DiscoveredMCPServer: + return DiscoveredMCPServer.from_dict(s) - @staticmethod - def from_dict(obj: Any) -> 'SessionFSStatRequest': - assert isinstance(obj, dict) - path = from_str(obj.get("path")) - session_id = from_str(obj.get("sessionId")) - return SessionFSStatRequest(path, session_id) +def discovered_mcp_server_to_dict(x: DiscoveredMCPServer) -> Any: + return to_class(DiscoveredMCPServer, x) - def to_dict(self) -> dict: - result: dict = {} - result["path"] = from_str(self.path) - result["sessionId"] = from_str(self.session_id) - return result +def server_skill_list_from_dict(s: Any) -> ServerSkillList: + return ServerSkillList.from_dict(s) + +def server_skill_list_to_dict(x: ServerSkillList) -> Any: + return to_class(ServerSkillList, x) + +def server_skill_from_dict(s: Any) -> ServerSkill: + return ServerSkill.from_dict(s) + +def server_skill_to_dict(x: ServerSkill) -> Any: + return to_class(ServerSkill, x) + +def current_model_from_dict(s: Any) -> CurrentModel: + return CurrentModel.from_dict(s) + +def current_model_to_dict(x: CurrentModel) -> Any: + return to_class(CurrentModel, x) + +def model_capabilities_override_from_dict(s: Any) -> ModelCapabilitiesOverride: + return ModelCapabilitiesOverride.from_dict(s) + +def model_capabilities_override_to_dict(x: ModelCapabilitiesOverride) -> Any: + return to_class(ModelCapabilitiesOverride, x) + +def session_mode_from_dict(s: Any) -> SessionMode: + return SessionMode(s) -@dataclass -class SessionFSMkdirRequest: - path: str - """Path using SessionFs conventions""" +def session_mode_to_dict(x: SessionMode) -> Any: + return to_enum(SessionMode, x) - session_id: str - """Target session identifier""" +def agent_info_from_dict(s: Any) -> AgentInfo: + return AgentInfo.from_dict(s) - mode: int | None = None - """Optional POSIX-style mode for newly created directories""" +def agent_info_to_dict(x: AgentInfo) -> Any: + return to_class(AgentInfo, x) - recursive: bool | None = None - """Create parent directories as needed""" +def mcp_server_list_from_dict(s: Any) -> MCPServerList: + return MCPServerList.from_dict(s) - @staticmethod - def from_dict(obj: Any) -> 'SessionFSMkdirRequest': - assert isinstance(obj, dict) - path = from_str(obj.get("path")) - session_id = from_str(obj.get("sessionId")) - mode = from_union([from_int, from_none], obj.get("mode")) - recursive = from_union([from_bool, from_none], obj.get("recursive")) - return SessionFSMkdirRequest(path, session_id, mode, recursive) +def mcp_server_list_to_dict(x: MCPServerList) -> Any: + return to_class(MCPServerList, x) - def to_dict(self) -> dict: - result: dict = {} - result["path"] = from_str(self.path) - result["sessionId"] = from_str(self.session_id) - if self.mode is not None: - result["mode"] = from_union([from_int, from_none], self.mode) - if self.recursive is not None: - result["recursive"] = from_union([from_bool, from_none], self.recursive) - return result +def tool_call_result_from_dict(s: Any) -> ToolCallResult: + return ToolCallResult.from_dict(s) -@dataclass -class SessionFSReaddirResult: - entries: list[str] - """Entry names in the directory""" +def tool_call_result_to_dict(x: ToolCallResult) -> Any: + return to_class(ToolCallResult, x) - @staticmethod - def from_dict(obj: Any) -> 'SessionFSReaddirResult': - assert isinstance(obj, dict) - entries = from_list(from_str, obj.get("entries")) - return SessionFSReaddirResult(entries) +def handle_tool_call_result_from_dict(s: Any) -> HandleToolCallResult: + return HandleToolCallResult.from_dict(s) - def to_dict(self) -> dict: - result: dict = {} - result["entries"] = from_list(from_str, self.entries) - return result +def handle_tool_call_result_to_dict(x: HandleToolCallResult) -> Any: + return to_class(HandleToolCallResult, x) -@dataclass -class SessionFSReaddirRequest: - path: str - """Path using SessionFs conventions""" +def ui_elicitation_string_enum_field_from_dict(s: Any) -> UIElicitationStringEnumField: + return UIElicitationStringEnumField.from_dict(s) - session_id: str - """Target session identifier""" +def ui_elicitation_string_enum_field_to_dict(x: UIElicitationStringEnumField) -> Any: + return to_class(UIElicitationStringEnumField, x) - @staticmethod - def from_dict(obj: Any) -> 'SessionFSReaddirRequest': - assert isinstance(obj, dict) - path = from_str(obj.get("path")) - session_id = from_str(obj.get("sessionId")) - return SessionFSReaddirRequest(path, session_id) +def ui_elicitation_string_one_of_field_from_dict(s: Any) -> UIElicitationStringOneOfField: + return UIElicitationStringOneOfField.from_dict(s) - def to_dict(self) -> dict: - result: dict = {} - result["path"] = from_str(self.path) - result["sessionId"] = from_str(self.session_id) - return result +def ui_elicitation_string_one_of_field_to_dict(x: UIElicitationStringOneOfField) -> Any: + return to_class(UIElicitationStringOneOfField, x) -class SessionFSReaddirWithTypesEntryType(Enum): - """Entry type""" +def ui_elicitation_array_enum_field_from_dict(s: Any) -> UIElicitationArrayEnumField: + return UIElicitationArrayEnumField.from_dict(s) - DIRECTORY = "directory" - FILE = "file" +def ui_elicitation_array_enum_field_to_dict(x: UIElicitationArrayEnumField) -> Any: + return to_class(UIElicitationArrayEnumField, x) -@dataclass -class SessionFSReaddirWithTypesEntry: - name: str - """Entry name""" +def ui_elicitation_array_any_of_field_from_dict(s: Any) -> UIElicitationArrayAnyOfField: + return UIElicitationArrayAnyOfField.from_dict(s) - type: SessionFSReaddirWithTypesEntryType - """Entry type""" +def ui_elicitation_array_any_of_field_to_dict(x: UIElicitationArrayAnyOfField) -> Any: + return to_class(UIElicitationArrayAnyOfField, x) - @staticmethod - def from_dict(obj: Any) -> 'SessionFSReaddirWithTypesEntry': - assert isinstance(obj, dict) - name = from_str(obj.get("name")) - type = SessionFSReaddirWithTypesEntryType(obj.get("type")) - return SessionFSReaddirWithTypesEntry(name, type) +def ui_elicitation_response_from_dict(s: Any) -> UIElicitationResponse: + return UIElicitationResponse.from_dict(s) - def to_dict(self) -> dict: - result: dict = {} - result["name"] = from_str(self.name) - result["type"] = to_enum(SessionFSReaddirWithTypesEntryType, self.type) - return result +def ui_elicitation_response_to_dict(x: UIElicitationResponse) -> Any: + return to_class(UIElicitationResponse, x) -@dataclass -class SessionFSReaddirWithTypesResult: - entries: list[SessionFSReaddirWithTypesEntry] - """Directory entries with type information""" +def ui_elicitation_response_action_from_dict(s: Any) -> UIElicitationResponseAction: + return UIElicitationResponseAction(s) - @staticmethod - def from_dict(obj: Any) -> 'SessionFSReaddirWithTypesResult': - assert isinstance(obj, dict) - entries = from_list(SessionFSReaddirWithTypesEntry.from_dict, obj.get("entries")) - return SessionFSReaddirWithTypesResult(entries) +def ui_elicitation_response_action_to_dict(x: UIElicitationResponseAction) -> Any: + return to_enum(UIElicitationResponseAction, x) - def to_dict(self) -> dict: - result: dict = {} - result["entries"] = from_list(lambda x: to_class(SessionFSReaddirWithTypesEntry, x), self.entries) - return result +def ui_elicitation_response_content_from_dict(s: Any) -> dict[str, float | bool | list[str] | str]: + return from_dict(lambda x: from_union([from_float, from_bool, lambda x: from_list(from_str, x), from_str], x), s) -@dataclass -class SessionFSReaddirWithTypesRequest: - path: str - """Path using SessionFs conventions""" +def ui_elicitation_response_content_to_dict(x: dict[str, float | bool | list[str] | str]) -> Any: + return from_dict(lambda x: from_union([to_float, from_bool, lambda x: from_list(from_str, x), from_str], x), x) - session_id: str - """Target session identifier""" +def ui_elicitation_field_value_from_dict(s: Any) -> float | bool | list[str] | str: + return from_union([from_float, from_bool, lambda x: from_list(from_str, x), from_str], s) - @staticmethod - def from_dict(obj: Any) -> 'SessionFSReaddirWithTypesRequest': - assert isinstance(obj, dict) - path = from_str(obj.get("path")) - session_id = from_str(obj.get("sessionId")) - return SessionFSReaddirWithTypesRequest(path, session_id) +def ui_elicitation_field_value_to_dict(x: float | bool | list[str] | str) -> Any: + return from_union([to_float, from_bool, lambda x: from_list(from_str, x), from_str], x) - def to_dict(self) -> dict: - result: dict = {} - result["path"] = from_str(self.path) - result["sessionId"] = from_str(self.session_id) - return result +def ui_handle_pending_elicitation_request_from_dict(s: Any) -> UIHandlePendingElicitationRequest: + return UIHandlePendingElicitationRequest.from_dict(s) -@dataclass -class SessionFSRmRequest: - path: str - """Path using SessionFs conventions""" +def ui_handle_pending_elicitation_request_to_dict(x: UIHandlePendingElicitationRequest) -> Any: + return to_class(UIHandlePendingElicitationRequest, x) - session_id: str - """Target session identifier""" +def ui_elicitation_result_from_dict(s: Any) -> UIElicitationResult: + return UIElicitationResult.from_dict(s) - force: bool | None = None - """Ignore errors if the path does not exist""" +def ui_elicitation_result_to_dict(x: UIElicitationResult) -> Any: + return to_class(UIElicitationResult, x) - recursive: bool | None = None - """Remove directories and their contents recursively""" +def permission_decision_request_from_dict(s: Any) -> PermissionDecisionRequest: + return PermissionDecisionRequest.from_dict(s) - @staticmethod - def from_dict(obj: Any) -> 'SessionFSRmRequest': - assert isinstance(obj, dict) - path = from_str(obj.get("path")) - session_id = from_str(obj.get("sessionId")) - force = from_union([from_bool, from_none], obj.get("force")) - recursive = from_union([from_bool, from_none], obj.get("recursive")) - return SessionFSRmRequest(path, session_id, force, recursive) +def permission_decision_request_to_dict(x: PermissionDecisionRequest) -> Any: + return to_class(PermissionDecisionRequest, x) - def to_dict(self) -> dict: - result: dict = {} - result["path"] = from_str(self.path) - result["sessionId"] = from_str(self.session_id) - if self.force is not None: - result["force"] = from_union([from_bool, from_none], self.force) - if self.recursive is not None: - result["recursive"] = from_union([from_bool, from_none], self.recursive) - return result +def permission_decision_from_dict(s: Any) -> PermissionDecision: + return PermissionDecision.from_dict(s) -@dataclass -class SessionFSRenameRequest: - dest: str - """Destination path using SessionFs conventions""" +def permission_decision_to_dict(x: PermissionDecision) -> Any: + return to_class(PermissionDecision, x) - session_id: str - """Target session identifier""" +def permission_request_result_from_dict(s: Any) -> PermissionRequestResult: + return PermissionRequestResult.from_dict(s) - src: str - """Source path using SessionFs conventions""" +def permission_request_result_to_dict(x: PermissionRequestResult) -> Any: + return to_class(PermissionRequestResult, x) - @staticmethod - def from_dict(obj: Any) -> 'SessionFSRenameRequest': - assert isinstance(obj, dict) - dest = from_str(obj.get("dest")) - session_id = from_str(obj.get("sessionId")) - src = from_str(obj.get("src")) - return SessionFSRenameRequest(dest, session_id, src) +def session_log_level_from_dict(s: Any) -> SessionLogLevel: + return SessionLogLevel(s) - def to_dict(self) -> dict: - result: dict = {} - result["dest"] = from_str(self.dest) - result["sessionId"] = from_str(self.session_id) - result["src"] = from_str(self.src) - return result +def session_log_level_to_dict(x: SessionLogLevel) -> Any: + return to_enum(SessionLogLevel, x) def ping_result_from_dict(s: Any) -> PingResult: return PingResult.from_dict(s) @@ -3435,12 +4283,6 @@ def skills_config_set_disabled_skills_request_from_dict(s: Any) -> SkillsConfigS def skills_config_set_disabled_skills_request_to_dict(x: SkillsConfigSetDisabledSkillsRequest) -> Any: return to_class(SkillsConfigSetDisabledSkillsRequest, x) -def server_skill_list_from_dict(s: Any) -> ServerSkillList: - return ServerSkillList.from_dict(s) - -def server_skill_list_to_dict(x: ServerSkillList) -> Any: - return to_class(ServerSkillList, x) - def skills_discover_request_from_dict(s: Any) -> SkillsDiscoverRequest: return SkillsDiscoverRequest.from_dict(s) @@ -3471,12 +4313,6 @@ def sessions_fork_request_from_dict(s: Any) -> SessionsForkRequest: def sessions_fork_request_to_dict(x: SessionsForkRequest) -> Any: return to_class(SessionsForkRequest, x) -def current_model_from_dict(s: Any) -> CurrentModel: - return CurrentModel.from_dict(s) - -def current_model_to_dict(x: CurrentModel) -> Any: - return to_class(CurrentModel, x) - def model_switch_to_result_from_dict(s: Any) -> ModelSwitchToResult: return ModelSwitchToResult.from_dict(s) @@ -3489,12 +4325,6 @@ def model_switch_to_request_from_dict(s: Any) -> ModelSwitchToRequest: def model_switch_to_request_to_dict(x: ModelSwitchToRequest) -> Any: return to_class(ModelSwitchToRequest, x) -def session_mode_from_dict(s: Any) -> SessionMode: - return SessionMode(s) - -def session_mode_to_dict(x: SessionMode) -> Any: - return to_enum(SessionMode, x) - def mode_set_request_from_dict(s: Any) -> ModeSetRequest: return ModeSetRequest.from_dict(s) @@ -3555,6 +4385,12 @@ def workspaces_create_file_request_from_dict(s: Any) -> WorkspacesCreateFileRequ def workspaces_create_file_request_to_dict(x: WorkspacesCreateFileRequest) -> Any: return to_class(WorkspacesCreateFileRequest, x) +def instructions_get_sources_result_from_dict(s: Any) -> InstructionsGetSourcesResult: + return InstructionsGetSourcesResult.from_dict(s) + +def instructions_get_sources_result_to_dict(x: InstructionsGetSourcesResult) -> Any: + return to_class(InstructionsGetSourcesResult, x) + def fleet_start_result_from_dict(s: Any) -> FleetStartResult: return FleetStartResult.from_dict(s) @@ -3615,12 +4451,6 @@ def skills_disable_request_from_dict(s: Any) -> SkillsDisableRequest: def skills_disable_request_to_dict(x: SkillsDisableRequest) -> Any: return to_class(SkillsDisableRequest, x) -def mcp_server_list_from_dict(s: Any) -> MCPServerList: - return MCPServerList.from_dict(s) - -def mcp_server_list_to_dict(x: MCPServerList) -> Any: - return to_class(MCPServerList, x) - def mcp_enable_request_from_dict(s: Any) -> MCPEnableRequest: return MCPEnableRequest.from_dict(s) @@ -3657,12 +4487,6 @@ def extensions_disable_request_from_dict(s: Any) -> ExtensionsDisableRequest: def extensions_disable_request_to_dict(x: ExtensionsDisableRequest) -> Any: return to_class(ExtensionsDisableRequest, x) -def handle_tool_call_result_from_dict(s: Any) -> HandleToolCallResult: - return HandleToolCallResult.from_dict(s) - -def handle_tool_call_result_to_dict(x: HandleToolCallResult) -> Any: - return to_class(HandleToolCallResult, x) - def tools_handle_pending_tool_call_request_from_dict(s: Any) -> ToolsHandlePendingToolCallRequest: return ToolsHandlePendingToolCallRequest.from_dict(s) @@ -3681,42 +4505,12 @@ def commands_handle_pending_command_request_from_dict(s: Any) -> CommandsHandleP def commands_handle_pending_command_request_to_dict(x: CommandsHandlePendingCommandRequest) -> Any: return to_class(CommandsHandlePendingCommandRequest, x) -def ui_elicitation_response_from_dict(s: Any) -> UIElicitationResponse: - return UIElicitationResponse.from_dict(s) - -def ui_elicitation_response_to_dict(x: UIElicitationResponse) -> Any: - return to_class(UIElicitationResponse, x) - def ui_elicitation_request_from_dict(s: Any) -> UIElicitationRequest: return UIElicitationRequest.from_dict(s) def ui_elicitation_request_to_dict(x: UIElicitationRequest) -> Any: return to_class(UIElicitationRequest, x) -def ui_elicitation_result_from_dict(s: Any) -> UIElicitationResult: - return UIElicitationResult.from_dict(s) - -def ui_elicitation_result_to_dict(x: UIElicitationResult) -> Any: - return to_class(UIElicitationResult, x) - -def ui_handle_pending_elicitation_request_from_dict(s: Any) -> UIHandlePendingElicitationRequest: - return UIHandlePendingElicitationRequest.from_dict(s) - -def ui_handle_pending_elicitation_request_to_dict(x: UIHandlePendingElicitationRequest) -> Any: - return to_class(UIHandlePendingElicitationRequest, x) - -def permission_request_result_from_dict(s: Any) -> PermissionRequestResult: - return PermissionRequestResult.from_dict(s) - -def permission_request_result_to_dict(x: PermissionRequestResult) -> Any: - return to_class(PermissionRequestResult, x) - -def permission_decision_request_from_dict(s: Any) -> PermissionDecisionRequest: - return PermissionDecisionRequest.from_dict(s) - -def permission_decision_request_to_dict(x: PermissionDecisionRequest) -> Any: - return to_class(PermissionDecisionRequest, x) - def log_result_from_dict(s: Any) -> LogResult: return LogResult.from_dict(s) @@ -4087,6 +4881,15 @@ async def create_file(self, params: WorkspacesCreateFileRequest, *, timeout: flo await self._client.request("session.workspaces.createFile", params_dict, **_timeout_kwargs(timeout)) +class InstructionsApi: + def __init__(self, client: "JsonRpcClient", session_id: str): + self._client = client + self._session_id = session_id + + async def get_sources(self, *, timeout: float | None = None) -> InstructionsGetSourcesResult: + return InstructionsGetSourcesResult.from_dict(await self._client.request("session.instructions.getSources", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + + # Experimental: this API group is experimental and may change or be removed. class FleetApi: def __init__(self, client: "JsonRpcClient", session_id: str): @@ -4302,6 +5105,7 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self.name = NameApi(client, session_id) self.plan = PlanApi(client, session_id) self.workspaces = WorkspacesApi(client, session_id) + self.instructions = InstructionsApi(client, session_id) self.fleet = FleetApi(client, session_id) self.agent = AgentApi(client, session_id) self.skills = SkillsApi(client, session_id) diff --git a/python/copilot/generated/session_events.py b/python/copilot/generated/session_events.py index 784b0bb52..7cbff3039 100644 --- a/python/copilot/generated/session_events.py +++ b/python/copilot/generated/session_events.py @@ -252,27 +252,27 @@ def to_dict(self) -> dict: @dataclass -class SessionStartDataContext: +class WorkingDirectoryContext: "Working directory and git context at session start" cwd: str git_root: str | None = None repository: str | None = None - host_type: SessionStartDataContextHostType | None = None + host_type: WorkingDirectoryContextHostType | None = None branch: str | None = None head_commit: str | None = None base_commit: str | None = None @staticmethod - def from_dict(obj: Any) -> "SessionStartDataContext": + def from_dict(obj: Any) -> "WorkingDirectoryContext": assert isinstance(obj, dict) cwd = from_str(obj.get("cwd")) git_root = from_union([from_none, from_str], obj.get("gitRoot")) repository = from_union([from_none, from_str], obj.get("repository")) - host_type = from_union([from_none, lambda x: parse_enum(SessionStartDataContextHostType, x)], obj.get("hostType")) + host_type = from_union([from_none, lambda x: parse_enum(WorkingDirectoryContextHostType, x)], obj.get("hostType")) branch = from_union([from_none, from_str], obj.get("branch")) head_commit = from_union([from_none, from_str], obj.get("headCommit")) base_commit = from_union([from_none, from_str], obj.get("baseCommit")) - return SessionStartDataContext( + return WorkingDirectoryContext( cwd=cwd, git_root=git_root, repository=repository, @@ -290,7 +290,7 @@ def to_dict(self) -> dict: if self.repository is not None: result["repository"] = from_union([from_none, from_str], self.repository) if self.host_type is not None: - result["hostType"] = from_union([from_none, lambda x: to_enum(SessionStartDataContextHostType, x)], self.host_type) + result["hostType"] = from_union([from_none, lambda x: to_enum(WorkingDirectoryContextHostType, x)], self.host_type) if self.branch is not None: result["branch"] = from_union([from_none, from_str], self.branch) if self.head_commit is not None: @@ -310,7 +310,7 @@ class SessionStartData: start_time: datetime selected_model: str | None = None reasoning_effort: str | None = None - context: SessionStartDataContext | None = None + context: WorkingDirectoryContext | None = None already_in_use: bool | None = None remote_steerable: bool | None = None @@ -324,7 +324,7 @@ def from_dict(obj: Any) -> "SessionStartData": start_time = from_datetime(obj.get("startTime")) selected_model = from_union([from_none, from_str], obj.get("selectedModel")) reasoning_effort = from_union([from_none, from_str], obj.get("reasoningEffort")) - context = from_union([from_none, SessionStartDataContext.from_dict], obj.get("context")) + context = from_union([from_none, WorkingDirectoryContext.from_dict], obj.get("context")) already_in_use = from_union([from_none, from_bool], obj.get("alreadyInUse")) remote_steerable = from_union([from_none, from_bool], obj.get("remoteSteerable")) return SessionStartData( @@ -352,7 +352,7 @@ def to_dict(self) -> dict: if self.reasoning_effort is not None: result["reasoningEffort"] = from_union([from_none, from_str], self.reasoning_effort) if self.context is not None: - result["context"] = from_union([from_none, lambda x: to_class(SessionStartDataContext, x)], self.context) + result["context"] = from_union([from_none, lambda x: to_class(WorkingDirectoryContext, x)], self.context) if self.already_in_use is not None: result["alreadyInUse"] = from_union([from_none, from_bool], self.already_in_use) if self.remote_steerable is not None: @@ -360,55 +360,6 @@ def to_dict(self) -> dict: return result -@dataclass -class SessionResumeDataContext: - "Updated working directory and git context at resume time" - cwd: str - git_root: str | None = None - repository: str | None = None - host_type: SessionResumeDataContextHostType | None = None - branch: str | None = None - head_commit: str | None = None - base_commit: str | None = None - - @staticmethod - def from_dict(obj: Any) -> "SessionResumeDataContext": - assert isinstance(obj, dict) - cwd = from_str(obj.get("cwd")) - git_root = from_union([from_none, from_str], obj.get("gitRoot")) - repository = from_union([from_none, from_str], obj.get("repository")) - host_type = from_union([from_none, lambda x: parse_enum(SessionResumeDataContextHostType, x)], obj.get("hostType")) - branch = from_union([from_none, from_str], obj.get("branch")) - head_commit = from_union([from_none, from_str], obj.get("headCommit")) - base_commit = from_union([from_none, from_str], obj.get("baseCommit")) - return SessionResumeDataContext( - cwd=cwd, - git_root=git_root, - repository=repository, - host_type=host_type, - branch=branch, - head_commit=head_commit, - base_commit=base_commit, - ) - - def to_dict(self) -> dict: - result: dict = {} - result["cwd"] = from_str(self.cwd) - if self.git_root is not None: - result["gitRoot"] = from_union([from_none, from_str], self.git_root) - if self.repository is not None: - result["repository"] = from_union([from_none, from_str], self.repository) - if self.host_type is not None: - result["hostType"] = from_union([from_none, lambda x: to_enum(SessionResumeDataContextHostType, x)], self.host_type) - if self.branch is not None: - result["branch"] = from_union([from_none, from_str], self.branch) - if self.head_commit is not None: - result["headCommit"] = from_union([from_none, from_str], self.head_commit) - if self.base_commit is not None: - result["baseCommit"] = from_union([from_none, from_str], self.base_commit) - return result - - @dataclass class SessionResumeData: "Session resume metadata including current context and event count" @@ -416,7 +367,7 @@ class SessionResumeData: event_count: float selected_model: str | None = None reasoning_effort: str | None = None - context: SessionResumeDataContext | None = None + context: WorkingDirectoryContext | None = None already_in_use: bool | None = None remote_steerable: bool | None = None @@ -427,7 +378,7 @@ def from_dict(obj: Any) -> "SessionResumeData": event_count = from_float(obj.get("eventCount")) selected_model = from_union([from_none, from_str], obj.get("selectedModel")) reasoning_effort = from_union([from_none, from_str], obj.get("reasoningEffort")) - context = from_union([from_none, SessionResumeDataContext.from_dict], obj.get("context")) + context = from_union([from_none, WorkingDirectoryContext.from_dict], obj.get("context")) already_in_use = from_union([from_none, from_bool], obj.get("alreadyInUse")) remote_steerable = from_union([from_none, from_bool], obj.get("remoteSteerable")) return SessionResumeData( @@ -449,7 +400,7 @@ def to_dict(self) -> dict: if self.reasoning_effort is not None: result["reasoningEffort"] = from_union([from_none, from_str], self.reasoning_effort) if self.context is not None: - result["context"] = from_union([from_none, lambda x: to_class(SessionResumeDataContext, x)], self.context) + result["context"] = from_union([from_none, lambda x: to_class(WorkingDirectoryContext, x)], self.context) if self.already_in_use is not None: result["alreadyInUse"] = from_union([from_none, from_bool], self.already_in_use) if self.remote_steerable is not None: @@ -1038,7 +989,7 @@ def to_dict(self) -> dict: @dataclass class SessionContextChangedData: - "Updated working directory and git context after the change" + "Working directory and git context at session start" cwd: str git_root: str | None = None repository: str | None = None @@ -1484,6 +1435,8 @@ class UserMessageData: content: str transformed_content: str | None = None attachments: list[UserMessageAttachment] | None = None + supported_native_document_mime_types: list[str] | None = None + native_document_path_fallback_paths: list[str] | None = None source: str | None = None agent_mode: UserMessageAgentMode | None = None interaction_id: str | None = None @@ -1494,6 +1447,8 @@ def from_dict(obj: Any) -> "UserMessageData": content = from_str(obj.get("content")) transformed_content = from_union([from_none, from_str], obj.get("transformedContent")) attachments = from_union([from_none, lambda x: from_list(UserMessageAttachment.from_dict, x)], obj.get("attachments")) + supported_native_document_mime_types = from_union([from_none, lambda x: from_list(from_str, x)], obj.get("supportedNativeDocumentMimeTypes")) + native_document_path_fallback_paths = from_union([from_none, lambda x: from_list(from_str, x)], obj.get("nativeDocumentPathFallbackPaths")) source = from_union([from_none, from_str], obj.get("source")) agent_mode = from_union([from_none, lambda x: parse_enum(UserMessageAgentMode, x)], obj.get("agentMode")) interaction_id = from_union([from_none, from_str], obj.get("interactionId")) @@ -1501,6 +1456,8 @@ def from_dict(obj: Any) -> "UserMessageData": content=content, transformed_content=transformed_content, attachments=attachments, + supported_native_document_mime_types=supported_native_document_mime_types, + native_document_path_fallback_paths=native_document_path_fallback_paths, source=source, agent_mode=agent_mode, interaction_id=interaction_id, @@ -1513,6 +1470,10 @@ def to_dict(self) -> dict: result["transformedContent"] = from_union([from_none, from_str], self.transformed_content) if self.attachments is not None: result["attachments"] = from_union([from_none, lambda x: from_list(lambda x: to_class(UserMessageAttachment, x), x)], self.attachments) + if self.supported_native_document_mime_types is not None: + result["supportedNativeDocumentMimeTypes"] = from_union([from_none, lambda x: from_list(from_str, x)], self.supported_native_document_mime_types) + if self.native_document_path_fallback_paths is not None: + result["nativeDocumentPathFallbackPaths"] = from_union([from_none, lambda x: from_list(from_str, x)], self.native_document_path_fallback_paths) if self.source is not None: result["source"] = from_union([from_none, from_str], self.source) if self.agent_mode is not None: @@ -1703,6 +1664,7 @@ class AssistantMessageData: output_tokens: float | None = None interaction_id: str | None = None request_id: str | None = None + # Deprecated: this field is deprecated. parent_tool_call_id: str | None = None @staticmethod @@ -1763,6 +1725,7 @@ class AssistantMessageDeltaData: "Streaming assistant message delta for incremental response updates" message_id: str delta_content: str + # Deprecated: this field is deprecated. parent_tool_call_id: str | None = None @staticmethod @@ -1922,6 +1885,7 @@ class AssistantUsageData: initiator: str | None = None api_call_id: str | None = None provider_call_id: str | None = None + # Deprecated: this field is deprecated. parent_tool_call_id: str | None = None quota_snapshots: dict[str, AssistantUsageQuotaSnapshot] | None = None copilot_usage: AssistantUsageCopilotUsage | None = None @@ -2060,6 +2024,7 @@ class ToolExecutionStartData: arguments: Any = None mcp_server_name: str | None = None mcp_tool_name: str | None = None + # Deprecated: this field is deprecated. parent_tool_call_id: str | None = None @staticmethod @@ -2318,6 +2283,7 @@ class ToolExecutionCompleteData: result: ToolExecutionCompleteDataResult | None = None error: ToolExecutionCompleteDataError | None = None tool_telemetry: dict[str, Any] | None = None + # Deprecated: this field is deprecated. parent_tool_call_id: str | None = None @staticmethod @@ -2696,7 +2662,7 @@ def to_dict(self) -> dict: @dataclass class SystemMessageData: - "System or developer message content with role and optional template metadata" + "System/developer instruction content with role and optional template metadata" content: str role: SystemMessageDataRole name: str | None = None @@ -3939,13 +3905,7 @@ def to_dict(self) -> dict: return result -class SessionStartDataContextHostType(Enum): - "Hosting platform type of the repository (github or ado)" - GITHUB = "github" - ADO = "ado" - - -class SessionResumeDataContextHostType(Enum): +class WorkingDirectoryContextHostType(Enum): "Hosting platform type of the repository (github or ado)" GITHUB = "github" ADO = "ado" diff --git a/python/copilot/session.py b/python/copilot/session.py index 9552f75b6..43a1c4c5a 100644 --- a/python/copilot/session.py +++ b/python/copilot/session.py @@ -43,7 +43,7 @@ UIElicitationSchemaPropertyNumberType, UIHandlePendingElicitationRequest, ) -from .generated.rpc import ModelCapabilitiesOverride as _RpcModelCapabilitiesOverride +from .generated.rpc import ModelCapabilitiesClass as _RpcModelCapabilitiesOverride from .generated.session_events import ( AssistantMessageData, CapabilitiesChangedData, diff --git a/scripts/codegen/csharp.ts b/scripts/codegen/csharp.ts index f8bcfad1c..d9a4b0f96 100644 --- a/scripts/codegen/csharp.ts +++ b/scripts/codegen/csharp.ts @@ -13,6 +13,7 @@ import { promisify } from "util"; import type { JSONSchema7 } from "json-schema"; import { cloneSchemaForCodegen, + fixNullableRequiredRefsInApiSchema, getApiSchemaPath, getRpcSchemaTypeName, getSessionEventsSchemaPath, @@ -326,7 +327,7 @@ function getOrCreateEnum(parentClassName: string, propName: string, values: stri const lines: string[] = []; lines.push(...xmlDocEnumComment(description, "")); - if (deprecated) lines.push(`[Obsolete]`); + if (deprecated) lines.push(`[Obsolete("This member is deprecated and will be removed in a future version.")]`); lines.push(`[JsonConverter(typeof(JsonStringEnumConverter<${enumName}>))]`, `public enum ${enumName}`, `{`); for (const value of values) { lines.push(` /// The ${escapeXml(value)} variant.`); @@ -461,7 +462,7 @@ function generateDerivedClass( const required = new Set(schema.required || []); lines.push(...xmlDocCommentWithFallback(schema.description, `The ${escapeXml(discriminatorValue)} variant of .`, "")); - if (isSchemaDeprecated(schema)) lines.push(`[Obsolete]`); + if (isSchemaDeprecated(schema)) lines.push(`[Obsolete("This member is deprecated and will be removed in a future version.")]`); lines.push(`public partial class ${className} : ${baseClassName}`); lines.push(`{`); lines.push(` /// `); @@ -480,7 +481,7 @@ function generateDerivedClass( lines.push(...xmlDocPropertyComment((propSchema as JSONSchema7).description, propName, " ")); lines.push(...emitDataAnnotations(propSchema as JSONSchema7, " ")); - if (isSchemaDeprecated(propSchema as JSONSchema7)) lines.push(` [Obsolete]`); + if (isSchemaDeprecated(propSchema as JSONSchema7)) lines.push(` [Obsolete("This member is deprecated and will be removed in a future version.")]`); if (isDurationProperty(propSchema as JSONSchema7)) lines.push(` [JsonConverter(typeof(MillisecondsTimeSpanConverter))]`); if (!isReq) lines.push(` [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]`); lines.push(` [JsonPropertyName("${propName}")]`); @@ -504,7 +505,7 @@ function generateNestedClass( const required = new Set(schema.required || []); const lines: string[] = []; lines.push(...xmlDocCommentWithFallback(schema.description, `Nested data type for ${className}.`, "")); - if (isSchemaDeprecated(schema)) lines.push(`[Obsolete]`); + if (isSchemaDeprecated(schema)) lines.push(`[Obsolete("This member is deprecated and will be removed in a future version.")]`); lines.push(`public partial class ${className}`, `{`); for (const [propName, propSchema] of Object.entries(schema.properties || {})) { @@ -516,7 +517,7 @@ function generateNestedClass( lines.push(...xmlDocPropertyComment(prop.description, propName, " ")); lines.push(...emitDataAnnotations(prop, " ")); - if (isSchemaDeprecated(prop)) lines.push(` [Obsolete]`); + if (isSchemaDeprecated(prop)) lines.push(` [Obsolete("This member is deprecated and will be removed in a future version.")]`); if (isDurationProperty(prop)) lines.push(` [JsonConverter(typeof(MillisecondsTimeSpanConverter))]`); if (!isReq) lines.push(` [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]`); lines.push(` [JsonPropertyName("${propName}")]`); @@ -615,7 +616,7 @@ function generateDataClass(variant: EventVariant, knownTypes: Map.`, "")); } if (isSchemaDeprecated(variant.dataSchema)) { - lines.push(`[Obsolete]`); + lines.push(`[Obsolete("This member is deprecated and will be removed in a future version.")]`); } lines.push(`public partial class ${variant.dataClassName}`, `{`); @@ -627,7 +628,7 @@ function generateDataClass(variant: EventVariant, knownTypes: Map, cl lines.push(`[Experimental(Diagnostics.Experimental)]`); } if (groupDeprecated) { - lines.push(`[Obsolete]`); + lines.push(`[Obsolete("This member is deprecated and will be removed in a future version.")]`); } lines.push(`public sealed class ${className}`); lines.push(`{`); @@ -1104,7 +1105,7 @@ function emitServerInstanceMethod( lines.push(`${indent}[Experimental(Diagnostics.Experimental)]`); } if (method.deprecated && !groupDeprecated) { - lines.push(`${indent}[Obsolete]`); + lines.push(`${indent}[Obsolete("This member is deprecated and will be removed in a future version.")]`); } const sigParams: string[] = []; @@ -1208,7 +1209,7 @@ function emitSessionMethod(key: string, method: RpcMethod, lines: string[], clas lines.push(`${indent}[Experimental(Diagnostics.Experimental)]`); } if (method.deprecated && !groupDeprecated) { - lines.push(`${indent}[Obsolete]`); + lines.push(`${indent}[Obsolete("This member is deprecated and will be removed in a future version.")]`); } const sigParams: string[] = []; const bodyAssignments = [`SessionId = _sessionId`]; @@ -1238,7 +1239,7 @@ function emitSessionApiClass(className: string, node: Record, c const groupExperimental = isNodeFullyExperimental(node); const groupDeprecated = isNodeFullyDeprecated(node); const experimentalAttr = groupExperimental ? `[Experimental(Diagnostics.Experimental)]\n` : ""; - const deprecatedAttr = groupDeprecated ? `[Obsolete]\n` : ""; + const deprecatedAttr = groupDeprecated ? `[Obsolete("This member is deprecated and will be removed in a future version.")]\n` : ""; const subGroups = Object.entries(node).filter(([, v]) => typeof v === "object" && v !== null && !isRpcMethod(v)); const lines = [`/// Provides session-scoped ${displayName} APIs.`, `${experimentalAttr}${deprecatedAttr}public sealed class ${className}`, `{`, ` private readonly JsonRpc _rpc;`, ` private readonly string _sessionId;`, ""]; @@ -1328,7 +1329,7 @@ function emitClientSessionApiRegistration(clientSchema: Record, lines.push(`[Experimental(Diagnostics.Experimental)]`); } if (groupDeprecated) { - lines.push(`[Obsolete]`); + lines.push(`[Obsolete("This member is deprecated and will be removed in a future version.")]`); } lines.push(`public interface ${interfaceName}`); lines.push(`{`); @@ -1342,7 +1343,7 @@ function emitClientSessionApiRegistration(clientSchema: Record, lines.push(` [Experimental(Diagnostics.Experimental)]`); } if (method.deprecated && !groupDeprecated) { - lines.push(` [Obsolete]`); + lines.push(` [Obsolete("This member is deprecated and will be removed in a future version.")]`); } if (hasParams) { lines.push(` ${taskType} ${clientHandlerMethodName(method.rpcMethod)}(${paramsTypeName(method)} request, CancellationToken cancellationToken = default);`); @@ -1481,7 +1482,7 @@ internal static class Diagnostics export async function generateRpc(schemaPath?: string): Promise { console.log("C#: generating RPC types..."); const resolvedPath = schemaPath ?? (await getApiSchemaPath()); - const schema = cloneSchemaForCodegen(JSON.parse(await fs.readFile(resolvedPath, "utf-8")) as ApiSchema); + const schema = fixNullableRequiredRefsInApiSchema(cloneSchemaForCodegen(JSON.parse(await fs.readFile(resolvedPath, "utf-8")) as ApiSchema)); const code = generateRpcCode(schema); const outPath = await writeGeneratedFile("dotnet/src/Generated/Rpc.cs", code); console.log(` ✓ ${outPath}`); diff --git a/scripts/codegen/go.ts b/scripts/codegen/go.ts index fa21aa703..8f9d40321 100644 --- a/scripts/codegen/go.ts +++ b/scripts/codegen/go.ts @@ -13,6 +13,7 @@ import { FetchingJSONSchemaStore, InputData, JSONSchemaInput, quicktype } from " import { promisify } from "util"; import { cloneSchemaForCodegen, + fixNullableRequiredRefsInApiSchema, getApiSchemaPath, getRpcSchemaTypeName, getSessionEventsSchemaPath, @@ -968,7 +969,7 @@ async function generateRpc(schemaPath?: string): Promise { console.log("Go: generating RPC types..."); const resolvedPath = schemaPath ?? (await getApiSchemaPath()); - const schema = cloneSchemaForCodegen(JSON.parse(await fs.readFile(resolvedPath, "utf-8")) as ApiSchema); + const schema = fixNullableRequiredRefsInApiSchema(cloneSchemaForCodegen(JSON.parse(await fs.readFile(resolvedPath, "utf-8")) as ApiSchema)); const allMethods = [ ...collectRpcMethods(schema.server || {}), diff --git a/scripts/codegen/python.ts b/scripts/codegen/python.ts index bb1f56e0d..175c5175b 100644 --- a/scripts/codegen/python.ts +++ b/scripts/codegen/python.ts @@ -12,6 +12,7 @@ import type { JSONSchema7 } from "json-schema"; import { fileURLToPath } from "url"; import { cloneSchemaForCodegen, + fixNullableRequiredRefsInApiSchema, getApiSchemaPath, getRpcSchemaTypeName, getSessionEventsSchemaPath, @@ -184,6 +185,157 @@ function collapsePlaceholderPythonDataclasses(code: string): string { return code.replace(/\n{3,}/g, "\n\n"); } +/** + * Reorder Python class/enum definitions so forward references are resolved. + * Quicktype may emit classes in an order where a class references another + * that hasn't been defined yet, causing NameError at import time. + * This performs a topological sort of type definitions while preserving + * the relative position of non-class blocks (functions, standalone code). + */ +function reorderPythonForwardRefs(code: string): string { + // Split code into top-level blocks. Each block starts at an unindented + // line that begins a class, decorated class, enum, or function definition. + const lines = code.split("\n"); + + interface Block { + name: string; + code: string; + isType: boolean; // true for class/enum definitions + } + + const blocks: Block[] = []; + let currentLines: string[] = []; + let currentName: string | null = null; + let isType = false; + + function flushBlock() { + if (currentLines.length === 0) return; + const blockCode = currentLines.join("\n"); + blocks.push({ + name: currentName ?? `__anon_${blocks.length}`, + code: blockCode, + isType, + }); + currentLines = []; + currentName = null; + isType = false; + } + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + const isTopLevel = line.length > 0 && line[0] !== " " && line[0] !== "\t"; + + if (isTopLevel) { + const classMatch = line.match(/^class\s+(\w+)/); + const defMatch = line.match(/^def\s+(\w+)/); + const decoratorMatch = line === "@dataclass"; + const commentMatch = line.startsWith("# "); + + if (classMatch) { + // If previous block was just a decorator waiting for a class, merge + if (currentLines.length > 0 && currentName === null && isType) { + // This is the class line following @dataclass + currentName = classMatch[1]; + currentLines.push(line); + continue; + } + flushBlock(); + currentLines = [line]; + currentName = classMatch[1]; + isType = true; + } else if (decoratorMatch) { + flushBlock(); + currentLines = [line]; + isType = true; + } else if (defMatch) { + flushBlock(); + currentLines = [line]; + currentName = defMatch[1]; + isType = false; + } else if (commentMatch && currentLines.length === 0) { + // Standalone comment — attach to next block + currentLines = [line]; + } else { + currentLines.push(line); + } + } else { + currentLines.push(line); + } + } + flushBlock(); + + if (blocks.length === 0) return code; + + // Collect all type names (classes and enums) + const typeNames = new Set(blocks.filter((b) => b.isType).map((b) => b.name)); + if (typeNames.size === 0) return code; + + // Build dependency graph: for each type block, find references to other type names + const deps = new Map>(); + for (const block of blocks) { + if (!block.isType) continue; + const blockDeps = new Set(); + for (const tn of typeNames) { + if (tn === block.name) continue; + if (new RegExp(`\\b${tn}\\b`).test(block.code)) { + blockDeps.add(tn); + } + } + deps.set(block.name, blockDeps); + } + + // Kahn's algorithm for topological sort + const inDegree = new Map(); + for (const tn of typeNames) inDegree.set(tn, deps.get(tn)?.size ?? 0); + + const dependents = new Map(); + for (const tn of typeNames) dependents.set(tn, []); + for (const [name, d] of deps) { + for (const dep of d) { + dependents.get(dep)!.push(name); + } + } + + const queue: string[] = []; + for (const [tn, deg] of inDegree) { + if (deg === 0) queue.push(tn); + } + + const sorted: string[] = []; + while (queue.length > 0) { + const node = queue.shift()!; + sorted.push(node); + for (const dep of dependents.get(node) ?? []) { + const newDeg = inDegree.get(dep)! - 1; + inDegree.set(dep, newDeg); + if (newDeg === 0) queue.push(dep); + } + } + + // If there are cycles, keep remaining nodes in original order + for (const block of blocks) { + if (block.isType && !sorted.includes(block.name)) { + sorted.push(block.name); + } + } + + // Rebuild: place type blocks in sorted order at the positions + // where type blocks originally appeared + const typeBlockMap = new Map(blocks.filter((b) => b.isType).map((b) => [b.name, b])); + let sortIdx = 0; + const result: string[] = []; + for (const block of blocks) { + if (block.isType) { + result.push(typeBlockMap.get(sorted[sortIdx])!.code); + sortIdx++; + } else { + result.push(block.code); + } + } + + return result.join("\n"); +} + function normalizePythonDataclassBlock(block: string, name: string): string { return block .replace(/^@dataclass\r?\nclass\s+\w+:/, "@dataclass\nclass:") @@ -1395,7 +1547,7 @@ async function generateRpc(schemaPath?: string): Promise { const { FetchingJSONSchemaStore, InputData, JSONSchemaInput, quicktype } = await import("quicktype-core"); const resolvedPath = schemaPath ?? (await getApiSchemaPath()); - const schema = cloneSchemaForCodegen(JSON.parse(await fs.readFile(resolvedPath, "utf-8")) as ApiSchema); + const schema = fixNullableRequiredRefsInApiSchema(cloneSchemaForCodegen(JSON.parse(await fs.readFile(resolvedPath, "utf-8")) as ApiSchema)); const allMethods = [ ...collectRpcMethods(schema.server || {}), @@ -1482,6 +1634,10 @@ async function generateRpc(schemaPath?: string): Promise { typesCode = modernizePython(typesCode); typesCode = collapsePlaceholderPythonDataclasses(typesCode); + // Reorder class/enum definitions to resolve forward references. + // Quicktype may emit classes before their dependencies are defined. + typesCode = reorderPythonForwardRefs(typesCode); + // Strip quicktype's import block and preamble — we provide our own unified header. // The preamble ends just before the first helper function (e.g. "def from_str") // or class definition. diff --git a/scripts/codegen/typescript.ts b/scripts/codegen/typescript.ts index 8cc3e4078..1aba7384c 100644 --- a/scripts/codegen/typescript.ts +++ b/scripts/codegen/typescript.ts @@ -11,6 +11,7 @@ import type { JSONSchema7 } from "json-schema"; import { compile } from "json-schema-to-typescript"; import { getApiSchemaPath, + fixNullableRequiredRefsInApiSchema, getRpcSchemaTypeName, getSessionEventsSchemaPath, normalizeSchemaTitles, @@ -320,7 +321,7 @@ async function generateRpc(schemaPath?: string): Promise { console.log("TypeScript: generating RPC types..."); const resolvedPath = schemaPath ?? (await getApiSchemaPath()); - const schema = JSON.parse(await fs.readFile(resolvedPath, "utf-8")) as ApiSchema; + const schema = fixNullableRequiredRefsInApiSchema(JSON.parse(await fs.readFile(resolvedPath, "utf-8")) as ApiSchema); const lines: string[] = []; lines.push(`/** diff --git a/scripts/codegen/utils.ts b/scripts/codegen/utils.ts index d6083adec..bc144bf75 100644 --- a/scripts/codegen/utils.ts +++ b/scripts/codegen/utils.ts @@ -128,6 +128,78 @@ export function postProcessSchema(schema: JSONSchema7): JSONSchema7 { return processed; } +/** + * Normalize schema defects where a required property with a `$ref` to an object type + * has a description explicitly mentioning "null" as a valid value. + * + * In JSON Schema, `required` only means the key must be present — it doesn't prevent + * the value from being null. Some schemas mark properties as required but describe them + * as nullable (e.g., "Currently selected agent, or null if using the default"). + * + * This function converts such properties from: + * `{ "$ref": "#/definitions/Foo", "description": "...null..." }` + * to: + * `{ "anyOf": [{ "$ref": "#/definitions/Foo" }, { "type": "null" }], "description": "...null..." }` + * + * This makes all downstream codegen (Go, C#, Python/quicktype, TypeScript) correctly + * emit nullable/optional types without per-language heuristics. + */ +export function normalizeNullableRequiredRefs(schema: JSONSchema7): JSONSchema7 { + if (typeof schema !== "object" || schema === null) return schema; + + const processed = { ...schema }; + + if (processed.properties && processed.required) { + const requiredSet = new Set(processed.required); + const newProps: Record = {}; + const newRequired = [...processed.required]; + + for (const [key, value] of Object.entries(processed.properties)) { + if (typeof value !== "object" || value === null) { + newProps[key] = value; + continue; + } + const prop = value as JSONSchema7; + if ( + requiredSet.has(key) && + prop.$ref && + typeof prop.description === "string" && + /\bnull\b/i.test(prop.description) + ) { + // Convert to anyOf: [$ref, null] and remove from required + const { $ref, ...rest } = prop; + newProps[key] = { + ...rest, + anyOf: [{ $ref }, { type: "null" as const }], + }; + const idx = newRequired.indexOf(key); + if (idx !== -1) newRequired.splice(idx, 1); + } else { + newProps[key] = normalizeNullableRequiredRefs(prop); + } + } + + processed.properties = newProps; + processed.required = newRequired; + } + + // Recurse into nested schemas + if (processed.items) { + if (typeof processed.items === "object" && !Array.isArray(processed.items)) { + processed.items = normalizeNullableRequiredRefs(processed.items as JSONSchema7); + } + } + for (const combiner of ["anyOf", "allOf", "oneOf"] as const) { + if (processed[combiner]) { + processed[combiner] = processed[combiner]!.map((item) => + typeof item === "object" ? normalizeNullableRequiredRefs(item as JSONSchema7) : item + ) as JSONSchema7Definition[]; + } + } + + return processed; +} + // ── File output ───────────────────────────────────────────────────────────── export async function writeGeneratedFile(relativePath: string, content: string): Promise { @@ -452,6 +524,52 @@ export function normalizeApiSchema(schema: ApiSchema): ApiSchema { }; } +/** + * Apply `normalizeNullableRequiredRefs` to every JSON Schema reachable from the API schema + * (method params, results, and shared definitions). Call after `cloneSchemaForCodegen` to + * fix schema defects before any per-language codegen runs. + */ +export function fixNullableRequiredRefsInApiSchema(schema: ApiSchema): ApiSchema { + function walkApiNode(node: Record | undefined): Record | undefined { + if (!node) return undefined; + const result: Record = {}; + for (const [key, value] of Object.entries(node)) { + if (isRpcMethod(value)) { + const method = value as RpcMethod; + result[key] = { + ...method, + params: method.params ? normalizeNullableRequiredRefs(method.params) : method.params, + result: method.result ? normalizeNullableRequiredRefs(method.result) : method.result, + }; + } else if (typeof value === "object" && value !== null) { + result[key] = walkApiNode(value as Record); + } else { + result[key] = value; + } + } + return result; + } + + function normalizeDefs(defs: Record | undefined): Record | undefined { + if (!defs) return undefined; + return Object.fromEntries( + Object.entries(defs).map(([key, value]) => [ + key, + typeof value === "object" && value !== null ? normalizeNullableRequiredRefs(value as JSONSchema7) : value, + ]) + ); + } + + return { + ...schema, + definitions: normalizeDefs(schema.definitions), + $defs: normalizeDefs(schema.$defs), + server: walkApiNode(schema.server), + session: walkApiNode(schema.session), + clientSession: walkApiNode(schema.clientSession), + }; +} + /** Returns true when every leaf RPC method inside `node` is marked experimental. */ export function isNodeFullyExperimental(node: Record): boolean { const methods: RpcMethod[] = []; diff --git a/test/harness/package-lock.json b/test/harness/package-lock.json index 2c82d7b87..dad61dfd3 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.30", + "@github/copilot": "^1.0.32-1", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "openai": "^6.17.0", @@ -462,27 +462,27 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.30", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.30.tgz", - "integrity": "sha512-JYZNMM6hteAE6tIMbHobRjpAaXzvqeeglXgGlDCr26rRq3K6h5ul2GN27qzhMBaWyujUQN402KLKdrhDPqcL7A==", + "version": "1.0.32-1", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.32-1.tgz", + "integrity": "sha512-uJgZWkd+gYS6t8NeWgZd+KDlQ41RFvAydOPdJqMDdB8aBwJYKQA75AVQzJyIne/CaMmv2Cy24X+IeRsMXvg+YA==", "dev": true, "license": "SEE LICENSE IN LICENSE.md", "bin": { "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.30", - "@github/copilot-darwin-x64": "1.0.30", - "@github/copilot-linux-arm64": "1.0.30", - "@github/copilot-linux-x64": "1.0.30", - "@github/copilot-win32-arm64": "1.0.30", - "@github/copilot-win32-x64": "1.0.30" + "@github/copilot-darwin-arm64": "1.0.32-1", + "@github/copilot-darwin-x64": "1.0.32-1", + "@github/copilot-linux-arm64": "1.0.32-1", + "@github/copilot-linux-x64": "1.0.32-1", + "@github/copilot-win32-arm64": "1.0.32-1", + "@github/copilot-win32-x64": "1.0.32-1" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.30", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.30.tgz", - "integrity": "sha512-qhLMhAY7nskG6yabbsWSqErxPWcZLX1ixJBdQX3RLqgw5dyNvZRNzG2evUnABo5bqgndztsFXjE3u4XtfX0WkA==", + "version": "1.0.32-1", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.32-1.tgz", + "integrity": "sha512-MGz9kKJYqrfZ94DOVsKy8c0sTFn1Gax60hM3TjMt6K+Tt7n8vGhrpBn+KjFYOb+6+r7fp3E7fc6tTtwjgaURVw==", "cpu": [ "arm64" ], @@ -497,9 +497,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.30", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.30.tgz", - "integrity": "sha512-nsjGRt1jLBzCaVd6eb3ok75zqePr8eU8GSTqu1KVf5KUrnvvfIlsvESkEAE8l+lkR14f7SGQLfMJ2EEbcJMGcg==", + "version": "1.0.32-1", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.32-1.tgz", + "integrity": "sha512-HSLJXMVk2yf6Xb6NhNxEYvD57hBGdWs5zQ7EOHrFYO+qA5/iD4JVGgQNg7sS88+qsTR5PtEcxwbtQPid1KZJnQ==", "cpu": [ "x64" ], @@ -514,9 +514,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.30", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.30.tgz", - "integrity": "sha512-7wOrOKm9MHnglyzzGeZnXSkfRi4sXB2Db7rK/CgUenxS+dwwIuXhT4rgkH/DIOiDbGCxYjigICxln28Jvbs+cA==", + "version": "1.0.32-1", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.32-1.tgz", + "integrity": "sha512-XBiX4947+ygPugwsZrrVOwftIWWASoknq1FzehIpj7BqPxjwTpzDXPDJNleHf+6a1cGm8cUutDn/wslHjJEW9A==", "cpu": [ "arm64" ], @@ -531,9 +531,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.30", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.30.tgz", - "integrity": "sha512-OSJtP7mV9vnDzGFjBkI3sgbNOcxsRcq7vXrT4PNrjJw4Mc71aaW55hc5F1j2fElfGWIb+Jubm3AB8nb6AoufnA==", + "version": "1.0.32-1", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.32-1.tgz", + "integrity": "sha512-iJkcWKSoaDY5GKtOZtoZV5YhuOqvVSdENashNKjXzkIoFN0mqonIhsbAv3OB2Kr34ZwoQF3CfNoOCNBs2tg8pg==", "cpu": [ "x64" ], @@ -548,9 +548,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.30", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.30.tgz", - "integrity": "sha512-5nCz/+9VWJdNvW2uRYeMmnRdQq/gpuSlmYMvRv8fIsFF8KH0mdJndJn8xN6GeJtx0fKJrLzgKqJHWdgb5MtLgA==", + "version": "1.0.32-1", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.32-1.tgz", + "integrity": "sha512-U/lfmWAqOIxucqotmsOsJtOjfAhNIYAFeqxyaKo+V35YkurXZGTNjB2YxqUlmKm/7fuOgAACHKvrK+tWs+Mlvg==", "cpu": [ "arm64" ], @@ -565,9 +565,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.30", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.30.tgz", - "integrity": "sha512-tJvgCsWLJVQvHLvFyQZ0P5MQ7YGX51/bl9kbXDUFCGATtPpELul3NyHWwEYGjRv+VDPvhFxjbf+V7Bf/VzYZ7w==", + "version": "1.0.32-1", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.32-1.tgz", + "integrity": "sha512-oSNG9nRHsyTdi2miBfti4egT+CHPGu0QTXXUasISsfwhex6SS4qeVFe8mt8/clnTlyJD9N7EDgABDduSYQv87g==", "cpu": [ "x64" ], diff --git a/test/harness/package.json b/test/harness/package.json index 94fe9d8c5..37bb8031a 100644 --- a/test/harness/package.json +++ b/test/harness/package.json @@ -11,7 +11,7 @@ "test": "vitest run" }, "devDependencies": { - "@github/copilot": "^1.0.30", + "@github/copilot": "^1.0.32-1", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "openai": "^6.17.0",