diff --git a/docs/features/mcp.md b/docs/features/mcp.md index 6f715bd2ed..0159f8346b 100644 --- a/docs/features/mcp.md +++ b/docs/features/mcp.md @@ -262,6 +262,51 @@ directories for different applications. | `tools` | `string[]` | No | Tools to enable | | `timeout` | `number` | No | Timeout in milliseconds | +## MCP diagnostics + +The Node.js SDK can capture verbose MCP transport diagnostics for troubleshooting. +Set `onMcpDiagnostic` when you create or resume a session. The SDK then registers +interest in the ephemeral `mcp.diagnostic` event before returning the session. +Subscribing with `session.on()` alone does not register interest, so it does not +enable diagnostic events. + +```typescript +const session = await client.createSession({ + mcpServers: { + filesystem: { + type: "local", + command: "npx", + args: ["-y", "@modelcontextprotocol/server-filesystem", "."], + tools: ["*"], + }, + }, + onMcpDiagnostic: (diagnostic, { sessionId }) => { + switch (diagnostic.detail.kind) { + case "wire_message": + console.log(sessionId, diagnostic.detail.direction, diagnostic.detail.method); + if (diagnostic.detail.truncated) { + console.log("The captured wire payload was clipped."); + } + break; + case "http_exchange": + console.log(sessionId, diagnostic.detail.phase, diagnostic.detail.statusCode); + break; + case "server_log": + console.error(sessionId, diagnostic.detail.line); + break; + case "process_lifecycle": + console.log(sessionId, diagnostic.detail.command, diagnostic.detail.exitCode); + break; + } + }, +}); +``` + +Diagnostics are high-volume and are not persisted or replayed with the session. +The runtime redacts sensitive values and size-caps captured payloads. Check +`detail.truncated` on `wire_message` diagnostics before relying on a captured payload +being complete. + ## Troubleshooting ### Tools not showing up or not being invoked diff --git a/dotnet/src/Generated/Rpc.cs b/dotnet/src/Generated/Rpc.cs index d09976bc80..5c1219c327 100644 --- a/dotnet/src/Generated/Rpc.cs +++ b/dotnet/src/Generated/Rpc.cs @@ -24577,6 +24577,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(GitHub.Copilot.AttachmentSelectionDetails), TypeInfoPropertyName = "SessionEventsAttachmentSelectionDetails")] [JsonSerializable(typeof(GitHub.Copilot.AttachmentSelectionDetailsEnd), TypeInfoPropertyName = "SessionEventsAttachmentSelectionDetailsEnd")] [JsonSerializable(typeof(GitHub.Copilot.AttachmentSelectionDetailsStart), TypeInfoPropertyName = "SessionEventsAttachmentSelectionDetailsStart")] +[JsonSerializable(typeof(GitHub.Copilot.AutoApprovalJudgeFailureReason), TypeInfoPropertyName = "SessionEventsAutoApprovalJudgeFailureReason")] [JsonSerializable(typeof(GitHub.Copilot.AutoApprovalRecommendation), TypeInfoPropertyName = "SessionEventsAutoApprovalRecommendation")] [JsonSerializable(typeof(GitHub.Copilot.AutoModeResolvedReasoningBucket), TypeInfoPropertyName = "SessionEventsAutoModeResolvedReasoningBucket")] [JsonSerializable(typeof(GitHub.Copilot.AutoModeSwitchCompletedData), TypeInfoPropertyName = "SessionEventsAutoModeSwitchCompletedData")] @@ -24615,6 +24616,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(GitHub.Copilot.CommandsChangedEvent), TypeInfoPropertyName = "SessionEventsCommandsChangedEvent")] [JsonSerializable(typeof(GitHub.Copilot.CompactionCompleteCompactionTokensUsed), TypeInfoPropertyName = "SessionEventsCompactionCompleteCompactionTokensUsed")] [JsonSerializable(typeof(GitHub.Copilot.CompactionCompleteCompactionTokensUsedCopilotUsageTokenDetail), TypeInfoPropertyName = "SessionEventsCompactionCompleteCompactionTokensUsedCopilotUsageTokenDetail")] +[JsonSerializable(typeof(GitHub.Copilot.CompactionTrigger), TypeInfoPropertyName = "SessionEventsCompactionTrigger")] [JsonSerializable(typeof(GitHub.Copilot.ContextTier), TypeInfoPropertyName = "SessionEventsContextTier")] [JsonSerializable(typeof(GitHub.Copilot.CustomAgentsUpdatedAgent), TypeInfoPropertyName = "SessionEventsCustomAgentsUpdatedAgent")] [JsonSerializable(typeof(GitHub.Copilot.ElicitationCompletedAction), TypeInfoPropertyName = "SessionEventsElicitationCompletedAction")] @@ -24638,6 +24640,8 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(GitHub.Copilot.ExternalToolCompletedEvent), TypeInfoPropertyName = "SessionEventsExternalToolCompletedEvent")] [JsonSerializable(typeof(GitHub.Copilot.ExternalToolRequestedData), TypeInfoPropertyName = "SessionEventsExternalToolRequestedData")] [JsonSerializable(typeof(GitHub.Copilot.ExternalToolRequestedEvent), TypeInfoPropertyName = "SessionEventsExternalToolRequestedEvent")] +[JsonSerializable(typeof(GitHub.Copilot.FactoryRunUpdatedData), TypeInfoPropertyName = "SessionEventsFactoryRunUpdatedData")] +[JsonSerializable(typeof(GitHub.Copilot.FactoryRunUpdatedEvent), TypeInfoPropertyName = "SessionEventsFactoryRunUpdatedEvent")] [JsonSerializable(typeof(GitHub.Copilot.GitHubRepoRef), TypeInfoPropertyName = "SessionEventsGitHubRepoRef")] [JsonSerializable(typeof(GitHub.Copilot.HandoffRepository), TypeInfoPropertyName = "SessionEventsHandoffRepository")] [JsonSerializable(typeof(GitHub.Copilot.HandoffSourceType), TypeInfoPropertyName = "SessionEventsHandoffSourceType")] @@ -24657,6 +24661,13 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(GitHub.Copilot.McpAppToolCallCompleteEvent), TypeInfoPropertyName = "SessionEventsMcpAppToolCallCompleteEvent")] [JsonSerializable(typeof(GitHub.Copilot.McpAppToolCallCompleteToolMeta), TypeInfoPropertyName = "SessionEventsMcpAppToolCallCompleteToolMeta")] [JsonSerializable(typeof(GitHub.Copilot.McpAppToolCallCompleteToolMetaUI), TypeInfoPropertyName = "SessionEventsMcpAppToolCallCompleteToolMetaUI")] +[JsonSerializable(typeof(GitHub.Copilot.McpDiagnosticData), TypeInfoPropertyName = "SessionEventsMcpDiagnosticData")] +[JsonSerializable(typeof(GitHub.Copilot.McpDiagnosticDetail), TypeInfoPropertyName = "SessionEventsMcpDiagnosticDetail")] +[JsonSerializable(typeof(GitHub.Copilot.McpDiagnosticEvent), TypeInfoPropertyName = "SessionEventsMcpDiagnosticEvent")] +[JsonSerializable(typeof(GitHub.Copilot.McpDiagnosticHttpExchangePhase), TypeInfoPropertyName = "SessionEventsMcpDiagnosticHttpExchangePhase")] +[JsonSerializable(typeof(GitHub.Copilot.McpDiagnosticServerLogStream), TypeInfoPropertyName = "SessionEventsMcpDiagnosticServerLogStream")] +[JsonSerializable(typeof(GitHub.Copilot.McpDiagnosticWireMessageDirection), TypeInfoPropertyName = "SessionEventsMcpDiagnosticWireMessageDirection")] +[JsonSerializable(typeof(GitHub.Copilot.McpDiagnosticWireMessageKind), TypeInfoPropertyName = "SessionEventsMcpDiagnosticWireMessageKind")] [JsonSerializable(typeof(GitHub.Copilot.McpHeadersRefreshCompletedData), TypeInfoPropertyName = "SessionEventsMcpHeadersRefreshCompletedData")] [JsonSerializable(typeof(GitHub.Copilot.McpHeadersRefreshCompletedEvent), TypeInfoPropertyName = "SessionEventsMcpHeadersRefreshCompletedEvent")] [JsonSerializable(typeof(GitHub.Copilot.McpHeadersRefreshCompletedOutcome), TypeInfoPropertyName = "SessionEventsMcpHeadersRefreshCompletedOutcome")] @@ -24722,6 +24733,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(GitHub.Copilot.PermissionRequestRead), TypeInfoPropertyName = "SessionEventsPermissionRequestRead")] [JsonSerializable(typeof(GitHub.Copilot.PermissionRequestShell), TypeInfoPropertyName = "SessionEventsPermissionRequestShell")] [JsonSerializable(typeof(GitHub.Copilot.PermissionRequestShellCommand), TypeInfoPropertyName = "SessionEventsPermissionRequestShellCommand")] +[JsonSerializable(typeof(GitHub.Copilot.PermissionRequestShellCommandSegment), TypeInfoPropertyName = "SessionEventsPermissionRequestShellCommandSegment")] [JsonSerializable(typeof(GitHub.Copilot.PermissionRequestShellPossibleUrl), TypeInfoPropertyName = "SessionEventsPermissionRequestShellPossibleUrl")] [JsonSerializable(typeof(GitHub.Copilot.PermissionRequestUrl), TypeInfoPropertyName = "SessionEventsPermissionRequestUrl")] [JsonSerializable(typeof(GitHub.Copilot.PermissionRequestWrite), TypeInfoPropertyName = "SessionEventsPermissionRequestWrite")] @@ -24738,6 +24750,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(GitHub.Copilot.SamplingCompletedEvent), TypeInfoPropertyName = "SessionEventsSamplingCompletedEvent")] [JsonSerializable(typeof(GitHub.Copilot.SamplingRequestedData), TypeInfoPropertyName = "SessionEventsSamplingRequestedData")] [JsonSerializable(typeof(GitHub.Copilot.SamplingRequestedEvent), TypeInfoPropertyName = "SessionEventsSamplingRequestedEvent")] +[JsonSerializable(typeof(GitHub.Copilot.ScheduleOrigin), TypeInfoPropertyName = "SessionEventsScheduleOrigin")] [JsonSerializable(typeof(GitHub.Copilot.SessionEvent), TypeInfoPropertyName = "SessionEventsSessionEvent")] [JsonSerializable(typeof(GitHub.Copilot.SessionLimitsConfig), TypeInfoPropertyName = "SessionEventsSessionLimitsConfig")] [JsonSerializable(typeof(GitHub.Copilot.SessionLimitsExhaustedCompletedData), TypeInfoPropertyName = "SessionEventsSessionLimitsExhaustedCompletedData")] @@ -24783,6 +24796,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(GitHub.Copilot.SystemNotificationNewInboxMessage), TypeInfoPropertyName = "SessionEventsSystemNotificationNewInboxMessage")] [JsonSerializable(typeof(GitHub.Copilot.SystemNotificationShellCompleted), TypeInfoPropertyName = "SessionEventsSystemNotificationShellCompleted")] [JsonSerializable(typeof(GitHub.Copilot.SystemNotificationShellDetachedCompleted), TypeInfoPropertyName = "SessionEventsSystemNotificationShellDetachedCompleted")] +[JsonSerializable(typeof(GitHub.Copilot.SystemNotificationUnclassified), TypeInfoPropertyName = "SessionEventsSystemNotificationUnclassified")] [JsonSerializable(typeof(GitHub.Copilot.ToolExecutionCompleteContent), TypeInfoPropertyName = "SessionEventsToolExecutionCompleteContent")] [JsonSerializable(typeof(GitHub.Copilot.ToolExecutionCompleteContentAudio), TypeInfoPropertyName = "SessionEventsToolExecutionCompleteContentAudio")] [JsonSerializable(typeof(GitHub.Copilot.ToolExecutionCompleteContentImage), TypeInfoPropertyName = "SessionEventsToolExecutionCompleteContentImage")] diff --git a/dotnet/src/Generated/SessionEvents.cs b/dotnet/src/Generated/SessionEvents.cs index a8c70df7cc..f833cdd103 100644 --- a/dotnet/src/Generated/SessionEvents.cs +++ b/dotnet/src/Generated/SessionEvents.cs @@ -52,10 +52,12 @@ namespace GitHub.Copilot; [JsonDerivedType(typeof(ExitPlanModeRequestedEvent), "exit_plan_mode.requested")] [JsonDerivedType(typeof(ExternalToolCompletedEvent), "external_tool.completed")] [JsonDerivedType(typeof(ExternalToolRequestedEvent), "external_tool.requested")] +[JsonDerivedType(typeof(FactoryRunUpdatedEvent), "factory.run_updated")] [JsonDerivedType(typeof(HookEndEvent), "hook.end")] [JsonDerivedType(typeof(HookProgressEvent), "hook.progress")] [JsonDerivedType(typeof(HookStartEvent), "hook.start")] [JsonDerivedType(typeof(McpAppToolCallCompleteEvent), "mcp_app.tool_call_complete")] +[JsonDerivedType(typeof(McpDiagnosticEvent), "mcp.diagnostic")] [JsonDerivedType(typeof(McpHeadersRefreshCompletedEvent), "mcp.headers_refresh_completed")] [JsonDerivedType(typeof(McpHeadersRefreshRequiredEvent), "mcp.headers_refresh_required")] [JsonDerivedType(typeof(McpOauthCompletedEvent), "mcp.oauth_completed")] @@ -1194,6 +1196,20 @@ public sealed partial class McpHeadersRefreshCompletedEvent : SessionEvent public required McpHeadersRefreshCompletedData Data { get; set; } } +/// Verbose MCP diagnostic data. Payloads are redacted and size-capped; `truncated` indicates that a captured payload was clipped. This event is ephemeral and high-volume, and is emitted only when a consumer has registered interest in `mcp.diagnostic` via `session.eventLog.registerInterest`. +/// Represents the mcp.diagnostic event. +[Experimental(Diagnostics.Experimental)] +public sealed partial class McpDiagnosticEvent : SessionEvent +{ + /// + [JsonIgnore] + public override string Type => "mcp.diagnostic"; + + /// The mcp.diagnostic event payload. + [JsonPropertyName("data")] + public required McpDiagnosticData Data { get; set; } +} + /// Opaque custom notification data. Consumers may branch on source and name, but payload semantics are source-defined. /// Represents the session.custom_notification event. public sealed partial class SessionCustomNotificationEvent : SessionEvent @@ -1444,6 +1460,20 @@ public sealed partial class SessionBackgroundTasksChangedEvent : SessionEvent public required SessionBackgroundTasksChangedData Data { get; set; } } +/// Ephemeral invalidation signal for a changed factory run. +/// Represents the factory.run_updated event. +[Experimental(Diagnostics.Experimental)] +public sealed partial class FactoryRunUpdatedEvent : SessionEvent +{ + /// + [JsonIgnore] + public override string Type => "factory.run_updated"; + + /// The factory.run_updated event payload. + [JsonPropertyName("data")] + public required FactoryRunUpdatedData Data { get; set; } +} + /// Payload of `session.skills_loaded` listing resolved skill metadata. /// Represents the session.skills_loaded event. public sealed partial class SessionSkillsLoadedEvent : SessionEvent @@ -1750,7 +1780,7 @@ public sealed partial class SessionResumeData [JsonPropertyName("contextTier")] public ContextTier? ContextTier { get; set; } - /// When true, tool calls and permission requests left in flight by the previous session lifetime remain pending after resume and the agentic loop awaits their results. User sends are queued behind the pending work until all such requests reach a terminal state. When false (the default), any such tool calls and permission requests are immediately marked as interrupted on resume. + /// When true, tool calls and permission requests left in flight by the previous session lifetime remain pending after resume and the agentic loop awaits their results. User sends are queued behind the pending work until all such requests reach a terminal state. When false or omitted, pending work is normally marked as interrupted unless the resume passively joined live work owned by another client; sessionWasActive distinguishes that case. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("continuePendingWork")] public bool? ContinuePendingWork { get; set; } @@ -1793,7 +1823,7 @@ public sealed partial class SessionResumeData [JsonPropertyName("sessionLimits")] public SessionLimitsConfig? SessionLimits { get; set; } - /// True when this resume attached to a session that the runtime already had running in-memory (for example, an extension joining a session another client was actively driving). False (or omitted) for cold resumes — the runtime had to reconstitute the session from its persisted event log. + /// True when this resume passively joined a session that already had live work running in the runtime - an agent turn, a native queue run, a queued resume continuation, or an in-flight send (for example, an extension joining a session another client was actively driving). False (or omitted) when the session had no live work or when the resume explicitly abandoned pending work, including cold resumes and suspended sessions that remain resident in memory. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("sessionWasActive")] public bool? SessionWasActive { get; set; } @@ -1904,6 +1934,11 @@ public sealed partial class SessionScheduleCreatedData [JsonPropertyName("intervalMs")] public TimeSpan? Interval { get; set; } + /// Who created the schedule (`user` or `model`). Persisted so a resumed session keeps gating non-user schedules from firing skills that opted out of model invocation. Absent on entries created before this field existed; a missing origin fails closed (treated the same as a non-user origin), so such a schedule may not resolve a `disable-model-invocation` skill. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("origin")] + public ScheduleOrigin? Origin { get; set; } + /// Prompt text that gets enqueued on every tick. [JsonPropertyName("prompt")] public required string Prompt { get; set; } @@ -2392,6 +2427,11 @@ public sealed partial class SessionCompactionStartData [JsonPropertyName("conversationTokens")] public long? ConversationTokens { get; set; } + /// Total context tokens (system + conversation + tool definitions) at compaction start, when known. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("currentTokens")] + public long? CurrentTokens { get; set; } + /// Model identifier used for compaction, when known. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("model")] @@ -2402,10 +2442,20 @@ public sealed partial class SessionCompactionStartData [JsonPropertyName("systemTokens")] public long? SystemTokens { get; set; } + /// Model context window token limit the compaction is targeting, when known. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("tokenLimit")] + public long? TokenLimit { get; set; } + /// Token count from tool definitions at compaction start. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("toolDefinitionsTokens")] public long? ToolDefinitionsTokens { get; set; } + + /// What initiated this compaction, when known. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("trigger")] + public CompactionTrigger? Trigger { get; set; } } /// Conversation compaction results including success status, metrics, and optional error details. @@ -2490,6 +2540,11 @@ public sealed partial class SessionCompactionCompleteData [JsonPropertyName("systemTokens")] public long? SystemTokens { get; set; } + /// Model context window token limit the compaction was targeting, when known. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("tokenLimit")] + public long? TokenLimit { get; set; } + /// Number of tokens removed during compaction. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("tokensRemoved")] @@ -2499,6 +2554,11 @@ public sealed partial class SessionCompactionCompleteData [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("toolDefinitionsTokens")] public long? ToolDefinitionsTokens { get; set; } + + /// What initiated this compaction, when known. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("trigger")] + public CompactionTrigger? Trigger { get; set; } } /// Task completion notification with summary from the agent. @@ -2557,7 +2617,7 @@ public sealed partial class UserMessageData [JsonPropertyName("parentAgentTaskId")] public string? ParentAgentTaskId { 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). + /// Origin of this message, used for timeline filtering and attribution (e.g., `skill-pdf` for hidden skill injection or `agent-<agent-id>` for an inter-agent prompt). [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("source")] public string? Source { get; set; } @@ -2648,6 +2708,11 @@ public sealed partial class AssistantReasoningData /// Unique identifier for this reasoning block. [JsonPropertyName("reasoningId")] public required string ReasoningId { get; set; } + + /// Gets or sets the rte value. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("rte")] + public bool? Rte { get; set; } } /// Streaming reasoning delta for incremental extended thinking updates. @@ -2773,6 +2838,11 @@ public sealed partial class AssistantMessageData [JsonPropertyName("requestId")] public string? RequestId { get; set; } + /// Gets or sets the rte value. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("rte")] + public bool? Rte { get; set; } + /// Neutral provider-tagged server-side tool-use payload (tool search, advisor) for verbatim round-tripping. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("serverTools")] @@ -2915,6 +2985,11 @@ public sealed partial class AssistantUsageData [JsonPropertyName("inputTokens")] public long? InputTokens { get; set; } + /// Coarse classification of the interaction that produced this call, mirroring the session's per-request agent context (e.g. `conversation-agent`, `conversation-subagent`, `conversation-sampling`, `conversation-background`, `conversation-compaction`, `conversation-user`). Non-billing; lets consumers attribute a model call to a call class (e.g. sub-agent/sidekick) independently of the billing initiator. Absent when the runtime did not classify the request. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("interactionType")] + public string? InteractionType { get; set; } + /// Average inter-token latency in milliseconds. Only available for streaming requests. [JsonConverter(typeof(MillisecondsTimeSpanConverter))] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] @@ -2960,6 +3035,11 @@ public sealed partial class AssistantUsageData [JsonPropertyName("reasoningTokens")] public long? ReasoningTokens { get; set; } + /// Gets or sets the rte value. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("rte")] + public bool? Rte { get; set; } + /// Copilot service request ID (x-copilot-service-request-id header) for CAPI log correlation. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("serviceRequestId")] @@ -3067,6 +3147,11 @@ public sealed partial class ModelCallFailureData [JsonPropertyName("requestFingerprint")] public ModelCallFailureRequestFingerprint? RequestFingerprint { get; set; } + /// Gets or sets the rte value. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("rte")] + public bool? Rte { get; set; } + /// Copilot service request ID (x-copilot-service-request-id header) for CAPI log correlation. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("serviceRequestId")] @@ -3095,6 +3180,12 @@ public sealed partial class ModelCallStartData [JsonPropertyName("model")] public string? Model { get; set; } + /// Previous response or interaction identifier included in the model request, when present. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonInclude] + [JsonPropertyName("previousResponseId")] + internal string? PreviousResponseId { get; set; } + /// Identifier of the assistant turn that initiated the model call. [JsonPropertyName("turnId")] public required string TurnId { get; set; } @@ -3162,6 +3253,11 @@ public sealed partial class ToolExecutionStartData [JsonPropertyName("parentToolCallId")] public string? ParentToolCallId { get; set; } + /// Gets or sets the rte value. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("rte")] + public bool? Rte { get; set; } + /// Shell-tool path hints derived from the command at start time for shell tools (bash/powershell/local_shell). Produced by the same shell-aware extractor as PermissionRequestShell.possiblePaths, so it is present even when the command is auto-approved and no permission request fires. Absent for non-shell tools. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("shellToolInfo")] @@ -3253,6 +3349,11 @@ public sealed partial class ToolExecutionCompleteData [JsonPropertyName("result")] public ToolExecutionCompleteResult? Result { get; set; } + /// Gets or sets the rte value. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("rte")] + public bool? Rte { get; set; } + /// Whether this tool execution ran inside a sandbox container. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("sandboxed")] @@ -3567,6 +3668,11 @@ public sealed partial class SystemMessageData [JsonPropertyName("content")] public required string Content { get; set; } + /// Logical interaction identifier for the model run receiving this prompt. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("interactionId")] + public string? InteractionId { get; set; } + /// Metadata about the prompt template and its construction. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("metadata")] @@ -3614,6 +3720,11 @@ public sealed partial class PermissionRequestedData [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("resolvedByHook")] public bool? ResolvedByHook { get; set; } + + /// Neutral risk metadata supplied by the tool host. Consumers may display this value but must not use it to bypass the permission decision. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("riskAssessment")] + public JsonElement? RiskAssessment { get; set; } } /// Permission request completion notification signaling UI dismissal. @@ -3841,6 +3952,22 @@ public sealed partial class McpHeadersRefreshCompletedData public required string RequestId { get; set; } } +/// Verbose MCP diagnostic data. Payloads are redacted and size-capped; `truncated` indicates that a captured payload was clipped. This event is ephemeral and high-volume, and is emitted only when a consumer has registered interest in `mcp.diagnostic` via `session.eventLog.registerInterest`. +public sealed partial class McpDiagnosticData +{ + /// Detailed diagnostic payload discriminated by `kind`. + [JsonPropertyName("detail")] + public required McpDiagnosticDetail Detail { get; set; } + + /// Configured name of the MCP server associated with this diagnostic. + [JsonPropertyName("serverName")] + public required string ServerName { get; set; } + + /// Transport mechanism used by the MCP server: stdio, HTTP, Server-Sent Events, or in-memory. + [JsonPropertyName("transport")] + public required McpServerTransport Transport { get; set; } +} + /// Opaque custom notification data. Consumers may branch on source and name, but payload semantics are source-defined. public sealed partial class SessionCustomNotificationData { @@ -4071,6 +4198,11 @@ public sealed partial class SessionManagedSettingsResolvedData [JsonPropertyName("managedKeys")] public required string[] ManagedKeys { get; set; } + /// Whether server and device each supplied a permission allowlist, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("permissionsAllowIntersected")] + public bool? PermissionsAllowIntersected { get; set; } + /// Whether the server (account/org) managed-settings layer was present. [JsonPropertyName("serverManaged")] public required bool ServerManaged { get; set; } @@ -4193,6 +4325,19 @@ public sealed partial class SessionBackgroundTasksChangedData { } +/// Ephemeral invalidation signal for a changed factory run. +[Experimental(Diagnostics.Experimental)] +public sealed partial class FactoryRunUpdatedData +{ + /// Monotonic revision now available for the run. + [JsonPropertyName("revision")] + public required long Revision { get; set; } + + /// Gets or sets the runId value. + [JsonPropertyName("runId")] + public required string RunId { get; set; } +} + /// Payload of `session.skills_loaded` listing resolved skill metadata. public sealed partial class SessionSkillsLoadedData { @@ -6670,6 +6815,20 @@ public sealed partial class SystemNotificationInstructionDiscovered : SystemNoti public required string TriggerTool { get; set; } } +/// System notification metadata from an external host that does not match a runtime-owned notification kind. +/// The unclassified variant of . +public sealed partial class SystemNotificationUnclassified : SystemNotification +{ + /// + [JsonIgnore] + public override string Type => "unclassified"; + + /// Opaque metadata supplied by the external host, when present. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("metadata")] + public JsonElement? Metadata { get; set; } +} + /// Structured metadata identifying what triggered this notification. /// Polymorphic base type discriminated by type. [JsonPolymorphic( @@ -6681,6 +6840,7 @@ public sealed partial class SystemNotificationInstructionDiscovered : SystemNoti [JsonDerivedType(typeof(SystemNotificationShellCompleted), "shell_completed")] [JsonDerivedType(typeof(SystemNotificationShellDetachedCompleted), "shell_detached_completed")] [JsonDerivedType(typeof(SystemNotificationInstructionDiscovered), "instruction_discovered")] +[JsonDerivedType(typeof(SystemNotificationUnclassified), "unclassified")] public partial class SystemNotification { /// The type discriminator. @@ -6702,6 +6862,19 @@ public sealed partial class PermissionRequestShellCommand public required bool ReadOnly { get; set; } } +/// A parsed shell command segment used for argument-aware managed policy matching. +/// Nested data type for PermissionRequestShellCommandSegment. +public sealed partial class PermissionRequestShellCommandSegment +{ + /// Full text of this command segment, including arguments. + [JsonPropertyName("fullCommandText")] + public required string FullCommandText { get; set; } + + /// Command identifier (e.g., executable name). + [JsonPropertyName("identifier")] + public required string Identifier { get; set; } +} + /// A URL that may be accessed by a command in a shell permission request. /// Nested data type for PermissionRequestShellPossibleUrl. public sealed partial class PermissionRequestShellPossibleUrl @@ -6727,6 +6900,11 @@ public sealed partial class PermissionRequestShell : PermissionRequest [JsonPropertyName("commands")] public required PermissionRequestShellCommand[] Commands { get; set; } + /// Parsed command segments, including arguments, used for managed policy matching. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("commandSegments")] + public PermissionRequestShellCommandSegment[]? CommandSegments { get; set; } + /// The complete shell command text to be executed. [JsonPropertyName("fullCommandText")] public required string FullCommandText { get; set; } @@ -6739,6 +6917,11 @@ public sealed partial class PermissionRequestShell : PermissionRequest [JsonPropertyName("intention")] public required string Intention { get; set; } + /// Whether managed policy requires a human response and forbids host auto-approval. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("managedApprovalRequired")] + public bool? ManagedApprovalRequired { get; set; } + /// File paths that may be read or written by the command. [JsonPropertyName("possiblePaths")] public required string[] PossiblePaths { get; set; } @@ -6792,6 +6975,11 @@ public sealed partial class PermissionRequestWrite : PermissionRequest [JsonPropertyName("intention")] public required string Intention { get; set; } + /// Whether managed policy requires a human response and forbids host auto-approval. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("managedApprovalRequired")] + public bool? ManagedApprovalRequired { get; set; } + /// Complete new file contents for newly created files. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("newFileContents")] @@ -6825,6 +7013,11 @@ public sealed partial class PermissionRequestRead : PermissionRequest [JsonPropertyName("intention")] public required string Intention { get; set; } + /// Whether managed policy requires a human response and forbids host auto-approval. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("managedApprovalRequired")] + public bool? ManagedApprovalRequired { get; set; } + /// Path of the file or directory being read. [JsonPropertyName("path")] public required string Path { get; set; } @@ -6892,6 +7085,11 @@ public sealed partial class PermissionRequestUrl : PermissionRequest [JsonPropertyName("intention")] public required string Intention { get; set; } + /// Whether managed policy requires a human response and forbids host auto-approval. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("managedApprovalRequired")] + public bool? ManagedApprovalRequired { get; set; } + /// True when this URL fetch is requesting to bypass the sandbox network policy: either the model set requestSandboxBypass: true, or the tool re-issued the request as an interactive bypass after the network policy denied the approved URL (host opted in via sandbox.allowBypass). This is a request, not a grant: the fetch runs only if the user approves this permission request. Hosts should highlight the elevated risk in the approval UI. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("requestSandboxBypass")] @@ -7083,6 +7281,16 @@ public partial class PermissionRequest [Experimental(Diagnostics.Experimental)] public sealed partial class PermissionAutoApproval { + /// Classified cause of an `error` recommendation. Absent for every other recommendation. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("failureReason")] + public AutoApprovalJudgeFailureReason? FailureReason { get; set; } + + /// Model id that produced the recommendation, when the judge was consulted and reported one. Absent for `excluded` (the judge was not consulted) and for failures that occurred before a model was selected. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("model")] + public string? Model { get; set; } + /// Human-readable reason for the judge's recommendation, when available. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("reason")] @@ -7123,6 +7331,11 @@ public sealed partial class PermissionPromptRequestCommands : PermissionPromptRe [JsonPropertyName("intention")] public required string Intention { get; set; } + /// Whether managed policy requires a human response and forbids host auto-approval. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("managedApprovalRequired")] + public bool? ManagedApprovalRequired { get; set; } + /// Tool call ID that triggered this permission request. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("toolCallId")] @@ -7164,6 +7377,11 @@ public sealed partial class PermissionPromptRequestWrite : PermissionPromptReque [JsonPropertyName("intention")] public required string Intention { get; set; } + /// Whether managed policy requires a human response and forbids host auto-approval. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("managedApprovalRequired")] + public bool? ManagedApprovalRequired { get; set; } + /// Complete new file contents for newly created files. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("newFileContents")] @@ -7193,6 +7411,11 @@ public sealed partial class PermissionPromptRequestRead : PermissionPromptReques [JsonPropertyName("intention")] public required string Intention { get; set; } + /// Whether managed policy requires a human response and forbids host auto-approval. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("managedApprovalRequired")] + public bool? ManagedApprovalRequired { get; set; } + /// Path of the file or directory being read. [JsonPropertyName("path")] public required string Path { get; set; } @@ -7258,6 +7481,11 @@ public sealed partial class PermissionPromptRequestUrl : PermissionPromptRequest [JsonPropertyName("intention")] public required string Intention { get; set; } + /// Whether managed policy requires a human response and forbids host auto-approval. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("managedApprovalRequired")] + public bool? ManagedApprovalRequired { get; set; } + /// True when this URL fetch is requesting to bypass the sandbox network policy: either the model set requestSandboxBypass: true, or the tool re-issued the request as an interactive bypass after the network policy denied the approved URL (host opted in via sandbox.allowBypass). This is a request, not a grant: the fetch runs only if the user approves this permission request. Hosts should highlight the elevated risk in the approval UI. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("requestSandboxBypass")] @@ -7879,6 +8107,168 @@ public sealed partial class McpOauthWWWAuthenticateParams public string? Scope { get; set; } } +/// A redacted MCP protocol message observed on the server transport. +/// The wire_message variant of . +public sealed partial class McpDiagnosticDetailWireMessage : McpDiagnosticDetail +{ + /// + [JsonIgnore] + public override string Kind => "wire_message"; + + /// Size in bytes of the observed message before any diagnostic payload clipping. + [JsonPropertyName("byteSize")] + public required long ByteSize { get; set; } + + /// Whether the runtime sent the message to the server or received it from the server. + [JsonPropertyName("direction")] + public required McpDiagnosticWireMessageDirection Direction { get; set; } + + /// MCP protocol message category. + [JsonPropertyName("messageKind")] + public required McpDiagnosticWireMessageKind MessageKind { get; set; } + + /// MCP method name, when the diagnostic message is associated with a method. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("method")] + public string? Method { get; set; } + + /// Already-serialized JSON payload after redaction. Omitted when payload capture is unavailable or excluded; when present, it may be clipped as indicated by `truncated`. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("payload")] + public string? Payload { get; set; } + + /// JSON-RPC request ID serialized as a string, when the diagnostic message is associated with a request or response. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("requestId")] + public string? RequestId { get; set; } + + /// Whether the captured `payload` was clipped to the diagnostic size cap. + [JsonPropertyName("truncated")] + public required bool Truncated { get; set; } +} + +/// A redacted HTTP request or response observed while communicating with an MCP server. +/// The http_exchange variant of . +public sealed partial class McpDiagnosticDetailHttpExchange : McpDiagnosticDetail +{ + /// + [JsonIgnore] + public override string Kind => "http_exchange"; + + /// Elapsed duration in milliseconds for the HTTP exchange, when measured. + [JsonConverter(typeof(MillisecondsTimeSpanConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("durationMs")] + public TimeSpan? Duration { get; set; } + + /// Redacted HTTP headers as name-to-value pairs, when available. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("headers")] + public IDictionary? Headers { get; set; } + + /// HTTP method used by the request, when available. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("httpMethod")] + public string? HttpMethod { get; set; } + + /// Whether this diagnostic describes the outgoing HTTP request or incoming HTTP response. + [JsonPropertyName("phase")] + public required McpDiagnosticHttpExchangePhase Phase { get; set; } + + /// Whether the exchange belongs to the Server-Sent Events stream leg, when applicable. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("sse")] + public bool? Sse { get; set; } + + /// HTTP response status code, when this diagnostic is associated with a response. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("statusCode")] + public int? StatusCode { get; set; } + + /// Redacted HTTP URL, when available. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("url")] + public string? Url { get; set; } +} + +/// A line emitted by an MCP server process. +/// The server_log variant of . +public sealed partial class McpDiagnosticDetailServerLog : McpDiagnosticDetail +{ + /// + [JsonIgnore] + public override string Kind => "server_log"; + + /// One server log line, without a trailing line terminator. + [JsonPropertyName("line")] + public required string Line { get; set; } + + /// Operating-system process ID of the MCP server process, when known. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("pid")] + public long? Pid { get; set; } + + /// Process output stream that emitted the line. + [JsonPropertyName("stream")] + public required McpDiagnosticServerLogStream Stream { get; set; } +} + +/// MCP server process startup or termination details. +/// The process_lifecycle variant of . +public sealed partial class McpDiagnosticDetailProcessLifecycle : McpDiagnosticDetail +{ + /// + [JsonIgnore] + public override string Kind => "process_lifecycle"; + + /// Arguments used to launch the MCP server process, when applicable. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("args")] + public string[]? Args { get; set; } + + /// Command used to launch the MCP server process, when applicable. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("command")] + public string? Command { get; set; } + + /// Names of environment variables supplied to the MCP server process. Values are never included. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("envKeys")] + public string[]? EnvKeys { get; set; } + + /// Process exit code, when the MCP server process has exited normally. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("exitCode")] + public long? ExitCode { get; set; } + + /// Operating-system process ID of the MCP server process, when known. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("pid")] + public long? Pid { get; set; } + + /// Signal that terminated the MCP server process, when applicable. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("signal")] + public string? Signal { get; set; } +} + +/// Detailed MCP diagnostic payload discriminated by `kind`. +/// Polymorphic base type discriminated by kind. +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(McpDiagnosticDetailWireMessage), "wire_message")] +[JsonDerivedType(typeof(McpDiagnosticDetailHttpExchange), "http_exchange")] +[JsonDerivedType(typeof(McpDiagnosticDetailServerLog), "server_log")] +[JsonDerivedType(typeof(McpDiagnosticDetailProcessLifecycle), "process_lifecycle")] +public partial class McpDiagnosticDetail +{ + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} + + /// The user's selected action for an exhausted session limit. /// Nested data type for SessionLimitsExhaustedResponse. public sealed partial class SessionLimitsExhaustedResponse @@ -8412,6 +8802,67 @@ public override void Write(Utf8JsonWriter writer, Verbosity value, JsonSerialize } } +/// Who created the schedule: `user` (an explicit user action such as `/every` or `/after`) or `model` (the agent via the `manage_schedule` tool). Gates whether a scheduled skill that opted out of model invocation may fire: only user-created schedules may. +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ScheduleOrigin : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ScheduleOrigin(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// The schedule was created by an explicit user action, such as `/every` or `/after`. + public static ScheduleOrigin User { get; } = new("user"); + + /// The schedule was created by the agent via the `manage_schedule` tool. + public static ScheduleOrigin Model { get; } = new("model"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ScheduleOrigin left, ScheduleOrigin right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ScheduleOrigin left, ScheduleOrigin right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is ScheduleOrigin other && Equals(other); + + /// + public bool Equals(ScheduleOrigin other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override ScheduleOrigin Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, ScheduleOrigin value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ScheduleOrigin)); + } + } +} + /// The type of operation performed on the autopilot objective state file. [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] @@ -8919,36 +9370,106 @@ public override void Write(Utf8JsonWriter writer, ShutdownType value, JsonSerial } } -/// The agent mode that was active when this message was sent. +/// What initiated a conversation compaction. [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct UserMessageAgentMode : IEquatable +public readonly struct CompactionTrigger : IEquatable { private readonly string? _value; - /// Initializes a new instance of the struct. - /// The value to associate with this . + /// Initializes a new instance of the struct. + /// The value to associate with this . [JsonConstructor] - public UserMessageAgentMode(string value) + public CompactionTrigger(string value) { ArgumentException.ThrowIfNullOrWhiteSpace(value); _value = value; } - /// Gets the value associated with this . + /// Gets the value associated with this . public string Value => _value ?? string.Empty; - /// The agent is responding interactively to the user. - public static UserMessageAgentMode Interactive { get; } = new("interactive"); + /// Background compaction started automatically because context utilization crossed the background threshold. + public static CompactionTrigger Threshold { get; } = new("threshold"); - /// The agent is preparing a plan before making changes. - public static UserMessageAgentMode Plan { get; } = new("plan"); + /// Compaction forced by a context-limit model response (e.g. HTTP 413) before retrying the request. + public static CompactionTrigger ContextLimitRetry { get; } = new("context_limit_retry"); - /// The agent is working autonomously toward task completion. - public static UserMessageAgentMode Autopilot { get; } = new("autopilot"); + /// User-requested compaction, e.g. the /compact command or the history.compact API. + public static CompactionTrigger Manual { get; } = new("manual"); - /// The agent is in shell-focused UI mode. - public static UserMessageAgentMode Shell { get; } = new("shell"); + /// Emergency compaction triggered by high process memory usage. + public static CompactionTrigger MemoryPressure { get; } = new("memory_pressure"); + + /// Compaction requested while switching to a model with a smaller context window. + public static CompactionTrigger ModelSwitch { get; } = new("model_switch"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CompactionTrigger left, CompactionTrigger right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CompactionTrigger left, CompactionTrigger right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is CompactionTrigger other && Equals(other); + + /// + public bool Equals(CompactionTrigger other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override CompactionTrigger Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, CompactionTrigger value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CompactionTrigger)); + } + } +} + +/// The agent mode that was active when this message was sent. +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct UserMessageAgentMode : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public UserMessageAgentMode(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// The agent is responding interactively to the user. + public static UserMessageAgentMode Interactive { get; } = new("interactive"); + + /// The agent is preparing a plan before making changes. + public static UserMessageAgentMode Plan { get; } = new("plan"); + + /// The agent is working autonomously toward task completion. + public static UserMessageAgentMode Autopilot { get; } = new("autopilot"); + + /// The agent is in shell-focused UI mode. + public static UserMessageAgentMode Shell { get; } = new("shell"); /// Returns a value indicating whether two instances are equivalent. public static bool operator ==(UserMessageAgentMode left, UserMessageAgentMode right) => left.Equals(right); @@ -10414,6 +10935,77 @@ public override void Write(Utf8JsonWriter writer, PermissionRequestMemoryDirecti } } +/// Why the auto-approval judge produced no usable recommendation. Present only alongside an `error` recommendation, where the human-readable reason is a fixed string and therefore cannot distinguish these cases. Intended to make a judge failure reportable by a consumer that has no access to the host's logs. +[Experimental(Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct AutoApprovalJudgeFailureReason : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public AutoApprovalJudgeFailureReason(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// The judge model call exceeded its deadline. + public static AutoApprovalJudgeFailureReason Timeout { get; } = new("timeout"); + + /// The judge model call was cancelled before it returned. + public static AutoApprovalJudgeFailureReason Abort { get; } = new("abort"); + + /// The judge model call completed but returned no content. + public static AutoApprovalJudgeFailureReason EmptyResponse { get; } = new("empty_response"); + + /// The judge model call failed (for example a transport, authentication, or rate-limit error). + public static AutoApprovalJudgeFailureReason ModelError { get; } = new("model_error"); + + /// The judge model replied, but the reply carried no ALLOW/DENY verdict. + public static AutoApprovalJudgeFailureReason ParseError { get; } = new("parse_error"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(AutoApprovalJudgeFailureReason left, AutoApprovalJudgeFailureReason right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(AutoApprovalJudgeFailureReason left, AutoApprovalJudgeFailureReason right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is AutoApprovalJudgeFailureReason other && Equals(other); + + /// + public bool Equals(AutoApprovalJudgeFailureReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override AutoApprovalJudgeFailureReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, AutoApprovalJudgeFailureReason value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AutoApprovalJudgeFailureReason)); + } + } +} + /// Outcome of the auto-approval safety judge for a permission request. Present only when auto mode is enabled; its absence means the judge did not evaluate the request (auto mode was off). [Experimental(Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] @@ -10927,6 +11519,320 @@ public override void Write(Utf8JsonWriter writer, McpHeadersRefreshCompletedOutc } } +/// Direction of an MCP wire message. +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct McpDiagnosticWireMessageDirection : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public McpDiagnosticWireMessageDirection(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// The runtime sent the message to the MCP server. + public static McpDiagnosticWireMessageDirection Outbound { get; } = new("outbound"); + + /// The runtime received the message from the MCP server. + public static McpDiagnosticWireMessageDirection Inbound { get; } = new("inbound"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpDiagnosticWireMessageDirection left, McpDiagnosticWireMessageDirection right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpDiagnosticWireMessageDirection left, McpDiagnosticWireMessageDirection right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is McpDiagnosticWireMessageDirection other && Equals(other); + + /// + public bool Equals(McpDiagnosticWireMessageDirection other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override McpDiagnosticWireMessageDirection Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, McpDiagnosticWireMessageDirection value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpDiagnosticWireMessageDirection)); + } + } +} + +/// Category of an MCP wire message. +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct McpDiagnosticWireMessageKind : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public McpDiagnosticWireMessageKind(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// A JSON-RPC request. + public static McpDiagnosticWireMessageKind Request { get; } = new("request"); + + /// A JSON-RPC response. + public static McpDiagnosticWireMessageKind Response { get; } = new("response"); + + /// A JSON-RPC notification. + public static McpDiagnosticWireMessageKind Notification { get; } = new("notification"); + + /// A protocol or transport error represented as a diagnostic message. + public static McpDiagnosticWireMessageKind Error { get; } = new("error"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpDiagnosticWireMessageKind left, McpDiagnosticWireMessageKind right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpDiagnosticWireMessageKind left, McpDiagnosticWireMessageKind right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is McpDiagnosticWireMessageKind other && Equals(other); + + /// + public bool Equals(McpDiagnosticWireMessageKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override McpDiagnosticWireMessageKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, McpDiagnosticWireMessageKind value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpDiagnosticWireMessageKind)); + } + } +} + +/// Phase of an observed MCP HTTP exchange. +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct McpDiagnosticHttpExchangePhase : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public McpDiagnosticHttpExchangePhase(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// The outgoing HTTP request. + public static McpDiagnosticHttpExchangePhase Request { get; } = new("request"); + + /// The incoming HTTP response. + public static McpDiagnosticHttpExchangePhase Response { get; } = new("response"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpDiagnosticHttpExchangePhase left, McpDiagnosticHttpExchangePhase right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpDiagnosticHttpExchangePhase left, McpDiagnosticHttpExchangePhase right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is McpDiagnosticHttpExchangePhase other && Equals(other); + + /// + public bool Equals(McpDiagnosticHttpExchangePhase other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override McpDiagnosticHttpExchangePhase Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, McpDiagnosticHttpExchangePhase value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpDiagnosticHttpExchangePhase)); + } + } +} + +/// Output stream for an MCP server log line. +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct McpDiagnosticServerLogStream : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public McpDiagnosticServerLogStream(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// The process standard-error stream. + public static McpDiagnosticServerLogStream Stderr { get; } = new("stderr"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpDiagnosticServerLogStream left, McpDiagnosticServerLogStream right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpDiagnosticServerLogStream left, McpDiagnosticServerLogStream right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is McpDiagnosticServerLogStream other && Equals(other); + + /// + public bool Equals(McpDiagnosticServerLogStream other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override McpDiagnosticServerLogStream Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, McpDiagnosticServerLogStream value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpDiagnosticServerLogStream)); + } + } +} + +/// Transport mechanism: stdio, http, sse (deprecated), or memory (in-process MCP server). +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct McpServerTransport : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public McpServerTransport(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// Server communicates over stdio with a local child process. + public static McpServerTransport Stdio { get; } = new("stdio"); + + /// Server communicates over streamable HTTP. + public static McpServerTransport Http { get; } = new("http"); + + /// Server communicates over Server-Sent Events (deprecated). + public static McpServerTransport Sse { get; } = new("sse"); + + /// Server is backed by an in-memory runtime implementation. + public static McpServerTransport Memory { get; } = new("memory"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpServerTransport left, McpServerTransport right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpServerTransport left, McpServerTransport right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is McpServerTransport other && Equals(other); + + /// + public bool Equals(McpServerTransport other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override McpServerTransport Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, McpServerTransport value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpServerTransport)); + } + } +} + /// The user's auto-mode-switch choice. [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] @@ -11597,73 +12503,6 @@ public override void Write(Utf8JsonWriter writer, McpServerStatus value, JsonSer } } -/// Transport mechanism: stdio, http, sse (deprecated), or memory (in-process MCP server). -[JsonConverter(typeof(Converter))] -[DebuggerDisplay("{Value,nq}")] -public readonly struct McpServerTransport : IEquatable -{ - private readonly string? _value; - - /// Initializes a new instance of the struct. - /// The value to associate with this . - [JsonConstructor] - public McpServerTransport(string value) - { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; - } - - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; - - /// Server communicates over stdio with a local child process. - public static McpServerTransport Stdio { get; } = new("stdio"); - - /// Server communicates over streamable HTTP. - public static McpServerTransport Http { get; } = new("http"); - - /// Server communicates over Server-Sent Events (deprecated). - public static McpServerTransport Sse { get; } = new("sse"); - - /// Server is backed by an in-memory runtime implementation. - public static McpServerTransport Memory { get; } = new("memory"); - - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpServerTransport left, McpServerTransport right) => left.Equals(right); - - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpServerTransport left, McpServerTransport right) => !(left == right); - - /// - public override bool Equals(object? obj) => obj is McpServerTransport other && Equals(other); - - /// - public bool Equals(McpServerTransport other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - - /// - public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - - /// - public override string ToString() => Value; - - /// Provides a for serializing instances. - [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter - { - /// - public override McpServerTransport Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); - } - - /// - public override void Write(Utf8JsonWriter writer, McpServerTransport value, JsonSerializerOptions options) - { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpServerTransport)); - } - } -} - /// Discovery source. [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] @@ -11908,6 +12747,8 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(ExternalToolCompletedEvent))] [JsonSerializable(typeof(ExternalToolRequestedData))] [JsonSerializable(typeof(ExternalToolRequestedEvent))] +[JsonSerializable(typeof(FactoryRunUpdatedData))] +[JsonSerializable(typeof(FactoryRunUpdatedEvent))] [JsonSerializable(typeof(GitHubRepoRef))] [JsonSerializable(typeof(HandoffRepository))] [JsonSerializable(typeof(HeaderEntry))] @@ -11923,6 +12764,13 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(McpAppToolCallCompleteEvent))] [JsonSerializable(typeof(McpAppToolCallCompleteToolMeta))] [JsonSerializable(typeof(McpAppToolCallCompleteToolMetaUI))] +[JsonSerializable(typeof(McpDiagnosticData))] +[JsonSerializable(typeof(McpDiagnosticDetail))] +[JsonSerializable(typeof(McpDiagnosticDetailHttpExchange))] +[JsonSerializable(typeof(McpDiagnosticDetailProcessLifecycle))] +[JsonSerializable(typeof(McpDiagnosticDetailServerLog))] +[JsonSerializable(typeof(McpDiagnosticDetailWireMessage))] +[JsonSerializable(typeof(McpDiagnosticEvent))] [JsonSerializable(typeof(McpHeadersRefreshCompletedData))] [JsonSerializable(typeof(McpHeadersRefreshCompletedEvent))] [JsonSerializable(typeof(McpHeadersRefreshRequiredData))] @@ -11974,6 +12822,7 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(PermissionRequestRead))] [JsonSerializable(typeof(PermissionRequestShell))] [JsonSerializable(typeof(PermissionRequestShellCommand))] +[JsonSerializable(typeof(PermissionRequestShellCommandSegment))] [JsonSerializable(typeof(PermissionRequestShellPossibleUrl))] [JsonSerializable(typeof(PermissionRequestUrl))] [JsonSerializable(typeof(PermissionRequestWrite))] @@ -12130,6 +12979,7 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(SystemNotificationNewInboxMessage))] [JsonSerializable(typeof(SystemNotificationShellCompleted))] [JsonSerializable(typeof(SystemNotificationShellDetachedCompleted))] +[JsonSerializable(typeof(SystemNotificationUnclassified))] [JsonSerializable(typeof(ToolExecutionCompleteContent))] [JsonSerializable(typeof(ToolExecutionCompleteContentAudio))] [JsonSerializable(typeof(ToolExecutionCompleteContentImage))] diff --git a/go/rpc/zsession_encoding.go b/go/rpc/zsession_encoding.go index fe99126cf7..9970366716 100644 --- a/go/rpc/zsession_encoding.go +++ b/go/rpc/zsession_encoding.go @@ -203,6 +203,12 @@ func (e *SessionEvent) UnmarshalJSON(data []byte) error { return err } e.Data = &d + case SessionEventTypeFactoryRunUpdated: + var d FactoryRunUpdatedData + if err := json.Unmarshal(raw.Data, &d); err != nil { + return err + } + e.Data = &d case SessionEventTypeHookEnd: var d HookEndData if err := json.Unmarshal(raw.Data, &d); err != nil { @@ -227,6 +233,12 @@ func (e *SessionEvent) UnmarshalJSON(data []byte) error { return err } e.Data = &d + case SessionEventTypeMCPDiagnostic: + var d MCPDiagnosticData + if err := json.Unmarshal(raw.Data, &d); err != nil { + return err + } + e.Data = &d case SessionEventTypeMCPHeadersRefreshCompleted: var d MCPHeadersRefreshCompletedData if err := json.Unmarshal(raw.Data, &d); err != nil { @@ -1370,6 +1382,12 @@ func unmarshalSystemNotification(data []byte) (SystemNotification, error) { return nil, err } return &d, nil + case SystemNotificationTypeUnclassified: + var d SystemNotificationUnclassified + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil default: return &RawSystemNotification{Discriminator: raw.Type, Raw: data}, nil } @@ -1452,6 +1470,17 @@ func (r SystemNotificationShellDetachedCompleted) MarshalJSON() ([]byte, error) }) } +func (r SystemNotificationUnclassified) MarshalJSON() ([]byte, error) { + type alias SystemNotificationUnclassified + return json.Marshal(struct { + Type SystemNotificationType `json:"type"` + alias + }{ + Type: r.Type(), + alias: alias(r), + }) +} + func (r *SystemNotificationData) UnmarshalJSON(data []byte) error { type rawSystemNotificationData struct { Content string `json:"content"` @@ -1893,6 +1922,7 @@ func (r *PermissionRequestedData) UnmarshalJSON(data []byte) error { PromptRequest json.RawMessage `json:"promptRequest,omitempty"` RequestID string `json:"requestId"` ResolvedByHook *bool `json:"resolvedByHook,omitempty"` + RiskAssessment any `json:"riskAssessment,omitempty"` } var raw rawPermissionRequestedData if err := json.Unmarshal(data, &raw); err != nil { @@ -1914,6 +1944,7 @@ func (r *PermissionRequestedData) UnmarshalJSON(data []byte) error { } r.RequestID = raw.RequestID r.ResolvedByHook = raw.ResolvedByHook + r.RiskAssessment = raw.RiskAssessment return nil } @@ -2159,6 +2190,125 @@ func (r *PermissionCompletedData) UnmarshalJSON(data []byte) error { return nil } +func unmarshalMCPDiagnosticDetail(data []byte) (MCPDiagnosticDetail, error) { + if string(data) == "null" { + return nil, nil + } + type rawUnion struct { + Kind MCPDiagnosticDetailKind `json:"kind"` + } + var raw rawUnion + if err := json.Unmarshal(data, &raw); err != nil { + return nil, err + } + + switch raw.Kind { + case MCPDiagnosticDetailKindHTTPExchange: + var d MCPDiagnosticHTTPExchange + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil + case MCPDiagnosticDetailKindProcessLifecycle: + var d MCPDiagnosticProcessLifecycle + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil + case MCPDiagnosticDetailKindServerLog: + var d MCPDiagnosticServerLog + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil + case MCPDiagnosticDetailKindWireMessage: + var d MCPDiagnosticWireMessage + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil + default: + return &RawMCPDiagnosticDetail{Discriminator: raw.Kind, Raw: data}, nil + } +} + +func (r RawMCPDiagnosticDetail) MarshalJSON() ([]byte, error) { + if r.Raw != nil { + return r.Raw, nil + } + return json.Marshal(struct { + Kind MCPDiagnosticDetailKind `json:"kind"` + }{ + Kind: r.Discriminator, + }) +} + +func (r MCPDiagnosticHTTPExchange) MarshalJSON() ([]byte, error) { + type alias MCPDiagnosticHTTPExchange + return json.Marshal(struct { + Kind MCPDiagnosticDetailKind `json:"kind"` + alias + }{ + Kind: r.Kind(), + alias: alias(r), + }) +} + +func (r MCPDiagnosticProcessLifecycle) MarshalJSON() ([]byte, error) { + type alias MCPDiagnosticProcessLifecycle + return json.Marshal(struct { + Kind MCPDiagnosticDetailKind `json:"kind"` + alias + }{ + Kind: r.Kind(), + alias: alias(r), + }) +} + +func (r MCPDiagnosticServerLog) MarshalJSON() ([]byte, error) { + type alias MCPDiagnosticServerLog + return json.Marshal(struct { + Kind MCPDiagnosticDetailKind `json:"kind"` + alias + }{ + Kind: r.Kind(), + alias: alias(r), + }) +} + +func (r MCPDiagnosticWireMessage) MarshalJSON() ([]byte, error) { + type alias MCPDiagnosticWireMessage + return json.Marshal(struct { + Kind MCPDiagnosticDetailKind `json:"kind"` + alias + }{ + Kind: r.Kind(), + alias: alias(r), + }) +} + +func (r *MCPDiagnosticData) UnmarshalJSON(data []byte) error { + type rawMCPDiagnosticData struct { + Detail json.RawMessage `json:"detail"` + ServerName string `json:"serverName"` + Transport MCPServerTransport `json:"transport"` + } + var raw rawMCPDiagnosticData + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + if raw.Detail != nil { + value, err := unmarshalMCPDiagnosticDetail(raw.Detail) + if err != nil { + return err + } + r.Detail = value + } + r.ServerName = raw.ServerName + r.Transport = raw.Transport + return nil +} + func (r *SessionExtensionsAttachmentsPushedData) UnmarshalJSON(data []byte) error { type rawSessionExtensionsAttachmentsPushedData struct { Attachments []json.RawMessage `json:"attachments"` diff --git a/go/rpc/zsession_events.go b/go/rpc/zsession_events.go index 44f49948f4..fc4f1f2af0 100644 --- a/go/rpc/zsession_events.go +++ b/go/rpc/zsession_events.go @@ -81,24 +81,30 @@ const ( SessionEventTypeExitPlanModeRequested SessionEventType = "exit_plan_mode.requested" SessionEventTypeExternalToolCompleted SessionEventType = "external_tool.completed" SessionEventTypeExternalToolRequested SessionEventType = "external_tool.requested" - SessionEventTypeHookEnd SessionEventType = "hook.end" - SessionEventTypeHookProgress SessionEventType = "hook.progress" - SessionEventTypeHookStart SessionEventType = "hook.start" - SessionEventTypeMCPAppToolCallComplete SessionEventType = "mcp_app.tool_call_complete" - SessionEventTypeMCPHeadersRefreshCompleted SessionEventType = "mcp.headers_refresh_completed" - SessionEventTypeMCPHeadersRefreshRequired SessionEventType = "mcp.headers_refresh_required" - SessionEventTypeMCPOauthCompleted SessionEventType = "mcp.oauth_completed" - SessionEventTypeMCPOauthRequired SessionEventType = "mcp.oauth_required" - SessionEventTypeMCPPromptsListChanged SessionEventType = "mcp.prompts.list_changed" - SessionEventTypeMCPResourcesListChanged SessionEventType = "mcp.resources.list_changed" - SessionEventTypeMCPToolsListChanged SessionEventType = "mcp.tools.list_changed" - SessionEventTypeModelCallFailure SessionEventType = "model.call_failure" - SessionEventTypeModelCallStart SessionEventType = "model.call_start" - SessionEventTypePendingMessagesModified SessionEventType = "pending_messages.modified" - SessionEventTypePermissionCompleted SessionEventType = "permission.completed" - SessionEventTypePermissionRequested SessionEventType = "permission.requested" - SessionEventTypeSamplingCompleted SessionEventType = "sampling.completed" - SessionEventTypeSamplingRequested SessionEventType = "sampling.requested" + // Experimental: SessionEventTypeFactoryRunUpdated identifies an experimental event that may + // change or be removed. + SessionEventTypeFactoryRunUpdated SessionEventType = "factory.run_updated" + SessionEventTypeHookEnd SessionEventType = "hook.end" + SessionEventTypeHookProgress SessionEventType = "hook.progress" + SessionEventTypeHookStart SessionEventType = "hook.start" + SessionEventTypeMCPAppToolCallComplete SessionEventType = "mcp_app.tool_call_complete" + // Experimental: SessionEventTypeMCPDiagnostic identifies an experimental event that may + // change or be removed. + SessionEventTypeMCPDiagnostic SessionEventType = "mcp.diagnostic" + SessionEventTypeMCPHeadersRefreshCompleted SessionEventType = "mcp.headers_refresh_completed" + SessionEventTypeMCPHeadersRefreshRequired SessionEventType = "mcp.headers_refresh_required" + SessionEventTypeMCPOauthCompleted SessionEventType = "mcp.oauth_completed" + SessionEventTypeMCPOauthRequired SessionEventType = "mcp.oauth_required" + SessionEventTypeMCPPromptsListChanged SessionEventType = "mcp.prompts.list_changed" + SessionEventTypeMCPResourcesListChanged SessionEventType = "mcp.resources.list_changed" + SessionEventTypeMCPToolsListChanged SessionEventType = "mcp.tools.list_changed" + SessionEventTypeModelCallFailure SessionEventType = "model.call_failure" + SessionEventTypeModelCallStart SessionEventType = "model.call_start" + SessionEventTypePendingMessagesModified SessionEventType = "pending_messages.modified" + SessionEventTypePermissionCompleted SessionEventType = "permission.completed" + SessionEventTypePermissionRequested SessionEventType = "permission.requested" + SessionEventTypeSamplingCompleted SessionEventType = "sampling.completed" + SessionEventTypeSamplingRequested SessionEventType = "sampling.requested" // Experimental: SessionEventTypeSessionAutoModeResolved identifies an experimental event // that may change or be removed. SessionEventTypeSessionAutoModeResolved SessionEventType = "session.auto_mode_resolved" @@ -214,6 +220,7 @@ type AssistantReasoningData struct { Content string `json:"content"` // Unique identifier for this reasoning block ReasoningID string `json:"reasoningId"` + Rte *bool `json:"rte,omitempty"` } func (*AssistantReasoningData) sessionEventData() {} @@ -253,6 +260,7 @@ type AssistantMessageData struct { ReasoningWireField *string `json:"reasoningWireField,omitempty"` // GitHub request tracing ID (x-github-request-id header) for correlating with server-side logs RequestID *string `json:"requestId,omitempty"` + Rte *bool `json:"rte,omitempty"` // Neutral provider-tagged server-side tool-use payload (tool search, advisor) for verbatim round-tripping ServerTools *AssistantMessageServerTools `json:"serverTools,omitempty"` // Copilot service request ID (x-copilot-service-request-id header) for CAPI log correlation @@ -356,12 +364,18 @@ func (*SessionBinaryAssetData) Type() SessionEventType { return SessionEventType type SessionCompactionStartData struct { // Token count from non-system messages (user, assistant, tool) at compaction start ConversationTokens *int64 `json:"conversationTokens,omitempty"` + // Total context tokens (system + conversation + tool definitions) at compaction start, when known + CurrentTokens *int64 `json:"currentTokens,omitempty"` // Model identifier used for compaction, when known Model *string `json:"model,omitempty"` // Token count from system message(s) at compaction start SystemTokens *int64 `json:"systemTokens,omitempty"` + // Model context window token limit the compaction is targeting, when known + TokenLimit *int64 `json:"tokenLimit,omitempty"` // Token count from tool definitions at compaction start ToolDefinitionsTokens *int64 `json:"toolDefinitionsTokens,omitempty"` + // What initiated this compaction, when known + Trigger *CompactionTrigger `json:"trigger,omitempty"` } func (*SessionCompactionStartData) sessionEventData() {} @@ -403,10 +417,14 @@ type SessionCompactionCompleteData struct { SummaryContent *string `json:"summaryContent,omitempty"` // Token count from system message(s) after compaction SystemTokens *int64 `json:"systemTokens,omitempty"` + // Model context window token limit the compaction was targeting, when known + TokenLimit *int64 `json:"tokenLimit,omitempty"` // Number of tokens removed during compaction TokensRemoved *int64 `json:"tokensRemoved,omitempty"` // Token count from tool definitions after compaction ToolDefinitionsTokens *int64 `json:"toolDefinitionsTokens,omitempty"` + // What initiated this compaction, when known + Trigger *CompactionTrigger `json:"trigger,omitempty"` } func (*SessionCompactionCompleteData) sessionEventData() {} @@ -609,6 +627,8 @@ type SessionManagedSettingsResolvedData struct { FailClosed bool `json:"failClosed"` // The setting keys under enterprise management in the effective managed settings (e.g. `model`, `enabledPlugins`, `permissions`). Empty when no managed settings are in force. ManagedKeys []string `json:"managedKeys"` + // Whether server and device each supplied a permission allowlist, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. + PermissionsAllowIntersected *bool `json:"permissionsAllowIntersected,omitempty"` // Whether the server (account/org) managed-settings layer was present ServerManaged bool `json:"serverManaged"` // The effective (resolved) managed settings values, so clients can render exactly what is enforced. Absent when no managed policy is in force. @@ -622,6 +642,17 @@ func (*SessionManagedSettingsResolvedData) Type() SessionEventType { return SessionEventTypeSessionManagedSettingsResolved } +// Ephemeral invalidation signal for a changed factory run. +// Experimental: FactoryRunUpdatedData is part of an experimental API and may change or be removed. +type FactoryRunUpdatedData struct { + // Monotonic revision now available for the run. + Revision int64 `json:"revision"` + RunID string `json:"runId"` +} + +func (*FactoryRunUpdatedData) sessionEventData() {} +func (*FactoryRunUpdatedData) Type() SessionEventType { return SessionEventTypeFactoryRunUpdated } + // Ephemeral progress update from a running hook process type HookProgressData struct { // Human-readable progress message from the hook process @@ -733,6 +764,7 @@ type ModelCallFailureData struct { ReasoningEffort *string `json:"reasoningEffort,omitempty"` // Content-free structural summary of the failing request. Contains only counts and shape flags (no prompt content), so it is safe for unrestricted telemetry. Populated only for client-error (4xx) failures. RequestFingerprint *ModelCallFailureRequestFingerprint `json:"requestFingerprint,omitempty"` + Rte *bool `json:"rte,omitempty"` // Copilot service request ID (x-copilot-service-request-id header) for CAPI log correlation ServiceRequestID *string `json:"serviceRequestId,omitempty"` // Where the failed model call originated @@ -818,6 +850,8 @@ type AssistantUsageData struct { Initiator *string `json:"initiator,omitempty"` // Number of input tokens consumed InputTokens *int64 `json:"inputTokens,omitempty"` + // Coarse classification of the interaction that produced this call, mirroring the session's per-request agent context (e.g. `conversation-agent`, `conversation-subagent`, `conversation-sampling`, `conversation-background`, `conversation-compaction`, `conversation-user`). Non-billing; lets consumers attribute a model call to a call class (e.g. sub-agent/sidekick) independently of the billing initiator. Absent when the runtime did not classify the request. + InteractionType *string `json:"interactionType,omitempty"` // Average inter-token latency in milliseconds. Only available for streaming requests InterTokenLatencyMs *float64 `json:"interTokenLatencyMs,omitempty"` // Model identifier used for this API call @@ -836,6 +870,7 @@ type AssistantUsageData struct { ReasoningEffort *string `json:"reasoningEffort,omitempty"` // Number of output tokens used for reasoning (e.g., chain-of-thought) ReasoningTokens *int64 `json:"reasoningTokens,omitempty"` + Rte *bool `json:"rte,omitempty"` // Copilot service request ID (x-copilot-service-request-id header) for CAPI log correlation ServiceRequestID *string `json:"serviceRequestId,omitempty"` // Time to first token in milliseconds. Only available for streaming requests @@ -926,6 +961,9 @@ func (*AssistantTurnRetryData) Type() SessionEventType { return SessionEventType type ModelCallStartData struct { // Model identifier used for this API call, when known Model *string `json:"model,omitempty"` + // Previous response or interaction identifier included in the model request, when present + // Internal: PreviousResponseID is part of the SDK's internal API surface and is not intended for external use. + PreviousResponseID *string `json:"previousResponseId,omitempty"` // Identifier of the assistant turn that initiated the model call TurnID string `json:"turnId"` } @@ -1213,7 +1251,7 @@ type UserMessageData struct { NativeDocumentPathFallbackPaths []string `json:"nativeDocumentPathFallbackPaths,omitzero"` // Parent agent task ID for background telemetry correlated to this user turn ParentAgentTaskID *string `json:"parentAgentTaskId,omitempty"` - // Origin of this message, used for timeline filtering (e.g., "skill-pdf" for skill-injected messages that should be hidden from the user) + // Origin of this message, used for timeline filtering and attribution (e.g., `skill-pdf` for hidden skill injection or `agent-` for an inter-agent prompt) Source *string `json:"source,omitempty"` // Normalized document MIME types that were sent natively instead of through tagged_files XML SupportedNativeDocumentMIMETypes []string `json:"supportedNativeDocumentMimeTypes,omitzero"` @@ -1247,6 +1285,8 @@ type PermissionRequestedData struct { RequestID string `json:"requestId"` // When true, this permission was already resolved by a permissionRequest hook and requires no client action ResolvedByHook *bool `json:"resolvedByHook,omitempty"` + // Neutral risk metadata supplied by the tool host. Consumers may display this value but must not use it to bypass the permission decision. + RiskAssessment any `json:"riskAssessment,omitempty"` } func (*PermissionRequestedData) sessionEventData() {} @@ -1438,6 +1478,8 @@ type SessionScheduleCreatedData struct { ID int64 `json:"id"` // Interval between ticks in milliseconds (relative-interval schedules) IntervalMs *int64 `json:"intervalMs,omitempty"` + // Who created the schedule (`user` or `model`). Persisted so a resumed session keeps gating non-user schedules from firing skills that opted out of model invocation. Absent on entries created before this field existed; a missing origin fails closed (treated the same as a non-user origin), so such a schedule may not resolve a `disable-model-invocation` skill. + Origin *ScheduleOrigin `json:"origin,omitempty"` // Prompt text that gets enqueued on every tick Prompt string `json:"prompt"` // Whether the schedule re-arms after each tick (`/every`) or fires once (`/after`) @@ -1580,7 +1622,7 @@ type SessionResumeData struct { Context *WorkingDirectoryContext `json:"context,omitempty"` // Context tier currently selected at resume time; null when no tier is active ContextTier *ContextTier `json:"contextTier,omitempty"` - // When true, tool calls and permission requests left in flight by the previous session lifetime remain pending after resume and the agentic loop awaits their results. User sends are queued behind the pending work until all such requests reach a terminal state. When false (the default), any such tool calls and permission requests are immediately marked as interrupted on resume. + // When true, tool calls and permission requests left in flight by the previous session lifetime remain pending after resume and the agentic loop awaits their results. User sends are queued behind the pending work until all such requests reach a terminal state. When false or omitted, pending work is normally marked as interrupted unless the resume passively joined live work owned by another client; sessionWasActive distinguishes that case. ContinuePendingWork *bool `json:"continuePendingWork,omitempty"` // Total number of persisted events in the session at the time of resume EventCount int64 `json:"eventCount"` @@ -1598,7 +1640,7 @@ type SessionResumeData struct { SelectedModel *string `json:"selectedModel,omitempty"` // Session limits currently configured at resume time; null when no limits are active SessionLimits *SessionLimitsConfig `json:"sessionLimits,omitempty"` - // True when this resume attached to a session that the runtime already had running in-memory (for example, an extension joining a session another client was actively driving). False (or omitted) for cold resumes — the runtime had to reconstitute the session from its persisted event log. + // True when this resume passively joined a session that already had live work running in the runtime - an agent turn, a native queue run, a queued resume continuation, or an in-flight send (for example, an extension joining a session another client was actively driving). False (or omitted) when the session had no live work or when the resume explicitly abandoned pending work, including cold resumes and suspended sessions that remain resident in memory. SessionWasActive *bool `json:"sessionWasActive,omitempty"` // Output verbosity level used for model calls, if applicable (e.g. "low", "medium", "high") Verbosity *Verbosity `json:"verbosity,omitempty"` @@ -1861,6 +1903,8 @@ func (*SystemNotificationData) Type() SessionEventType { return SessionEventType type SystemMessageData struct { // The system or developer prompt text sent as model input Content string `json:"content"` + // Logical interaction identifier for the model run receiving this prompt + InteractionID *string `json:"interactionId,omitempty"` // Metadata about the prompt template and its construction Metadata *SystemMessageMetadata `json:"metadata,omitempty"` // Optional name identifier for the message source @@ -1901,6 +1945,7 @@ type ToolExecutionCompleteData struct { ParentToolCallID *string `json:"parentToolCallId,omitempty"` // Tool execution result on success Result *ToolExecutionCompleteResult `json:"result,omitempty"` + Rte *bool `json:"rte,omitempty"` // Whether this tool execution ran inside a sandbox container Sandboxed *bool `json:"sandboxed,omitempty"` // Whether the tool execution completed successfully @@ -1948,6 +1993,7 @@ type ToolExecutionStartData struct { // 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"` + Rte *bool `json:"rte,omitempty"` // Shell-tool path hints derived from the command at start time for shell tools (bash/powershell/local_shell). Produced by the same shell-aware extractor as PermissionRequestShell.possiblePaths, so it is present even when the command is auto-approved and no permission request fires. Absent for non-shell tools. ShellToolInfo *ToolExecutionStartShellToolInfo `json:"shellToolInfo,omitempty"` // Unique identifier for this tool call @@ -2055,6 +2101,19 @@ type ToolUserRequestedData struct { func (*ToolUserRequestedData) sessionEventData() {} func (*ToolUserRequestedData) Type() SessionEventType { return SessionEventTypeToolUserRequested } +// Verbose MCP diagnostic data. Payloads are redacted and size-capped; `truncated` indicates that a captured payload was clipped. This event is ephemeral and high-volume, and is emitted only when a consumer has registered interest in `mcp.diagnostic` via `session.eventLog.registerInterest`. +type MCPDiagnosticData struct { + // Detailed diagnostic payload discriminated by `kind`. + Detail MCPDiagnosticDetail `json:"detail"` + // Configured name of the MCP server associated with this diagnostic. + ServerName string `json:"serverName"` + // Transport mechanism used by the MCP server: stdio, HTTP, Server-Sent Events, or in-memory. + Transport MCPServerTransport `json:"transport"` +} + +func (*MCPDiagnosticData) sessionEventData() {} +func (*MCPDiagnosticData) Type() SessionEventType { return SessionEventTypeMCPDiagnostic } + // Warning message for timeline display with categorization type SessionWarningData struct { // Human-readable warning message for display in the timeline @@ -2495,6 +2554,104 @@ type MCPAppToolCallCompleteToolMetaUI struct { Visibility []string `json:"visibility,omitzero"` } +// Detailed MCP diagnostic payload discriminated by `kind`. +type MCPDiagnosticDetail interface { + mcpDiagnosticDetail() + Kind() MCPDiagnosticDetailKind +} + +type RawMCPDiagnosticDetail struct { + Discriminator MCPDiagnosticDetailKind + Raw json.RawMessage +} + +func (RawMCPDiagnosticDetail) mcpDiagnosticDetail() {} +func (r RawMCPDiagnosticDetail) Kind() MCPDiagnosticDetailKind { + return r.Discriminator +} + +// A redacted HTTP request or response observed while communicating with an MCP server. +type MCPDiagnosticHTTPExchange struct { + // Elapsed duration in milliseconds for the HTTP exchange, when measured. + DurationMs *int64 `json:"durationMs,omitempty"` + // Redacted HTTP headers as name-to-value pairs, when available. + Headers map[string]string `json:"headers,omitzero"` + // HTTP method used by the request, when available. + HTTPMethod *string `json:"httpMethod,omitempty"` + // Whether this diagnostic describes the outgoing HTTP request or incoming HTTP response. + Phase MCPDiagnosticHTTPExchangePhase `json:"phase"` + // Whether the exchange belongs to the Server-Sent Events stream leg, when applicable. + SSE *bool `json:"sse,omitempty"` + // HTTP response status code, when this diagnostic is associated with a response. + StatusCode *int32 `json:"statusCode,omitempty"` + // Redacted HTTP URL, when available. + URL *string `json:"url,omitempty"` +} + +func (MCPDiagnosticHTTPExchange) mcpDiagnosticDetail() {} +func (MCPDiagnosticHTTPExchange) Kind() MCPDiagnosticDetailKind { + return MCPDiagnosticDetailKindHTTPExchange +} + +// MCP server process startup or termination details. +type MCPDiagnosticProcessLifecycle struct { + // Arguments used to launch the MCP server process, when applicable. + Args []string `json:"args,omitzero"` + // Command used to launch the MCP server process, when applicable. + Command *string `json:"command,omitempty"` + // Names of environment variables supplied to the MCP server process. Values are never included. + EnvKeys []string `json:"envKeys,omitzero"` + // Process exit code, when the MCP server process has exited normally. + ExitCode *int64 `json:"exitCode,omitempty"` + // Operating-system process ID of the MCP server process, when known. + Pid *int64 `json:"pid,omitempty"` + // Signal that terminated the MCP server process, when applicable. + Signal *string `json:"signal,omitempty"` +} + +func (MCPDiagnosticProcessLifecycle) mcpDiagnosticDetail() {} +func (MCPDiagnosticProcessLifecycle) Kind() MCPDiagnosticDetailKind { + return MCPDiagnosticDetailKindProcessLifecycle +} + +// A line emitted by an MCP server process. +type MCPDiagnosticServerLog struct { + // One server log line, without a trailing line terminator. + Line string `json:"line"` + // Operating-system process ID of the MCP server process, when known. + Pid *int64 `json:"pid,omitempty"` + // Process output stream that emitted the line. + Stream MCPDiagnosticServerLogStream `json:"stream"` +} + +func (MCPDiagnosticServerLog) mcpDiagnosticDetail() {} +func (MCPDiagnosticServerLog) Kind() MCPDiagnosticDetailKind { + return MCPDiagnosticDetailKindServerLog +} + +// A redacted MCP protocol message observed on the server transport. +type MCPDiagnosticWireMessage struct { + // Size in bytes of the observed message before any diagnostic payload clipping. + ByteSize int64 `json:"byteSize"` + // Whether the runtime sent the message to the server or received it from the server. + Direction MCPDiagnosticWireMessageDirection `json:"direction"` + // MCP protocol message category. + MessageKind MCPDiagnosticWireMessageKind `json:"messageKind"` + // MCP method name, when the diagnostic message is associated with a method. + Method *string `json:"method,omitempty"` + // Already-serialized JSON payload after redaction. Omitted when payload capture is unavailable or excluded; when present, it may be clipped as indicated by `truncated`. + Payload *string `json:"payload,omitempty"` + // JSON-RPC request ID serialized as a string, when the diagnostic message is associated with a request or response. + RequestID *string `json:"requestId,omitempty"` + // Whether the captured `payload` was clipped to the diagnostic size cap. + Truncated bool `json:"truncated"` +} + +func (MCPDiagnosticWireMessage) mcpDiagnosticDetail() {} +func (MCPDiagnosticWireMessage) Kind() MCPDiagnosticDetailKind { + return MCPDiagnosticDetailKindWireMessage +} + // Raw HTTP response details from the OAuth auth challenge, as observed by the runtime. type MCPOauthHTTPResponse struct { // Complete UTF-8 response body for host-specific challenge handling, including an empty string for an empty body. Omitted when the complete body is not valid UTF-8; body read failures fail the HTTP operation rather than exposing a partial response. @@ -2566,6 +2723,10 @@ type ModelCallFailureRequestFingerprint struct { // Auto-approval judge information attached to a permission request. Present (non-null) only when the session's allow-all mode is "auto"; its absence means auto mode was off and the judge did not evaluate the request. The `recommendation` conveys the judge's disposition for this request. // Experimental: PermissionAutoApproval is part of an experimental API and may change or be removed. type PermissionAutoApproval struct { + // Classified cause of an `error` recommendation. Absent for every other recommendation. + FailureReason *AutoApprovalJudgeFailureReason `json:"failureReason,omitempty"` + // Model id that produced the recommendation, when the judge was consulted and reported one. Absent for `excluded` (the judge was not consulted) and for failures that occurred before a model was selected. + Model *string `json:"model,omitempty"` // Human-readable reason for the judge's recommendation, when available. Reason *string `json:"reason,omitempty"` // The auto-approval safety judge's outcome for this request. @@ -2601,6 +2762,8 @@ type PermissionPromptRequestCommands struct { FullCommandText string `json:"fullCommandText"` // Human-readable description of what the command intends to do Intention string `json:"intention"` + // Whether managed policy requires a human response and forbids host auto-approval + ManagedApprovalRequired *bool `json:"managedApprovalRequired,omitempty"` // Tool call ID that triggered this permission request ToolCallID *string `json:"toolCallId,omitempty"` // Optional warning message about risks of running this command @@ -2761,6 +2924,8 @@ type PermissionPromptRequestRead struct { AutoApproval *PermissionAutoApproval `json:"autoApproval,omitempty"` // Human-readable description of why the file is being read Intention string `json:"intention"` + // Whether managed policy requires a human response and forbids host auto-approval + ManagedApprovalRequired *bool `json:"managedApprovalRequired,omitempty"` // Path of the file or directory being read Path string `json:"path"` // Tool call ID that triggered this permission request @@ -2779,6 +2944,8 @@ type PermissionPromptRequestURL struct { AutoApproval *PermissionAutoApproval `json:"autoApproval,omitempty"` // Human-readable description of why the URL is being accessed Intention string `json:"intention"` + // Whether managed policy requires a human response and forbids host auto-approval + ManagedApprovalRequired *bool `json:"managedApprovalRequired,omitempty"` // True when this URL fetch is requesting to bypass the sandbox network policy: either the model set requestSandboxBypass: true, or the tool re-issued the request as an interactive bypass after the network policy denied the approved URL (host opted in via sandbox.allowBypass). This is a request, not a grant: the fetch runs only if the user approves this permission request. Hosts should highlight the elevated risk in the approval UI. RequestSandboxBypass *bool `json:"requestSandboxBypass,omitempty"` // Model-provided justification for the sandbox-bypass request. Only meaningful when requestSandboxBypass is true. @@ -2807,6 +2974,8 @@ type PermissionPromptRequestWrite struct { FileName string `json:"fileName"` // Human-readable description of the intended file change Intention string `json:"intention"` + // Whether managed policy requires a human response and forbids host auto-approval + ManagedApprovalRequired *bool `json:"managedApprovalRequired,omitempty"` // Complete new file contents for newly created files NewFileContents *string `json:"newFileContents,omitempty"` // Tool call ID that triggered this permission request @@ -2946,6 +3115,8 @@ func (PermissionRequestMemory) Kind() PermissionRequestKind { type PermissionRequestRead struct { // Human-readable description of why the file is being read Intention string `json:"intention"` + // Whether managed policy requires a human response and forbids host auto-approval + ManagedApprovalRequired *bool `json:"managedApprovalRequired,omitempty"` // Path of the file or directory being read Path string `json:"path"` // True when the model has requested to run this search outside the sandbox (it set requestSandboxBypass: true and the host opted in via sandbox.allowBypass). This is a request, not a grant: the search runs unsandboxed only if the user approves this permission request. Hosts should highlight the elevated risk in the approval UI. @@ -2967,12 +3138,16 @@ type PermissionRequestShell struct { CanOfferSessionApproval bool `json:"canOfferSessionApproval"` // Parsed command identifiers found in the command text Commands []PermissionRequestShellCommand `json:"commands"` + // Parsed command segments, including arguments, used for managed policy matching + CommandSegments []PermissionRequestShellCommandSegment `json:"commandSegments,omitzero"` // The complete shell command text to be executed FullCommandText string `json:"fullCommandText"` // Whether the command includes a file write redirection (e.g., > or >>) HasWriteFileRedirection bool `json:"hasWriteFileRedirection"` // Human-readable description of what the command intends to do Intention string `json:"intention"` + // Whether managed policy requires a human response and forbids host auto-approval + ManagedApprovalRequired *bool `json:"managedApprovalRequired,omitempty"` // File paths that may be read or written by the command PossiblePaths []string `json:"possiblePaths"` // URLs that may be accessed by the command @@ -2996,6 +3171,8 @@ func (PermissionRequestShell) Kind() PermissionRequestKind { type PermissionRequestURL struct { // Human-readable description of why the URL is being accessed Intention string `json:"intention"` + // Whether managed policy requires a human response and forbids host auto-approval + ManagedApprovalRequired *bool `json:"managedApprovalRequired,omitempty"` // True when this URL fetch is requesting to bypass the sandbox network policy: either the model set requestSandboxBypass: true, or the tool re-issued the request as an interactive bypass after the network policy denied the approved URL (host opted in via sandbox.allowBypass). This is a request, not a grant: the fetch runs only if the user approves this permission request. Hosts should highlight the elevated risk in the approval UI. RequestSandboxBypass *bool `json:"requestSandboxBypass,omitempty"` // Model-provided justification for the sandbox-bypass request. Only meaningful when requestSandboxBypass is true. @@ -3021,6 +3198,8 @@ type PermissionRequestWrite struct { FileName string `json:"fileName"` // Human-readable description of the intended file change Intention string `json:"intention"` + // Whether managed policy requires a human response and forbids host auto-approval + ManagedApprovalRequired *bool `json:"managedApprovalRequired,omitempty"` // Complete new file contents for newly created files NewFileContents *string `json:"newFileContents,omitempty"` // True when a built-in file tool (apply_patch / str_replace_editor) asked to write a path the sandbox filesystem policy would block, and the host opted in via sandbox.allowBypass. This is a request, not a grant: the write happens unsandboxed only if the user approves this permission request. Hosts should highlight the elevated risk in the approval UI. @@ -3044,6 +3223,14 @@ type PermissionRequestShellCommand struct { ReadOnly bool `json:"readOnly"` } +// A parsed shell command segment used for argument-aware managed policy matching. +type PermissionRequestShellCommandSegment struct { + // Full text of this command segment, including arguments + FullCommandText string `json:"fullCommandText"` + // Command identifier (e.g., executable name) + Identifier string `json:"identifier"` +} + // A URL that may be accessed by a command in a shell permission request. type PermissionRequestShellPossibleURL struct { // URL that may be accessed by the command @@ -3460,6 +3647,17 @@ func (SystemNotificationShellDetachedCompleted) Type() SystemNotificationType { return SystemNotificationTypeShellDetachedCompleted } +// System notification metadata from an external host that does not match a runtime-owned notification kind. +type SystemNotificationUnclassified struct { + // Opaque metadata supplied by the external host, when present. + Metadata any `json:"metadata,omitempty"` +} + +func (SystemNotificationUnclassified) systemNotification() {} +func (SystemNotificationUnclassified) Type() SystemNotificationType { + return SystemNotificationTypeUnclassified +} + // A content block within a tool result, which may be text, terminal output, image, audio, or a resource type ToolExecutionCompleteContent interface { toolExecutionCompleteContent() @@ -3808,6 +4006,23 @@ const ( AssistantUsageAPIEndpointWsResponses AssistantUsageAPIEndpoint = "ws:/responses" ) +// Why the auto-approval judge produced no usable recommendation. Present only alongside an `error` recommendation, where the human-readable reason is a fixed string and therefore cannot distinguish these cases. Intended to make a judge failure reportable by a consumer that has no access to the host's logs. +// Experimental: AutoApprovalJudgeFailureReason is part of an experimental API and may change or be removed. +type AutoApprovalJudgeFailureReason string + +const ( + // The judge model call was cancelled before it returned. + AutoApprovalJudgeFailureReasonAbort AutoApprovalJudgeFailureReason = "abort" + // The judge model call completed but returned no content. + AutoApprovalJudgeFailureReasonEmptyResponse AutoApprovalJudgeFailureReason = "empty_response" + // The judge model call failed (for example a transport, authentication, or rate-limit error). + AutoApprovalJudgeFailureReasonModelError AutoApprovalJudgeFailureReason = "model_error" + // The judge model replied, but the reply carried no ALLOW/DENY verdict. + AutoApprovalJudgeFailureReasonParseError AutoApprovalJudgeFailureReason = "parse_error" + // The judge model call exceeded its deadline. + AutoApprovalJudgeFailureReasonTimeout AutoApprovalJudgeFailureReason = "timeout" +) + // Outcome of the auto-approval safety judge for a permission request. Present only when auto mode is enabled; its absence means the judge did not evaluate the request (auto mode was off). // Experimental: AutoApprovalRecommendation is part of an experimental API and may change or be removed. type AutoApprovalRecommendation string @@ -3916,6 +4131,22 @@ const ( CitationProviderOpenai CitationProvider = "openai" ) +// What initiated a conversation compaction +type CompactionTrigger string + +const ( + // Compaction forced by a context-limit model response (e.g. HTTP 413) before retrying the request. + CompactionTriggerContextLimitRetry CompactionTrigger = "context_limit_retry" + // User-requested compaction, e.g. the /compact command or the history.compact API. + CompactionTriggerManual CompactionTrigger = "manual" + // Emergency compaction triggered by high process memory usage. + CompactionTriggerMemoryPressure CompactionTrigger = "memory_pressure" + // Compaction requested while switching to a model with a smaller context window. + CompactionTriggerModelSwitch CompactionTrigger = "model_switch" + // Background compaction started automatically because context utilization crossed the background threshold. + CompactionTriggerThreshold CompactionTrigger = "threshold" +) + // The user action: "accept" (submitted form), "decline" (explicitly refused), or "cancel" (dismissed) type ElicitationCompletedAction string @@ -4033,6 +4264,58 @@ const ( ManagedSettingsResolvedSourceServer ManagedSettingsResolvedSource = "server" ) +// Kind discriminator for MCPDiagnosticDetail. +type MCPDiagnosticDetailKind string + +const ( + MCPDiagnosticDetailKindHTTPExchange MCPDiagnosticDetailKind = "http_exchange" + MCPDiagnosticDetailKindProcessLifecycle MCPDiagnosticDetailKind = "process_lifecycle" + MCPDiagnosticDetailKindServerLog MCPDiagnosticDetailKind = "server_log" + MCPDiagnosticDetailKindWireMessage MCPDiagnosticDetailKind = "wire_message" +) + +// Phase of an observed MCP HTTP exchange. +type MCPDiagnosticHTTPExchangePhase string + +const ( + // The outgoing HTTP request. + MCPDiagnosticHTTPExchangePhaseRequest MCPDiagnosticHTTPExchangePhase = "request" + // The incoming HTTP response. + MCPDiagnosticHTTPExchangePhaseResponse MCPDiagnosticHTTPExchangePhase = "response" +) + +// Output stream for an MCP server log line. +type MCPDiagnosticServerLogStream string + +const ( + // The process standard-error stream. + MCPDiagnosticServerLogStreamStderr MCPDiagnosticServerLogStream = "stderr" +) + +// Direction of an MCP wire message. +type MCPDiagnosticWireMessageDirection string + +const ( + // The runtime received the message from the MCP server. + MCPDiagnosticWireMessageDirectionInbound MCPDiagnosticWireMessageDirection = "inbound" + // The runtime sent the message to the MCP server. + MCPDiagnosticWireMessageDirectionOutbound MCPDiagnosticWireMessageDirection = "outbound" +) + +// Category of an MCP wire message. +type MCPDiagnosticWireMessageKind string + +const ( + // A protocol or transport error represented as a diagnostic message. + MCPDiagnosticWireMessageKindError MCPDiagnosticWireMessageKind = "error" + // A JSON-RPC notification. + MCPDiagnosticWireMessageKindNotification MCPDiagnosticWireMessageKind = "notification" + // A JSON-RPC request. + MCPDiagnosticWireMessageKindRequest MCPDiagnosticWireMessageKind = "request" + // A JSON-RPC response. + MCPDiagnosticWireMessageKindResponse MCPDiagnosticWireMessageKind = "response" +) + // How the pending MCP headers refresh request resolved. type MCPHeadersRefreshCompletedOutcome string @@ -4278,6 +4561,16 @@ const ( PlanChangedOperationUpdate PlanChangedOperation = "update" ) +// Who created the schedule: `user` (an explicit user action such as `/every` or `/after`) or `model` (the agent via the `manage_schedule` tool). Gates whether a scheduled skill that opted out of model invocation may fire: only user-created schedules may. +type ScheduleOrigin string + +const ( + // The schedule was created by the agent via the `manage_schedule` tool. + ScheduleOriginModel ScheduleOrigin = "model" + // The schedule was created by an explicit user action, such as `/every` or `/after`. + ScheduleOriginUser ScheduleOrigin = "user" +) + // User action selected for an exhausted session limit. type SessionLimitsExhaustedResponseAction string @@ -4334,6 +4627,7 @@ const ( SystemNotificationTypeNewInboxMessage SystemNotificationType = "new_inbox_message" SystemNotificationTypeShellCompleted SystemNotificationType = "shell_completed" SystemNotificationTypeShellDetachedCompleted SystemNotificationType = "shell_detached_completed" + SystemNotificationTypeUnclassified SystemNotificationType = "unclassified" ) // Theme variant this icon is intended for diff --git a/go/zsession_events.go b/go/zsession_events.go index 1d35a0a518..2353fc771e 100644 --- a/go/zsession_events.go +++ b/go/zsession_events.go @@ -53,6 +53,7 @@ type ( AttachmentSelectionDetailsEnd = rpc.AttachmentSelectionDetailsEnd AttachmentSelectionDetailsStart = rpc.AttachmentSelectionDetailsStart AttachmentType = rpc.AttachmentType + AutoApprovalJudgeFailureReason = rpc.AutoApprovalJudgeFailureReason AutoApprovalRecommendation = rpc.AutoApprovalRecommendation AutoModeResolvedReasoningBucket = rpc.AutoModeResolvedReasoningBucket AutoModeSwitchCompletedData = rpc.AutoModeSwitchCompletedData @@ -85,6 +86,7 @@ type ( CommandsChangedData = rpc.CommandsChangedData CompactionCompleteCompactionTokensUsed = rpc.CompactionCompleteCompactionTokensUsed CompactionCompleteCompactionTokensUsedCopilotUsageTokenDetail = rpc.CompactionCompleteCompactionTokensUsedCopilotUsageTokenDetail + CompactionTrigger = rpc.CompactionTrigger ContextTier = rpc.ContextTier CustomAgentsUpdatedAgent = rpc.CustomAgentsUpdatedAgent ElicitationCompletedAction = rpc.ElicitationCompletedAction @@ -103,6 +105,7 @@ type ( ExtensionsLoadedExtensionStatus = rpc.ExtensionsLoadedExtensionStatus ExternalToolCompletedData = rpc.ExternalToolCompletedData ExternalToolRequestedData = rpc.ExternalToolRequestedData + FactoryRunUpdatedData = rpc.FactoryRunUpdatedData GitHubRepoRef = rpc.GitHubRepoRef HandoffRepository = rpc.HandoffRepository HandoffSourceType = rpc.HandoffSourceType @@ -118,6 +121,17 @@ type ( MCPAppToolCallCompleteError = rpc.MCPAppToolCallCompleteError MCPAppToolCallCompleteToolMeta = rpc.MCPAppToolCallCompleteToolMeta MCPAppToolCallCompleteToolMetaUI = rpc.MCPAppToolCallCompleteToolMetaUI + MCPDiagnosticData = rpc.MCPDiagnosticData + MCPDiagnosticDetail = rpc.MCPDiagnosticDetail + MCPDiagnosticDetailKind = rpc.MCPDiagnosticDetailKind + MCPDiagnosticHTTPExchange = rpc.MCPDiagnosticHTTPExchange + MCPDiagnosticHTTPExchangePhase = rpc.MCPDiagnosticHTTPExchangePhase + MCPDiagnosticProcessLifecycle = rpc.MCPDiagnosticProcessLifecycle + MCPDiagnosticServerLog = rpc.MCPDiagnosticServerLog + MCPDiagnosticServerLogStream = rpc.MCPDiagnosticServerLogStream + MCPDiagnosticWireMessage = rpc.MCPDiagnosticWireMessage + MCPDiagnosticWireMessageDirection = rpc.MCPDiagnosticWireMessageDirection + MCPDiagnosticWireMessageKind = rpc.MCPDiagnosticWireMessageKind MCPHeadersRefreshCompletedData = rpc.MCPHeadersRefreshCompletedData MCPHeadersRefreshCompletedOutcome = rpc.MCPHeadersRefreshCompletedOutcome MCPHeadersRefreshRequiredData = rpc.MCPHeadersRefreshRequiredData @@ -189,6 +203,7 @@ type ( PermissionRequestRead = rpc.PermissionRequestRead PermissionRequestShell = rpc.PermissionRequestShell PermissionRequestShellCommand = rpc.PermissionRequestShellCommand + PermissionRequestShellCommandSegment = rpc.PermissionRequestShellCommandSegment PermissionRequestShellPossibleURL = rpc.PermissionRequestShellPossibleURL PermissionRequestURL = rpc.PermissionRequestURL PermissionRequestWrite = rpc.PermissionRequestWrite @@ -202,6 +217,7 @@ type ( PlanChangedOperation = rpc.PlanChangedOperation PossibleURL = rpc.PossibleURL RawCitationLocation = rpc.RawCitationLocation + RawMCPDiagnosticDetail = rpc.RawMCPDiagnosticDetail RawPermissionPromptRequest = rpc.RawPermissionPromptRequest RawPermissionRequest = rpc.RawPermissionRequest RawPermissionResult = rpc.RawPermissionResult @@ -212,6 +228,7 @@ type ( ReasoningSummary = rpc.ReasoningSummary SamplingCompletedData = rpc.SamplingCompletedData SamplingRequestedData = rpc.SamplingRequestedData + ScheduleOrigin = rpc.ScheduleOrigin SessionAutoModeResolvedData = rpc.SessionAutoModeResolvedData SessionAutopilotObjectiveChangedData = rpc.SessionAutopilotObjectiveChangedData SessionBackgroundTasksChangedData = rpc.SessionBackgroundTasksChangedData @@ -298,6 +315,7 @@ type ( SystemNotificationShellCompleted = rpc.SystemNotificationShellCompleted SystemNotificationShellDetachedCompleted = rpc.SystemNotificationShellDetachedCompleted SystemNotificationType = rpc.SystemNotificationType + SystemNotificationUnclassified = rpc.SystemNotificationUnclassified ToolExecutionCompleteContent = rpc.ToolExecutionCompleteContent ToolExecutionCompleteContentAudio = rpc.ToolExecutionCompleteContentAudio ToolExecutionCompleteContentImage = rpc.ToolExecutionCompleteContentImage @@ -386,6 +404,11 @@ const ( AttachmentTypeGitHubTreeComparison = rpc.AttachmentTypeGitHubTreeComparison AttachmentTypeGitHubURL = rpc.AttachmentTypeGitHubURL AttachmentTypeSelection = rpc.AttachmentTypeSelection + AutoApprovalJudgeFailureReasonAbort = rpc.AutoApprovalJudgeFailureReasonAbort + AutoApprovalJudgeFailureReasonEmptyResponse = rpc.AutoApprovalJudgeFailureReasonEmptyResponse + AutoApprovalJudgeFailureReasonModelError = rpc.AutoApprovalJudgeFailureReasonModelError + AutoApprovalJudgeFailureReasonParseError = rpc.AutoApprovalJudgeFailureReasonParseError + AutoApprovalJudgeFailureReasonTimeout = rpc.AutoApprovalJudgeFailureReasonTimeout AutoApprovalRecommendationApprove = rpc.AutoApprovalRecommendationApprove AutoApprovalRecommendationError = rpc.AutoApprovalRecommendationError AutoApprovalRecommendationExcluded = rpc.AutoApprovalRecommendationExcluded @@ -413,6 +436,11 @@ const ( CitationProviderAnthropic = rpc.CitationProviderAnthropic CitationProviderClient = rpc.CitationProviderClient CitationProviderOpenai = rpc.CitationProviderOpenai + CompactionTriggerContextLimitRetry = rpc.CompactionTriggerContextLimitRetry + CompactionTriggerManual = rpc.CompactionTriggerManual + CompactionTriggerMemoryPressure = rpc.CompactionTriggerMemoryPressure + CompactionTriggerModelSwitch = rpc.CompactionTriggerModelSwitch + CompactionTriggerThreshold = rpc.CompactionTriggerThreshold ContextTierDefault = rpc.ContextTierDefault ContextTierLongContext = rpc.ContextTierLongContext ElicitationCompletedActionAccept = rpc.ElicitationCompletedActionAccept @@ -444,6 +472,19 @@ const ( ManagedSettingsResolvedSourceDevice = rpc.ManagedSettingsResolvedSourceDevice ManagedSettingsResolvedSourceNone = rpc.ManagedSettingsResolvedSourceNone ManagedSettingsResolvedSourceServer = rpc.ManagedSettingsResolvedSourceServer + MCPDiagnosticDetailKindHTTPExchange = rpc.MCPDiagnosticDetailKindHTTPExchange + MCPDiagnosticDetailKindProcessLifecycle = rpc.MCPDiagnosticDetailKindProcessLifecycle + MCPDiagnosticDetailKindServerLog = rpc.MCPDiagnosticDetailKindServerLog + MCPDiagnosticDetailKindWireMessage = rpc.MCPDiagnosticDetailKindWireMessage + MCPDiagnosticHTTPExchangePhaseRequest = rpc.MCPDiagnosticHTTPExchangePhaseRequest + MCPDiagnosticHTTPExchangePhaseResponse = rpc.MCPDiagnosticHTTPExchangePhaseResponse + MCPDiagnosticServerLogStreamStderr = rpc.MCPDiagnosticServerLogStreamStderr + MCPDiagnosticWireMessageDirectionInbound = rpc.MCPDiagnosticWireMessageDirectionInbound + MCPDiagnosticWireMessageDirectionOutbound = rpc.MCPDiagnosticWireMessageDirectionOutbound + MCPDiagnosticWireMessageKindError = rpc.MCPDiagnosticWireMessageKindError + MCPDiagnosticWireMessageKindNotification = rpc.MCPDiagnosticWireMessageKindNotification + MCPDiagnosticWireMessageKindRequest = rpc.MCPDiagnosticWireMessageKindRequest + MCPDiagnosticWireMessageKindResponse = rpc.MCPDiagnosticWireMessageKindResponse MCPHeadersRefreshCompletedOutcomeHeaders = rpc.MCPHeadersRefreshCompletedOutcomeHeaders MCPHeadersRefreshCompletedOutcomeNone = rpc.MCPHeadersRefreshCompletedOutcomeNone MCPHeadersRefreshCompletedOutcomeTimeout = rpc.MCPHeadersRefreshCompletedOutcomeTimeout @@ -534,6 +575,8 @@ const ( ReasoningSummaryConcise = rpc.ReasoningSummaryConcise ReasoningSummaryDetailed = rpc.ReasoningSummaryDetailed ReasoningSummaryNone = rpc.ReasoningSummaryNone + ScheduleOriginModel = rpc.ScheduleOriginModel + ScheduleOriginUser = rpc.ScheduleOriginUser SessionEventTypeAbort = rpc.SessionEventTypeAbort SessionEventTypeAssistantIdle = rpc.SessionEventTypeAssistantIdle SessionEventTypeAssistantIntent = rpc.SessionEventTypeAssistantIntent @@ -562,10 +605,12 @@ const ( SessionEventTypeExitPlanModeRequested = rpc.SessionEventTypeExitPlanModeRequested SessionEventTypeExternalToolCompleted = rpc.SessionEventTypeExternalToolCompleted SessionEventTypeExternalToolRequested = rpc.SessionEventTypeExternalToolRequested + SessionEventTypeFactoryRunUpdated = rpc.SessionEventTypeFactoryRunUpdated SessionEventTypeHookEnd = rpc.SessionEventTypeHookEnd SessionEventTypeHookProgress = rpc.SessionEventTypeHookProgress SessionEventTypeHookStart = rpc.SessionEventTypeHookStart SessionEventTypeMCPAppToolCallComplete = rpc.SessionEventTypeMCPAppToolCallComplete + SessionEventTypeMCPDiagnostic = rpc.SessionEventTypeMCPDiagnostic SessionEventTypeMCPHeadersRefreshCompleted = rpc.SessionEventTypeMCPHeadersRefreshCompleted SessionEventTypeMCPHeadersRefreshRequired = rpc.SessionEventTypeMCPHeadersRefreshRequired SessionEventTypeMCPOauthCompleted = rpc.SessionEventTypeMCPOauthCompleted @@ -676,6 +721,7 @@ const ( SystemNotificationTypeNewInboxMessage = rpc.SystemNotificationTypeNewInboxMessage SystemNotificationTypeShellCompleted = rpc.SystemNotificationTypeShellCompleted SystemNotificationTypeShellDetachedCompleted = rpc.SystemNotificationTypeShellDetachedCompleted + SystemNotificationTypeUnclassified = rpc.SystemNotificationTypeUnclassified ToolExecutionCompleteContentResourceLinkIconThemeDark = rpc.ToolExecutionCompleteContentResourceLinkIconThemeDark ToolExecutionCompleteContentResourceLinkIconThemeLight = rpc.ToolExecutionCompleteContentResourceLinkIconThemeLight ToolExecutionCompleteContentTypeAudio = rpc.ToolExecutionCompleteContentTypeAudio diff --git a/nodejs/examples/mcp-diagnostics-example.ts b/nodejs/examples/mcp-diagnostics-example.ts new file mode 100644 index 0000000000..78e4dbf97c --- /dev/null +++ b/nodejs/examples/mcp-diagnostics-example.ts @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { approveAll, CopilotClient } from "@github/copilot-sdk"; + +console.log("🚀 Starting MCP diagnostics example\n"); + +await using client = new CopilotClient({ logLevel: "info" }); +await using session = await client.createSession({ + onPermissionRequest: approveAll, + mcpServers: { + filesystem: { + type: "local", + command: "npx", + args: ["-y", "@modelcontextprotocol/server-filesystem", "."], + tools: ["*"], + }, + }, + onMcpDiagnostic: (diagnostic) => { + switch (diagnostic.detail.kind) { + case "wire_message": + console.log( + `↔️ ${diagnostic.serverName} ${diagnostic.detail.direction} ${diagnostic.detail.method ?? ""}` + ); + if (diagnostic.detail.truncated) { + console.log(" Captured payload was clipped."); + } + break; + case "http_exchange": + console.log( + `🌐 ${diagnostic.serverName} ${diagnostic.detail.phase} ${diagnostic.detail.statusCode ?? ""}` + ); + break; + case "server_log": + console.error(`📋 ${diagnostic.serverName}: ${diagnostic.detail.line}`); + break; + case "process_lifecycle": + console.log( + `⚙️ ${diagnostic.serverName} ${diagnostic.detail.command ?? ""} ${diagnostic.detail.exitCode ?? ""}` + ); + break; + } + }, +}); + +console.log(`✅ Session created: ${session.sessionId}\n`); +console.log("💬 Sending message..."); +const result = await session.sendAndWait("Use the filesystem MCP server to list files here."); +console.log("📝 Response:", result?.data.content); +console.log("✅ Done!"); diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index 61f4a99416..f111d31dac 100644 --- a/nodejs/src/client.ts +++ b/nodejs/src/client.ts @@ -1461,7 +1461,10 @@ export class CopilotClient { this.connection!, undefined, this.onGetTraceContext, - { mcpAuthHandler: config.onMcpAuthRequest } + { + mcpAuthHandler: config.onMcpAuthRequest, + mcpDiagnosticHandler: config.onMcpDiagnostic, + } ); s.registerTools(config.tools); s.registerCanvases(config.canvases); @@ -1624,6 +1627,12 @@ export class CopilotClient { eventType: "mcp.oauth_required", }); } + if (config.onMcpDiagnostic) { + await this.connection!.sendRequest("session.eventLog.registerInterest", { + sessionId: returnedSessionId, + eventType: "mcp.diagnostic", + }); + } session["_workspacePath"] = workspacePath; session.setCapabilities(capabilities); @@ -1674,7 +1683,10 @@ export class CopilotClient { this.connection!, undefined, this.onGetTraceContext, - { mcpAuthHandler: config.onMcpAuthRequest } + { + mcpAuthHandler: config.onMcpAuthRequest, + mcpDiagnosticHandler: config.onMcpDiagnostic, + } ); session.registerTools(config.tools); session.registerCanvases(config.canvases); @@ -1825,6 +1837,12 @@ export class CopilotClient { eventType: "mcp.oauth_required", }); } + if (config.onMcpDiagnostic) { + await this.connection!.sendRequest("session.eventLog.registerInterest", { + sessionId, + eventType: "mcp.diagnostic", + }); + } await this.updateSessionOptionsForMode(session, config); } catch (e) { diff --git a/nodejs/src/generated/session-events.ts b/nodejs/src/generated/session-events.ts index b9c9612b8f..e8b7c5f04a 100644 --- a/nodejs/src/generated/session-events.ts +++ b/nodejs/src/generated/session-events.ts @@ -85,6 +85,7 @@ export type SessionEvent = | McpOauthCompletedEvent | McpHeadersRefreshRequiredEvent | McpHeadersRefreshCompletedEvent + | McpDiagnosticEvent | CustomNotificationEvent | ExternalToolRequestedEvent | ExternalToolCompletedEvent @@ -104,6 +105,7 @@ export type SessionEvent = | ExitPlanModeCompletedEvent | ToolsUpdatedEvent | BackgroundTasksChangedEvent + | FactoryRunUpdatedEvent | SkillsLoadedEvent | CustomAgentsUpdatedEvent | McpServersLoadedEvent @@ -156,6 +158,14 @@ export type Verbosity = | "medium" /** A more detailed response was requested. */ | "high"; +/** + * Who created the schedule: `user` (an explicit user action such as `/every` or `/after`) or `model` (the agent via the `manage_schedule` tool). Gates whether a scheduled skill that opted out of model invocation may fire: only user-created schedules may. + */ +export type ScheduleOrigin = + /** The schedule was created by an explicit user action, such as `/every` or `/after`. */ + | "user" + /** The schedule was created by the agent via the `manage_schedule` tool. */ + | "model"; /** * The type of operation performed on the autopilot objective state file */ @@ -233,6 +243,20 @@ export type ShutdownType = | "routine" /** The session ended because of a crash or fatal error. */ | "error"; +/** + * What initiated a conversation compaction + */ +export type CompactionTrigger = + /** Background compaction started automatically because context utilization crossed the background threshold. */ + | "threshold" + /** Compaction forced by a context-limit model response (e.g. HTTP 413) before retrying the request. */ + | "context_limit_retry" + /** User-requested compaction, e.g. the /compact command or the history.compact API. */ + | "manual" + /** Emergency compaction triggered by high process memory usage. */ + | "memory_pressure" + /** Compaction requested while switching to a model with a smaller context window. */ + | "model_switch"; /** * The agent mode that was active when this message was sent */ @@ -475,7 +499,8 @@ export type SystemNotification = | SystemNotificationNewInboxMessage | SystemNotificationShellCompleted | SystemNotificationShellDetachedCompleted - | SystemNotificationInstructionDiscovered; + | SystemNotificationInstructionDiscovered + | SystemNotificationUnclassified; /** * Whether the agent completed successfully or failed */ @@ -529,6 +554,21 @@ export type PermissionPromptRequest = | PermissionPromptRequestHook | PermissionPromptRequestExtensionManagement | PermissionPromptRequestExtensionPermissionAccess; +/** + * Why the auto-approval judge produced no usable recommendation. Present only alongside an `error` recommendation, where the human-readable reason is a fixed string and therefore cannot distinguish these cases. Intended to make a judge failure reportable by a consumer that has no access to the host's logs. + */ +/** @experimental */ +export type AutoApprovalJudgeFailureReason = + /** The judge model call exceeded its deadline. */ + | "timeout" + /** The judge model call was cancelled before it returned. */ + | "abort" + /** The judge model call completed but returned no content. */ + | "empty_response" + /** The judge model call failed (for example a transport, authentication, or rate-limit error). */ + | "model_error" + /** The judge model replied, but the reply carried no ALLOW/DENY verdict. */ + | "parse_error"; /** * Outcome of the auto-approval safety judge for a permission request. Present only when auto mode is enabled; its absence means the judge did not evaluate the request (auto mode was off). */ @@ -635,6 +675,58 @@ export type McpHeadersRefreshCompletedOutcome = | "none" /** No response arrived within the bounded window. */ | "timeout"; +/** + * Detailed MCP diagnostic payload discriminated by `kind`. + */ +export type McpDiagnosticDetail = + | McpDiagnosticWireMessage + | McpDiagnosticHttpExchange + | McpDiagnosticServerLog + | McpDiagnosticProcessLifecycle; +/** + * Direction of an MCP wire message. + */ +export type McpDiagnosticWireMessageDirection = + /** The runtime sent the message to the MCP server. */ + | "outbound" + /** The runtime received the message from the MCP server. */ + | "inbound"; +/** + * Category of an MCP wire message. + */ +export type McpDiagnosticWireMessageKind = + /** A JSON-RPC request. */ + | "request" + /** A JSON-RPC response. */ + | "response" + /** A JSON-RPC notification. */ + | "notification" + /** A protocol or transport error represented as a diagnostic message. */ + | "error"; +/** + * Phase of an observed MCP HTTP exchange. + */ +export type McpDiagnosticHttpExchangePhase = + /** The outgoing HTTP request. */ + | "request" + /** The incoming HTTP response. */ + | "response"; +/** + * Output stream for an MCP server log line. + */ +export type McpDiagnosticServerLogStream = /** The process standard-error stream. */ "stderr"; +/** + * Transport mechanism: stdio, http, sse (deprecated), or memory (in-process MCP server) + */ +export type McpServerTransport = + /** Server communicates over stdio with a local child process. */ + | "stdio" + /** Server communicates over streamable HTTP. */ + | "http" + /** Server communicates over Server-Sent Events (deprecated). */ + | "sse" + /** Server is backed by an in-memory runtime implementation. */ + | "memory"; /** * The user's auto-mode-switch choice */ @@ -755,18 +847,6 @@ export type McpServerStatus = | "disabled" /** The server is not configured for this session. */ | "not_configured"; -/** - * Transport mechanism: stdio, http, sse (deprecated), or memory (in-process MCP server) - */ -export type McpServerTransport = - /** Server communicates over stdio with a local child process. */ - | "stdio" - /** Server communicates over streamable HTTP. */ - | "http" - /** Server communicates over Server-Sent Events (deprecated). */ - | "sse" - /** Server is backed by an in-memory runtime implementation. */ - | "memory"; /** * Discovery source */ @@ -962,7 +1042,7 @@ export interface ResumeData { */ contextTier?: ContextTier | null; /** - * When true, tool calls and permission requests left in flight by the previous session lifetime remain pending after resume and the agentic loop awaits their results. User sends are queued behind the pending work until all such requests reach a terminal state. When false (the default), any such tool calls and permission requests are immediately marked as interrupted on resume. + * When true, tool calls and permission requests left in flight by the previous session lifetime remain pending after resume and the agentic loop awaits their results. User sends are queued behind the pending work until all such requests reach a terminal state. When false or omitted, pending work is normally marked as interrupted unless the resume passively joined live work owned by another client; sessionWasActive distinguishes that case. */ continuePendingWork?: boolean; /** @@ -995,7 +1075,7 @@ export interface ResumeData { */ sessionLimits?: SessionLimitsConfig | null; /** - * True when this resume attached to a session that the runtime already had running in-memory (for example, an extension joining a session another client was actively driving). False (or omitted) for cold resumes — the runtime had to reconstitute the session from its persisted event log. + * True when this resume passively joined a session that already had live work running in the runtime - an agent turn, a native queue run, a queued resume continuation, or an in-flight send (for example, an extension joining a session another client was actively driving). False (or omitted) when the session had no live work or when the resume explicitly abandoned pending work, including cold resumes and suspended sessions that remain resident in memory. */ sessionWasActive?: boolean; verbosity?: Verbosity; @@ -1242,6 +1322,7 @@ export interface ScheduleCreatedData { * Interval between ticks in milliseconds (relative-interval schedules) */ intervalMs?: number; + origin?: ScheduleOrigin; /** * Prompt text that gets enqueued on every tick */ @@ -2363,6 +2444,10 @@ export interface CompactionStartData { * Token count from non-system messages (user, assistant, tool) at compaction start */ conversationTokens?: number; + /** + * Total context tokens (system + conversation + tool definitions) at compaction start, when known + */ + currentTokens?: number; /** * Model identifier used for compaction, when known */ @@ -2371,10 +2456,15 @@ export interface CompactionStartData { * Token count from system message(s) at compaction start */ systemTokens?: number; + /** + * Model context window token limit the compaction is targeting, when known + */ + tokenLimit?: number; /** * Token count from tool definitions at compaction start */ toolDefinitionsTokens?: number; + trigger?: CompactionTrigger; } /** * Session event "session.compaction_complete". Conversation compaction results including success status, metrics, and optional error details @@ -2471,6 +2561,10 @@ export interface CompactionCompleteData { * Token count from system message(s) after compaction */ systemTokens?: number; + /** + * Model context window token limit the compaction was targeting, when known + */ + tokenLimit?: number; /** * Number of tokens removed during compaction */ @@ -2479,6 +2573,7 @@ export interface CompactionCompleteData { * Token count from tool definitions after compaction */ toolDefinitionsTokens?: number; + trigger?: CompactionTrigger; } /** * Token usage breakdown for the compaction LLM call (aligned with assistant.usage format) @@ -2656,7 +2751,7 @@ export interface UserMessageData { */ parentAgentTaskId?: string; /** - * Origin of this message, used for timeline filtering (e.g., "skill-pdf" for skill-injected messages that should be hidden from the user) + * Origin of this message, used for timeline filtering and attribution (e.g., `skill-pdf` for hidden skill injection or `agent-` for an inter-agent prompt) */ source?: string; /** @@ -3365,6 +3460,7 @@ export interface AssistantReasoningData { * Unique identifier for this reasoning block */ reasoningId: string; + rte?: boolean; } /** * Session event "assistant.reasoning_delta". Streaming reasoning delta for incremental extended thinking updates @@ -3593,6 +3689,7 @@ export interface AssistantMessageData { * GitHub request tracing ID (x-github-request-id header) for correlating with server-side logs */ requestId?: string; + rte?: boolean; serverTools?: AssistantMessageServerTools; /** * Copilot service request ID (x-copilot-service-request-id header) for CAPI log correlation @@ -4037,6 +4134,10 @@ export interface AssistantUsageData { * Number of input tokens consumed */ inputTokens?: number; + /** + * Coarse classification of the interaction that produced this call, mirroring the session's per-request agent context (e.g. `conversation-agent`, `conversation-subagent`, `conversation-sampling`, `conversation-background`, `conversation-compaction`, `conversation-user`). Non-billing; lets consumers attribute a model call to a call class (e.g. sub-agent/sidekick) independently of the billing initiator. Absent when the runtime did not classify the request. + */ + interactionType?: string; /** * Average inter-token latency in milliseconds. Only available for streaming requests */ @@ -4074,6 +4175,7 @@ export interface AssistantUsageData { * Number of output tokens used for reasoning (e.g., chain-of-thought) */ reasoningTokens?: number; + rte?: boolean; /** * Copilot service request ID (x-copilot-service-request-id header) for CAPI log correlation */ @@ -4289,6 +4391,7 @@ export interface ModelCallFailureData { */ reasoningEffort?: string; requestFingerprint?: ModelCallFailureRequestFingerprint; + rte?: boolean; /** * Copilot service request ID (x-copilot-service-request-id header) for CAPI log correlation */ @@ -4372,6 +4475,12 @@ export interface ModelCallStartData { * Model identifier used for this API call, when known */ model?: string; + /** + * Previous response or interaction identifier included in the model request, when present + * + * @internal + */ + previousResponseId?: string; /** * Identifier of the assistant turn that initiated the model call */ @@ -4523,6 +4632,7 @@ export interface ToolExecutionStartData { * Tool call ID of the parent tool invocation when this event originates from a sub-agent */ parentToolCallId?: string; + rte?: boolean; shellToolInfo?: ToolExecutionStartShellToolInfo; /** * Unique identifier for this tool call @@ -4731,6 +4841,7 @@ export interface ToolExecutionCompleteData { */ parentToolCallId?: string; result?: ToolExecutionCompleteResult; + rte?: boolean; /** * Whether this tool execution ran inside a sandbox container */ @@ -5863,6 +5974,10 @@ export interface SystemMessageData { * The system or developer prompt text sent as model input */ content: string; + /** + * Logical interaction identifier for the model run receiving this prompt + */ + interactionId?: string; metadata?: SystemMessageMetadata; /** * Optional name identifier for the message source @@ -6060,6 +6175,21 @@ export interface SystemNotificationInstructionDiscovered { */ type: "instruction_discovered"; } +/** + * System notification metadata from an external host that does not match a runtime-owned notification kind. + */ +export interface SystemNotificationUnclassified { + /** + * Opaque metadata supplied by the external host, when present. + */ + metadata?: { + [k: string]: unknown | undefined; + }; + /** + * Type discriminator. Always "unclassified". + */ + type: "unclassified"; +} /** * Session event "permission.requested". Permission request notification requiring client approval with request details */ @@ -6104,6 +6234,12 @@ export interface PermissionRequestedData { * When true, this permission was already resolved by a permissionRequest hook and requires no client action */ resolvedByHook?: boolean; + /** + * Neutral risk metadata supplied by the tool host. Consumers may display this value but must not use it to bypass the permission decision. + */ + riskAssessment?: { + [k: string]: unknown | undefined; + }; } /** * Shell command permission request @@ -6117,6 +6253,10 @@ export interface PermissionRequestShell { * Parsed command identifiers found in the command text */ commands: PermissionRequestShellCommand[]; + /** + * Parsed command segments, including arguments, used for managed policy matching + */ + commandSegments?: PermissionRequestShellCommandSegment[]; /** * The complete shell command text to be executed */ @@ -6133,6 +6273,10 @@ export interface PermissionRequestShell { * Permission kind discriminator */ kind: "shell"; + /** + * Whether managed policy requires a human response and forbids host auto-approval + */ + managedApprovalRequired?: boolean; /** * File paths that may be read or written by the command */ @@ -6171,6 +6315,19 @@ export interface PermissionRequestShellCommand { */ readOnly: boolean; } +/** + * A parsed shell command segment used for argument-aware managed policy matching. + */ +export interface PermissionRequestShellCommandSegment { + /** + * Full text of this command segment, including arguments + */ + fullCommandText: string; + /** + * Command identifier (e.g., executable name) + */ + identifier: string; +} /** * A URL that may be accessed by a command in a shell permission request. */ @@ -6204,6 +6361,10 @@ export interface PermissionRequestWrite { * Permission kind discriminator */ kind: "write"; + /** + * Whether managed policy requires a human response and forbids host auto-approval + */ + managedApprovalRequired?: boolean; /** * Complete new file contents for newly created files */ @@ -6233,6 +6394,10 @@ export interface PermissionRequestRead { * Permission kind discriminator */ kind: "read"; + /** + * Whether managed policy requires a human response and forbids host auto-approval + */ + managedApprovalRequired?: boolean; /** * Path of the file or directory being read */ @@ -6297,6 +6462,10 @@ export interface PermissionRequestUrl { * Permission kind discriminator */ kind: "url"; + /** + * Whether managed policy requires a human response and forbids host auto-approval + */ + managedApprovalRequired?: boolean; /** * True when this URL fetch is requesting to bypass the sandbox network policy: either the model set requestSandboxBypass: true, or the tool re-issued the request as an interactive bypass after the network policy denied the approved URL (host opted in via sandbox.allowBypass). This is a request, not a grant: the fetch runs only if the user approves this permission request. Hosts should highlight the elevated risk in the approval UI. */ @@ -6471,6 +6640,10 @@ export interface PermissionPromptRequestCommands { * Prompt kind discriminator */ kind: "commands"; + /** + * Whether managed policy requires a human response and forbids host auto-approval + */ + managedApprovalRequired?: boolean; /** * Tool call ID that triggered this permission request */ @@ -6485,6 +6658,11 @@ export interface PermissionPromptRequestCommands { */ /** @experimental */ export interface PermissionAutoApproval { + failureReason?: AutoApprovalJudgeFailureReason; + /** + * Model id that produced the recommendation, when the judge was consulted and reported one. Absent for `excluded` (the judge was not consulted) and for failures that occurred before a model was selected. + */ + model?: string; /** * Human-readable reason for the judge's recommendation, when available. */ @@ -6521,6 +6699,10 @@ export interface PermissionPromptRequestWrite { * Prompt kind discriminator */ kind: "write"; + /** + * Whether managed policy requires a human response and forbids host auto-approval + */ + managedApprovalRequired?: boolean; /** * Complete new file contents for newly created files */ @@ -6548,6 +6730,10 @@ export interface PermissionPromptRequestRead { * Prompt kind discriminator */ kind: "read"; + /** + * Whether managed policy requires a human response and forbids host auto-approval + */ + managedApprovalRequired?: boolean; /** * Path of the file or directory being read */ @@ -6612,6 +6798,10 @@ export interface PermissionPromptRequestUrl { * Prompt kind discriminator */ kind: "url"; + /** + * Whether managed policy requires a human response and forbids host auto-approval + */ + managedApprovalRequired?: boolean; /** * True when this URL fetch is requesting to bypass the sandbox network policy: either the model set requestSandboxBypass: true, or the tool re-issued the request as an interactive bypass after the network policy denied the approved URL (host opted in via sandbox.allowBypass). This is a request, not a grant: the fetch runs only if the user approves this permission request. Hosts should highlight the elevated risk in the approval UI. */ @@ -7651,6 +7841,166 @@ export interface McpHeadersRefreshCompletedData { */ requestId: string; } +/** + * Session event "mcp.diagnostic". Verbose MCP diagnostic data. Payloads are redacted and size-capped; `truncated` indicates that a captured payload was clipped. This event is ephemeral and high-volume, and is emitted only when a consumer has registered interest in `mcp.diagnostic` via `session.eventLog.registerInterest`. + */ +/** @experimental */ +export interface McpDiagnosticEvent { + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; + data: McpDiagnosticData; + /** + * Always true for events that are transient and not persisted to the session event log on disk. + */ + ephemeral: true; + /** + * Unique event identifier (UUID v4), generated when the event is emitted + */ + id: string; + /** + * ID of the chronologically preceding event in the session, forming a linked chain. Null for the first event. + */ + parentId: string | null; + /** + * ISO 8601 timestamp when the event was created + */ + timestamp: string; + /** + * Type discriminator. Always "mcp.diagnostic". + */ + type: "mcp.diagnostic"; +} +/** + * Verbose MCP diagnostic data. Payloads are redacted and size-capped; `truncated` indicates that a captured payload was clipped. This event is ephemeral and high-volume, and is emitted only when a consumer has registered interest in `mcp.diagnostic` via `session.eventLog.registerInterest`. + */ +export interface McpDiagnosticData { + detail: McpDiagnosticDetail; + /** + * Configured name of the MCP server associated with this diagnostic. + */ + serverName: string; + transport: McpServerTransport; +} +/** + * A redacted MCP protocol message observed on the server transport. + */ +export interface McpDiagnosticWireMessage { + /** + * Size in bytes of the observed message before any diagnostic payload clipping. + */ + byteSize: number; + direction: McpDiagnosticWireMessageDirection; + /** + * Diagnostic kind discriminator + */ + kind: "wire_message"; + messageKind: McpDiagnosticWireMessageKind; + /** + * MCP method name, when the diagnostic message is associated with a method. + */ + method?: string; + /** + * Already-serialized JSON payload after redaction. Omitted when payload capture is unavailable or excluded; when present, it may be clipped as indicated by `truncated`. + */ + payload?: string; + /** + * JSON-RPC request ID serialized as a string, when the diagnostic message is associated with a request or response. + */ + requestId?: string; + /** + * Whether the captured `payload` was clipped to the diagnostic size cap. + */ + truncated: boolean; +} +/** + * A redacted HTTP request or response observed while communicating with an MCP server. + */ +export interface McpDiagnosticHttpExchange { + /** + * Elapsed duration in milliseconds for the HTTP exchange, when measured. + */ + durationMs?: number; + /** + * Redacted HTTP headers as name-to-value pairs, when available. + */ + headers?: { + [k: string]: string | undefined; + }; + /** + * HTTP method used by the request, when available. + */ + httpMethod?: string; + /** + * Diagnostic kind discriminator + */ + kind: "http_exchange"; + phase: McpDiagnosticHttpExchangePhase; + /** + * Whether the exchange belongs to the Server-Sent Events stream leg, when applicable. + */ + sse?: boolean; + /** + * HTTP response status code, when this diagnostic is associated with a response. + */ + statusCode?: number; + /** + * Redacted HTTP URL, when available. + */ + url?: string; +} +/** + * A line emitted by an MCP server process. + */ +export interface McpDiagnosticServerLog { + /** + * Diagnostic kind discriminator + */ + kind: "server_log"; + /** + * One server log line, without a trailing line terminator. + */ + line: string; + /** + * Operating-system process ID of the MCP server process, when known. + */ + pid?: number; + stream: McpDiagnosticServerLogStream; +} +/** + * MCP server process startup or termination details. + */ +export interface McpDiagnosticProcessLifecycle { + /** + * Arguments used to launch the MCP server process, when applicable. + */ + args?: string[]; + /** + * Command used to launch the MCP server process, when applicable. + */ + command?: string; + /** + * Names of environment variables supplied to the MCP server process. Values are never included. + */ + envKeys?: string[]; + /** + * Process exit code, when the MCP server process has exited normally. + */ + exitCode?: number; + /** + * Diagnostic kind discriminator + */ + kind: "process_lifecycle"; + /** + * Operating-system process ID of the MCP server process, when known. + */ + pid?: number; + /** + * Signal that terminated the MCP server process, when applicable. + */ + signal?: string; +} /** * Session event "session.custom_notification". Opaque custom notification data. Consumers may branch on source and name, but payload semantics are source-defined. */ @@ -8253,6 +8603,10 @@ export interface ManagedSettingsResolvedData { * The setting keys under enterprise management in the effective managed settings (e.g. `model`, `enabledPlugins`, `permissions`). Empty when no managed settings are in force. */ managedKeys: string[]; + /** + * Whether server and device each supplied a permission allowlist, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. + */ + permissionsAllowIntersected?: boolean; /** * Whether the server (account/org) managed-settings layer was present */ @@ -8598,6 +8952,48 @@ export interface BackgroundTasksChangedEvent { * Empty payload for `session.background_tasks_changed`, indicating background task state changed. */ export interface BackgroundTasksChangedData {} +/** + * Session event "factory.run_updated". Ephemeral invalidation signal for a changed factory run. + */ +/** @experimental */ +export interface FactoryRunUpdatedEvent { + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; + data: FactoryRunUpdatedData; + /** + * Always true for events that are transient and not persisted to the session event log on disk. + */ + ephemeral: true; + /** + * Unique event identifier (UUID v4), generated when the event is emitted + */ + id: string; + /** + * ID of the chronologically preceding event in the session, forming a linked chain. Null for the first event. + */ + parentId: string | null; + /** + * ISO 8601 timestamp when the event was created + */ + timestamp: string; + /** + * Type discriminator. Always "factory.run_updated". + */ + type: "factory.run_updated"; +} +/** + * Ephemeral invalidation signal for a changed factory run. + */ +/** @experimental */ +export interface FactoryRunUpdatedData { + /** + * Monotonic revision now available for the run. + */ + revision: number; + runId: string; +} /** * Session event "session.skills_loaded". Payload of `session.skills_loaded` listing resolved skill metadata. */ diff --git a/nodejs/src/index.ts b/nodejs/src/index.ts index 352afc6a94..dcc452e3cf 100644 --- a/nodejs/src/index.ts +++ b/nodejs/src/index.ts @@ -97,6 +97,7 @@ export type { MCPServerConfig, DefaultAgentConfig, BearerTokenProvider, + McpDiagnosticHandler, MessageOptions, ModelBilling, ModelBillingTokenPrices, diff --git a/nodejs/src/session.ts b/nodejs/src/session.ts index 89403ade7b..cf74cbd0a2 100644 --- a/nodejs/src/session.ts +++ b/nodejs/src/session.ts @@ -10,6 +10,7 @@ import type { MessageConnection } from "vscode-jsonrpc/node.js"; import { ConnectionError, ErrorCodes, ResponseError } from "vscode-jsonrpc/node.js"; import { createSessionRpc } from "./generated/rpc.js"; +import type { McpDiagnosticData } from "./generated/session-events.js"; import type { ClientSessionApiHandlers, CanvasActionInvokeResult, @@ -35,6 +36,7 @@ import type { UiInputOptions, MessageOptions, McpAuthHandler, + McpDiagnosticHandler, McpAuthRequest, PermissionHandler, PermissionRequest, @@ -149,6 +151,7 @@ export class CopilotSession { private commandHandlers: Map = new Map(); private permissionHandler?: PermissionHandler; private mcpAuthHandler?: McpAuthHandler; + private mcpDiagnosticHandler?: McpDiagnosticHandler; private userInputHandler?: UserInputHandler; private elicitationHandler?: ElicitationHandler; private exitPlanModeHandler?: ExitPlanModeHandler; @@ -178,10 +181,14 @@ export class CopilotSession { private connection: MessageConnection, private _workspacePath?: string, traceContextProvider?: TraceContextProvider, - options?: { mcpAuthHandler?: McpAuthHandler } + options?: { + mcpAuthHandler?: McpAuthHandler; + mcpDiagnosticHandler?: McpDiagnosticHandler; + } ) { this.traceContextProvider = traceContextProvider; this.mcpAuthHandler = options?.mcpAuthHandler; + this.mcpDiagnosticHandler = options?.mcpDiagnosticHandler; } /** @@ -361,6 +368,8 @@ export class CopilotSession { this.typedEventHandlers.clear(); this.toolHandlers.clear(); this.permissionHandler = undefined; + this.mcpAuthHandler = undefined; + this.mcpDiagnosticHandler = undefined; this.userInputHandler = undefined; this.elicitationHandler = undefined; this.exitPlanModeHandler = undefined; @@ -539,6 +548,10 @@ export class CopilotSession { return; } void this._executeMcpAuthAndRespond(data); + } else if (event.type === "mcp.diagnostic") { + if (this.mcpDiagnosticHandler) { + void this._executeMcpDiagnosticHandler(event.data); + } } else if (event.type === "command.execute") { const { requestId, commandName, command, args } = event.data as { requestId: string; @@ -745,6 +758,18 @@ export class CopilotSession { } } + /** + * Executes an MCP diagnostic handler. + * @internal + */ + private async _executeMcpDiagnosticHandler(diagnostic: McpDiagnosticData): Promise { + try { + await this.mcpDiagnosticHandler!(diagnostic, { sessionId: this.sessionId }); + } catch { + // Diagnostic handlers are observational and must not interrupt session event dispatch. + } + } + /** * Executes a command handler and sends the result back via RPC. * @internal diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index 5f89ca3bed..160cc1fca3 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -11,6 +11,7 @@ import type { Canvas } from "./canvas.js"; import type { SessionFsProvider } from "./sessionFsProvider.js"; import type { CopilotRequestHandler } from "./copilotRequestHandler.js"; import type { + McpDiagnosticData, ReasoningSummary, SessionLimitsConfig, SessionEvent as GeneratedSessionEvent, @@ -1862,6 +1863,18 @@ export type McpAuthHandler = ( | undefined | Promise; +/** + * Callback invoked for verbose MCP diagnostics after the SDK host opts in. + * + * The diagnostic payload is redacted and size-capped by the runtime. For + * `wire_message` diagnostics, `truncated` indicates that the captured payload + * was clipped. + */ +export type McpDiagnosticHandler = ( + diagnostic: McpDiagnosticData, + context: { sessionId: string } +) => void | Promise; + /** * Stable extension identity for session participants that provide canvases. */ @@ -2246,6 +2259,16 @@ export interface SessionConfigBase { */ onMcpAuthRequest?: McpAuthHandler; + /** + * Optional handler for verbose MCP diagnostics. + * + * Providing this handler registers interest in the ephemeral, high-volume + * `mcp.diagnostic` event automatically. Normal event subscriptions alone do + * not enable diagnostics. Payloads are redacted and size-capped by the runtime; + * for `wire_message` diagnostics, `truncated` indicates captured-payload clipping. + */ + onMcpDiagnostic?: McpDiagnosticHandler; + /** * Handler for user input requests from the agent. * When provided, enables the ask_user tool allowing the agent to ask questions. diff --git a/nodejs/test/client.test.ts b/nodejs/test/client.test.ts index 77149bc4b9..4a8a80ee3a 100644 --- a/nodejs/test/client.test.ts +++ b/nodejs/test/client.test.ts @@ -129,6 +129,64 @@ describe("CopilotClient", () => { expect(observedRequest.wwwAuthenticateParams).toBeUndefined(); }); + it("delivers MCP diagnostics to the configured handler", async () => { + let observedDiagnostic: + | { + kind: string; + sessionId: string; + method?: string; + truncated?: boolean; + } + | undefined; + let resolveDiagnostic!: () => void; + const received = new Promise<{ + kind: string; + sessionId: string; + method?: string; + truncated?: boolean; + }>((resolve) => { + resolveDiagnostic = () => resolve(observedDiagnostic!); + }); + const session = new CopilotSession("session-1", {} as any, undefined, undefined, { + mcpDiagnosticHandler: (diagnostic, { sessionId }) => { + observedDiagnostic = { + kind: diagnostic.detail.kind, + sessionId, + ...(diagnostic.detail.kind === "wire_message" + ? { + method: diagnostic.detail.method, + truncated: diagnostic.detail.truncated, + } + : {}), + }; + resolveDiagnostic(); + }, + }); + + (session as any)._dispatchEvent({ + type: "mcp.diagnostic", + data: { + serverName: "filesystem", + transport: "stdio", + detail: { + kind: "wire_message", + direction: "outbound", + messageKind: "request", + method: "tools/list", + byteSize: 42, + truncated: true, + }, + }, + }); + + await expect(received).resolves.toEqual({ + kind: "wire_message", + sessionId: "session-1", + method: "tools/list", + truncated: true, + }); + }); + it("registers interest in MCP OAuth required events after create when an auth handler is configured", async () => { const client = new CopilotClient(); await client.start(); @@ -157,7 +215,35 @@ describe("CopilotClient", () => { expect(spy.mock.calls[1][1].sessionId).toBe(spy.mock.calls[0][1].sessionId); }); - it("does not register MCP OAuth interest without an auth handler", async () => { + it("registers interest in MCP diagnostics after create when a handler is configured", async () => { + const client = new CopilotClient(); + await client.start(); + onTestFinished(() => stopClient(client)); + + const spy = vi + .spyOn((client as any).connection!, "sendRequest") + .mockImplementation(async (method: string, params: any) => { + if (method === "session.eventLog.registerInterest") { + return { id: "interest-1" }; + } + if (method === "session.create") return { sessionId: params.sessionId }; + throw new Error(`Unexpected method: ${method}`); + }); + + await client.createSession({ + onPermissionRequest: approveAll, + onMcpDiagnostic: () => {}, + }); + + expect(spy.mock.calls[0][0]).toBe("session.create"); + expect(spy.mock.calls[1]).toEqual([ + "session.eventLog.registerInterest", + expect.objectContaining({ eventType: "mcp.diagnostic" }), + ]); + expect(spy.mock.calls[1][1].sessionId).toBe(spy.mock.calls[0][1].sessionId); + }); + + it("does not register MCP event interest with only an event subscription", async () => { const client = new CopilotClient(); await client.start(); onTestFinished(() => stopClient(client)); @@ -178,6 +264,10 @@ describe("CopilotClient", () => { "session.eventLog.registerInterest", expect.objectContaining({ eventType: "mcp.oauth_required" }) ); + expect(spy).not.toHaveBeenCalledWith( + "session.eventLog.registerInterest", + expect.objectContaining({ eventType: "mcp.diagnostic" }) + ); expect(spy).toHaveBeenCalledWith( "session.create", expect.objectContaining({ requestPermission: true }) @@ -279,6 +369,50 @@ describe("CopilotClient", () => { ); }); + it("registers MCP diagnostics interest after resuming only when a handler is configured", async () => { + const client = new CopilotClient(); + await client.start(); + onTestFinished(() => stopClient(client)); + + const spy = vi + .spyOn((client as any).connection!, "sendRequest") + .mockImplementation(async (method: string, params: any) => { + if (method === "session.eventLog.registerInterest") { + return { id: "interest-1" }; + } + if (method === "session.resume") return { sessionId: params.sessionId }; + throw new Error(`Unexpected method: ${method}`); + }); + + await client.resumeSession("session-with-diagnostics", { + onPermissionRequest: approveAll, + onMcpDiagnostic: () => {}, + }); + + const resumeIndex = spy.mock.calls.findIndex(([method]) => method === "session.resume"); + const interestIndex = spy.mock.calls.findIndex( + ([method, params]) => + method === "session.eventLog.registerInterest" && + params.eventType === "mcp.diagnostic" + ); + expect(resumeIndex).toBeGreaterThanOrEqual(0); + expect(interestIndex).toBeGreaterThan(resumeIndex); + expect(spy.mock.calls[interestIndex][1]).toEqual({ + sessionId: "session-with-diagnostics", + eventType: "mcp.diagnostic", + }); + + spy.mockClear(); + await client.resumeSession("session-without-diagnostics", { + onPermissionRequest: approveAll, + }); + + expect(spy).not.toHaveBeenCalledWith( + "session.eventLog.registerInterest", + expect.objectContaining({ eventType: "mcp.diagnostic" }) + ); + }); + it("forwards canvas declarations and request flags in session.create", async () => { const client = new CopilotClient(); await client.start(); diff --git a/nodejs/test/session-event-types.test.ts b/nodejs/test/session-event-types.test.ts index 37b49f8a48..f7519c40e3 100644 --- a/nodejs/test/session-event-types.test.ts +++ b/nodejs/test/session-event-types.test.ts @@ -49,6 +49,10 @@ import type { // narrow or annotate intermediate values. UserMessageAgentMode, Attachment, + McpDiagnosticData, + McpDiagnosticEvent, + McpDiagnosticHandler, + SessionConfig, WorkingDirectoryContextHostType, } from "../src/index.js"; @@ -81,6 +85,15 @@ type _AssistantMessageEventStaysAlignedWithSessionEventUnion = _AssertEqual< >; const _assistantMessageEventAlignmentCheck: _AssistantMessageEventStaysAlignedWithSessionEventUnion = true; +const _mcpDiagnosticHandler: McpDiagnosticHandler = (diagnostic) => { + if (diagnostic.detail.kind === "wire_message") { + diagnostic.detail.truncated satisfies boolean; + } +}; +const _mcpDiagnosticSessionConfig: SessionConfig = { + onMcpDiagnostic: _mcpDiagnosticHandler, +}; + describe("Session event type exports (#1156)", () => { it("exposes the headline ToolExecutionStartData type with a usable shape", () => { // This is the specific type called out in issue #1156. The annotation @@ -160,6 +173,7 @@ describe("Session event type exports (#1156)", () => { assertImportable(); assertImportable(); assertImportable(); + assertImportable(); assertImportable(); assertImportable(); @@ -169,6 +183,9 @@ describe("Session event type exports (#1156)", () => { assertImportable(); assertImportable(); assertImportable(); + assertImportable(); + assertImportable(); + assertImportable(); // Supporting auxiliary types referenced by the *Data shapes — these // must round-trip through the package root too, otherwise consumers @@ -177,6 +194,7 @@ describe("Session event type exports (#1156)", () => { assertImportable(); assertImportable(); + expect(_mcpDiagnosticSessionConfig.onMcpDiagnostic).toBe(_mcpDiagnosticHandler); expect(true).toBe(true); }); }); diff --git a/python/copilot/generated/session_events.py b/python/copilot/generated/session_events.py index fecd5839ed..5d89e683f4 100644 --- a/python/copilot/generated/session_events.py +++ b/python/copilot/generated/session_events.py @@ -201,6 +201,8 @@ class SessionEventType(Enum): MCP_OAUTH_COMPLETED = "mcp.oauth_completed" MCP_HEADERS_REFRESH_REQUIRED = "mcp.headers_refresh_required" MCP_HEADERS_REFRESH_COMPLETED = "mcp.headers_refresh_completed" + # Experimental: this event is part of an experimental API and may change or be removed. + MCP_DIAGNOSTIC = "mcp.diagnostic" SESSION_CUSTOM_NOTIFICATION = "session.custom_notification" EXTERNAL_TOOL_REQUESTED = "external_tool.requested" EXTERNAL_TOOL_COMPLETED = "external_tool.completed" @@ -223,6 +225,8 @@ class SessionEventType(Enum): EXIT_PLAN_MODE_COMPLETED = "exit_plan_mode.completed" SESSION_TOOLS_UPDATED = "session.tools_updated" SESSION_BACKGROUND_TASKS_CHANGED = "session.background_tasks_changed" + # Experimental: this event is part of an experimental API and may change or be removed. + FACTORY_RUN_UPDATED = "factory.run_updated" SESSION_SKILLS_LOADED = "session.skills_loaded" SESSION_CUSTOM_AGENTS_UPDATED = "session.custom_agents_updated" SESSION_MCP_SERVERS_LOADED = "session.mcp_servers_loaded" @@ -774,6 +778,30 @@ def to_dict(self) -> dict: return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class FactoryRunUpdatedData: + "Ephemeral invalidation signal for a changed factory run." + revision: int + run_id: str + + @staticmethod + def from_dict(obj: Any) -> "FactoryRunUpdatedData": + assert isinstance(obj, dict) + revision = from_int(obj.get("revision")) + run_id = from_str(obj.get("runId")) + return FactoryRunUpdatedData( + revision=revision, + run_id=run_id, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["revision"] = to_int(self.revision) + result["runId"] = from_str(self.run_id) + return result + + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class OmittedBinaryResult: @@ -821,21 +849,31 @@ def to_dict(self) -> dict: class PermissionAutoApproval: "Auto-approval judge information attached to a permission request. Present (non-null) only when the session's allow-all mode is \"auto\"; its absence means auto mode was off and the judge did not evaluate the request. The `recommendation` conveys the judge's disposition for this request." recommendation: AutoApprovalRecommendation + failure_reason: AutoApprovalJudgeFailureReason | None = None + model: str | None = None reason: str | None = None @staticmethod def from_dict(obj: Any) -> "PermissionAutoApproval": assert isinstance(obj, dict) recommendation = parse_enum(AutoApprovalRecommendation, obj.get("recommendation")) + failure_reason = from_union([from_none, lambda x: parse_enum(AutoApprovalJudgeFailureReason, x)], obj.get("failureReason")) + model = from_union([from_none, from_str], obj.get("model")) reason = from_union([from_none, from_str], obj.get("reason")) return PermissionAutoApproval( recommendation=recommendation, + failure_reason=failure_reason, + model=model, reason=reason, ) def to_dict(self) -> dict: result: dict = {} result["recommendation"] = to_enum(AutoApprovalRecommendation, self.recommendation) + if self.failure_reason is not None: + result["failureReason"] = from_union([from_none, lambda x: to_enum(AutoApprovalJudgeFailureReason, x)], self.failure_reason) + if self.model is not None: + result["model"] = from_union([from_none, from_str], self.model) if self.reason is not None: result["reason"] = from_union([from_none, from_str], self.reason) return result @@ -1133,6 +1171,7 @@ class SessionManagedSettingsResolvedData: managed_keys: list[str] server_managed: bool source: ManagedSettingsResolvedSource + permissions_allow_intersected: bool | None = None settings: Any = None @staticmethod @@ -1144,6 +1183,7 @@ def from_dict(obj: Any) -> "SessionManagedSettingsResolvedData": managed_keys = from_list(from_str, obj.get("managedKeys")) server_managed = from_bool(obj.get("serverManaged")) source = parse_enum(ManagedSettingsResolvedSource, obj.get("source")) + permissions_allow_intersected = from_union([from_none, from_bool], obj.get("permissionsAllowIntersected")) settings = obj.get("settings") return SessionManagedSettingsResolvedData( bypass_permissions_disabled=bypass_permissions_disabled, @@ -1152,6 +1192,7 @@ def from_dict(obj: Any) -> "SessionManagedSettingsResolvedData": managed_keys=managed_keys, server_managed=server_managed, source=source, + permissions_allow_intersected=permissions_allow_intersected, settings=settings, ) @@ -1163,6 +1204,8 @@ def to_dict(self) -> dict: result["managedKeys"] = from_list(from_str, self.managed_keys) result["serverManaged"] = from_bool(self.server_managed) result["source"] = to_enum(ManagedSettingsResolvedSource, self.source) + if self.permissions_allow_intersected is not None: + result["permissionsAllowIntersected"] = from_union([from_none, from_bool], self.permissions_allow_intersected) if self.settings is not None: result["settings"] = self.settings return result @@ -1246,6 +1289,7 @@ class AssistantMessageData: reasoning_text: str | None = None reasoning_wire_field: str | None = None request_id: str | None = None + rte: bool | None = None server_tools: AssistantMessageServerTools | None = None service_request_id: str | None = None tool_requests: list[AssistantMessageToolRequest] | None = None @@ -1269,6 +1313,7 @@ def from_dict(obj: Any) -> "AssistantMessageData": reasoning_text = from_union([from_none, from_str], obj.get("reasoningText")) reasoning_wire_field = from_union([from_none, from_str], obj.get("reasoningWireField")) request_id = from_union([from_none, from_str], obj.get("requestId")) + rte = from_union([from_none, from_bool], obj.get("rte")) server_tools = from_union([from_none, AssistantMessageServerTools.from_dict], obj.get("serverTools")) service_request_id = from_union([from_none, from_str], obj.get("serviceRequestId")) tool_requests = from_union([from_none, lambda x: from_list(AssistantMessageToolRequest.from_dict, x)], obj.get("toolRequests")) @@ -1289,6 +1334,7 @@ def from_dict(obj: Any) -> "AssistantMessageData": reasoning_text=reasoning_text, reasoning_wire_field=reasoning_wire_field, request_id=request_id, + rte=rte, server_tools=server_tools, service_request_id=service_request_id, tool_requests=tool_requests, @@ -1325,6 +1371,8 @@ def to_dict(self) -> dict: result["reasoningWireField"] = from_union([from_none, from_str], self.reasoning_wire_field) if self.request_id is not None: result["requestId"] = from_union([from_none, from_str], self.request_id) + if self.rte is not None: + result["rte"] = from_union([from_none, from_bool], self.rte) if self.server_tools is not None: result["serverTools"] = from_union([from_none, lambda x: to_class(AssistantMessageServerTools, x)], self.server_tools) if self.service_request_id is not None: @@ -1447,21 +1495,26 @@ class AssistantReasoningData: "Assistant reasoning content for timeline display with complete thinking text" content: str reasoning_id: str + rte: bool | None = None @staticmethod def from_dict(obj: Any) -> "AssistantReasoningData": assert isinstance(obj, dict) content = from_str(obj.get("content")) reasoning_id = from_str(obj.get("reasoningId")) + rte = from_union([from_none, from_bool], obj.get("rte")) return AssistantReasoningData( content=content, reasoning_id=reasoning_id, + rte=rte, ) def to_dict(self) -> dict: result: dict = {} result["content"] = from_str(self.content) result["reasoningId"] = from_str(self.reasoning_id) + if self.rte is not None: + result["rte"] = from_union([from_none, from_bool], self.rte) return result @@ -1722,6 +1775,7 @@ class AssistantUsageData: finish_reason: str | None = None initiator: str | None = None input_tokens: int | None = None + interaction_type: str | None = None inter_token_latency: timedelta | None = None output_tokens: int | None = None # Deprecated: this field is deprecated. @@ -1731,6 +1785,7 @@ class AssistantUsageData: _quota_snapshots: dict[str, _AssistantUsageQuotaSnapshot] | None = None reasoning_effort: str | None = None reasoning_tokens: int | None = None + rte: bool | None = None service_request_id: str | None = None time_to_first_token: timedelta | None = None @@ -1750,6 +1805,7 @@ def from_dict(obj: Any) -> "AssistantUsageData": finish_reason = from_union([from_none, from_str], obj.get("finishReason")) initiator = from_union([from_none, from_str], obj.get("initiator")) input_tokens = from_union([from_none, from_int], obj.get("inputTokens")) + interaction_type = from_union([from_none, from_str], obj.get("interactionType")) inter_token_latency = from_union([from_none, from_timedelta], obj.get("interTokenLatencyMs")) output_tokens = from_union([from_none, from_int], obj.get("outputTokens")) parent_tool_call_id = from_union([from_none, from_str], obj.get("parentToolCallId")) @@ -1757,6 +1813,7 @@ def from_dict(obj: Any) -> "AssistantUsageData": _quota_snapshots = from_union([from_none, lambda x: from_dict(_AssistantUsageQuotaSnapshot.from_dict, x)], obj.get("quotaSnapshots")) reasoning_effort = from_union([from_none, from_str], obj.get("reasoningEffort")) reasoning_tokens = from_union([from_none, from_int], obj.get("reasoningTokens")) + rte = from_union([from_none, from_bool], obj.get("rte")) service_request_id = from_union([from_none, from_str], obj.get("serviceRequestId")) time_to_first_token = from_union([from_none, from_timedelta], obj.get("timeToFirstTokenMs")) return AssistantUsageData( @@ -1773,6 +1830,7 @@ def from_dict(obj: Any) -> "AssistantUsageData": finish_reason=finish_reason, initiator=initiator, input_tokens=input_tokens, + interaction_type=interaction_type, inter_token_latency=inter_token_latency, output_tokens=output_tokens, parent_tool_call_id=parent_tool_call_id, @@ -1780,6 +1838,7 @@ def from_dict(obj: Any) -> "AssistantUsageData": _quota_snapshots=_quota_snapshots, reasoning_effort=reasoning_effort, reasoning_tokens=reasoning_tokens, + rte=rte, service_request_id=service_request_id, time_to_first_token=time_to_first_token, ) @@ -1811,6 +1870,8 @@ def to_dict(self) -> dict: result["initiator"] = from_union([from_none, from_str], self.initiator) if self.input_tokens is not None: result["inputTokens"] = from_union([from_none, to_int], self.input_tokens) + if self.interaction_type is not None: + result["interactionType"] = from_union([from_none, from_str], self.interaction_type) if self.inter_token_latency is not None: result["interTokenLatencyMs"] = from_union([from_none, to_timedelta], self.inter_token_latency) if self.output_tokens is not None: @@ -1825,6 +1886,8 @@ def to_dict(self) -> dict: result["reasoningEffort"] = from_union([from_none, from_str], self.reasoning_effort) if self.reasoning_tokens is not None: result["reasoningTokens"] = from_union([from_none, to_int], self.reasoning_tokens) + if self.rte is not None: + result["rte"] = from_union([from_none, from_bool], self.rte) if self.service_request_id is not None: result["serviceRequestId"] = from_union([from_none, from_str], self.service_request_id) if self.time_to_first_token is not None: @@ -3608,6 +3671,209 @@ def to_dict(self) -> dict: return result +@dataclass +class McpDiagnosticData: + "Verbose MCP diagnostic data. Payloads are redacted and size-capped; `truncated` indicates that a captured payload was clipped. This event is ephemeral and high-volume, and is emitted only when a consumer has registered interest in `mcp.diagnostic` via `session.eventLog.registerInterest`." + detail: McpDiagnosticDetail + server_name: str + transport: McpServerTransport + + @staticmethod + def from_dict(obj: Any) -> "McpDiagnosticData": + assert isinstance(obj, dict) + detail = _load_McpDiagnosticDetail(obj.get("detail")) + server_name = from_str(obj.get("serverName")) + transport = parse_enum(McpServerTransport, obj.get("transport")) + return McpDiagnosticData( + detail=detail, + server_name=server_name, + transport=transport, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["detail"] = self.detail.to_dict() + result["serverName"] = from_str(self.server_name) + result["transport"] = to_enum(McpServerTransport, self.transport) + return result + + +@dataclass +class McpDiagnosticHttpExchange: + "A redacted HTTP request or response observed while communicating with an MCP server." + kind: ClassVar[str] = "http_exchange" + phase: McpDiagnosticHttpExchangePhase + duration: timedelta | None = None + headers: dict[str, str] | None = None + http_method: str | None = None + sse: bool | None = None + status_code: int | None = None + url: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "McpDiagnosticHttpExchange": + assert isinstance(obj, dict) + phase = parse_enum(McpDiagnosticHttpExchangePhase, obj.get("phase")) + duration = from_union([from_none, from_timedelta], obj.get("durationMs")) + headers = from_union([from_none, lambda x: from_dict(from_str, x)], obj.get("headers")) + http_method = from_union([from_none, from_str], obj.get("httpMethod")) + sse = from_union([from_none, from_bool], obj.get("sse")) + status_code = from_union([from_none, from_int], obj.get("statusCode")) + url = from_union([from_none, from_str], obj.get("url")) + return McpDiagnosticHttpExchange( + phase=phase, + duration=duration, + headers=headers, + http_method=http_method, + sse=sse, + status_code=status_code, + url=url, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = self.kind + result["phase"] = to_enum(McpDiagnosticHttpExchangePhase, self.phase) + if self.duration is not None: + result["durationMs"] = from_union([from_none, to_timedelta_int], self.duration) + if self.headers is not None: + result["headers"] = from_union([from_none, lambda x: from_dict(from_str, x)], self.headers) + if self.http_method is not None: + result["httpMethod"] = from_union([from_none, from_str], self.http_method) + if self.sse is not None: + result["sse"] = from_union([from_none, from_bool], self.sse) + if self.status_code is not None: + result["statusCode"] = from_union([from_none, to_int], self.status_code) + if self.url is not None: + result["url"] = from_union([from_none, from_str], self.url) + return result + + +@dataclass +class McpDiagnosticProcessLifecycle: + "MCP server process startup or termination details." + kind: ClassVar[str] = "process_lifecycle" + args: list[str] | None = None + command: str | None = None + env_keys: list[str] | None = None + exit_code: int | None = None + pid: int | None = None + signal: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "McpDiagnosticProcessLifecycle": + assert isinstance(obj, dict) + args = from_union([from_none, lambda x: from_list(from_str, x)], obj.get("args")) + command = from_union([from_none, from_str], obj.get("command")) + env_keys = from_union([from_none, lambda x: from_list(from_str, x)], obj.get("envKeys")) + exit_code = from_union([from_none, from_int], obj.get("exitCode")) + pid = from_union([from_none, from_int], obj.get("pid")) + signal = from_union([from_none, from_str], obj.get("signal")) + return McpDiagnosticProcessLifecycle( + args=args, + command=command, + env_keys=env_keys, + exit_code=exit_code, + pid=pid, + signal=signal, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = self.kind + if self.args is not None: + result["args"] = from_union([from_none, lambda x: from_list(from_str, x)], self.args) + if self.command is not None: + result["command"] = from_union([from_none, from_str], self.command) + if self.env_keys is not None: + result["envKeys"] = from_union([from_none, lambda x: from_list(from_str, x)], self.env_keys) + if self.exit_code is not None: + result["exitCode"] = from_union([from_none, to_int], self.exit_code) + if self.pid is not None: + result["pid"] = from_union([from_none, to_int], self.pid) + if self.signal is not None: + result["signal"] = from_union([from_none, from_str], self.signal) + return result + + +@dataclass +class McpDiagnosticServerLog: + "A line emitted by an MCP server process." + kind: ClassVar[str] = "server_log" + line: str + stream: McpDiagnosticServerLogStream + pid: int | None = None + + @staticmethod + def from_dict(obj: Any) -> "McpDiagnosticServerLog": + assert isinstance(obj, dict) + line = from_str(obj.get("line")) + stream = parse_enum(McpDiagnosticServerLogStream, obj.get("stream")) + pid = from_union([from_none, from_int], obj.get("pid")) + return McpDiagnosticServerLog( + line=line, + stream=stream, + pid=pid, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = self.kind + result["line"] = from_str(self.line) + result["stream"] = to_enum(McpDiagnosticServerLogStream, self.stream) + if self.pid is not None: + result["pid"] = from_union([from_none, to_int], self.pid) + return result + + +@dataclass +class McpDiagnosticWireMessage: + "A redacted MCP protocol message observed on the server transport." + byte_size: int + direction: McpDiagnosticWireMessageDirection + kind: ClassVar[str] = "wire_message" + message_kind: McpDiagnosticWireMessageKind + truncated: bool + method: str | None = None + payload: str | None = None + request_id: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "McpDiagnosticWireMessage": + assert isinstance(obj, dict) + byte_size = from_int(obj.get("byteSize")) + direction = parse_enum(McpDiagnosticWireMessageDirection, obj.get("direction")) + message_kind = parse_enum(McpDiagnosticWireMessageKind, obj.get("messageKind")) + truncated = from_bool(obj.get("truncated")) + method = from_union([from_none, from_str], obj.get("method")) + payload = from_union([from_none, from_str], obj.get("payload")) + request_id = from_union([from_none, from_str], obj.get("requestId")) + return McpDiagnosticWireMessage( + byte_size=byte_size, + direction=direction, + message_kind=message_kind, + truncated=truncated, + method=method, + payload=payload, + request_id=request_id, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["byteSize"] = to_int(self.byte_size) + result["direction"] = to_enum(McpDiagnosticWireMessageDirection, self.direction) + result["kind"] = self.kind + result["messageKind"] = to_enum(McpDiagnosticWireMessageKind, self.message_kind) + result["truncated"] = from_bool(self.truncated) + if self.method is not None: + result["method"] = from_union([from_none, from_str], self.method) + if self.payload is not None: + result["payload"] = from_union([from_none, from_str], self.payload) + if self.request_id is not None: + result["requestId"] = from_union([from_none, from_str], self.request_id) + return result + + @dataclass class McpHeadersRefreshCompletedData: "MCP headers refresh request completion notification" @@ -3956,6 +4222,7 @@ class ModelCallFailureData: _quota_snapshots: dict[str, _AssistantUsageQuotaSnapshot] | None = None reasoning_effort: str | None = None request_fingerprint: ModelCallFailureRequestFingerprint | None = None + rte: bool | None = None service_request_id: str | None = None status_code: int | None = None transport: ModelCallFailureTransport | None = None @@ -3982,6 +4249,7 @@ def from_dict(obj: Any) -> "ModelCallFailureData": _quota_snapshots = from_union([from_none, lambda x: from_dict(_AssistantUsageQuotaSnapshot.from_dict, x)], obj.get("quotaSnapshots")) reasoning_effort = from_union([from_none, from_str], obj.get("reasoningEffort")) request_fingerprint = from_union([from_none, ModelCallFailureRequestFingerprint.from_dict], obj.get("requestFingerprint")) + rte = from_union([from_none, from_bool], obj.get("rte")) service_request_id = from_union([from_none, from_str], obj.get("serviceRequestId")) status_code = from_union([from_none, from_int], obj.get("statusCode")) transport = from_union([from_none, lambda x: parse_enum(ModelCallFailureTransport, x)], obj.get("transport")) @@ -4005,6 +4273,7 @@ def from_dict(obj: Any) -> "ModelCallFailureData": _quota_snapshots=_quota_snapshots, reasoning_effort=reasoning_effort, request_fingerprint=request_fingerprint, + rte=rte, service_request_id=service_request_id, status_code=status_code, transport=transport, @@ -4049,6 +4318,8 @@ def to_dict(self) -> dict: result["reasoningEffort"] = from_union([from_none, from_str], self.reasoning_effort) if self.request_fingerprint is not None: result["requestFingerprint"] = from_union([from_none, lambda x: to_class(ModelCallFailureRequestFingerprint, x)], self.request_fingerprint) + if self.rte is not None: + result["rte"] = from_union([from_none, from_bool], self.rte) if self.service_request_id is not None: result["serviceRequestId"] = from_union([from_none, from_str], self.service_request_id) if self.status_code is not None: @@ -4107,15 +4378,19 @@ class ModelCallStartData: "Model API dispatch metadata for internal telemetry" turn_id: str model: str | None = None + # Internal: this field is an internal SDK API and is not part of the public surface. + _previous_response_id: str | None = None @staticmethod def from_dict(obj: Any) -> "ModelCallStartData": assert isinstance(obj, dict) turn_id = from_str(obj.get("turnId")) model = from_union([from_none, from_str], obj.get("model")) + _previous_response_id = from_union([from_none, from_str], obj.get("previousResponseId")) return ModelCallStartData( turn_id=turn_id, model=model, + _previous_response_id=_previous_response_id, ) def to_dict(self) -> dict: @@ -4123,6 +4398,8 @@ def to_dict(self) -> dict: result["turnId"] = from_str(self.turn_id) if self.model is not None: result["model"] = from_union([from_none, from_str], self.model) + if self._previous_response_id is not None: + result["previousResponseId"] = from_union([from_none, from_str], self._previous_response_id) return result @@ -4378,6 +4655,7 @@ class PermissionPromptRequestCommands: kind: ClassVar[str] = "commands" # Experimental: this field is part of an experimental API and may change or be removed. auto_approval: PermissionAutoApproval | None = None + managed_approval_required: bool | None = None tool_call_id: str | None = None warning: str | None = None @@ -4389,6 +4667,7 @@ def from_dict(obj: Any) -> "PermissionPromptRequestCommands": full_command_text = from_str(obj.get("fullCommandText")) intention = from_str(obj.get("intention")) auto_approval = from_union([from_none, PermissionAutoApproval.from_dict], obj.get("autoApproval")) + managed_approval_required = from_union([from_none, from_bool], obj.get("managedApprovalRequired")) tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) warning = from_union([from_none, from_str], obj.get("warning")) return PermissionPromptRequestCommands( @@ -4397,6 +4676,7 @@ def from_dict(obj: Any) -> "PermissionPromptRequestCommands": full_command_text=full_command_text, intention=intention, auto_approval=auto_approval, + managed_approval_required=managed_approval_required, tool_call_id=tool_call_id, warning=warning, ) @@ -4410,6 +4690,8 @@ def to_dict(self) -> dict: result["kind"] = self.kind if self.auto_approval is not None: result["autoApproval"] = from_union([from_none, lambda x: to_class(PermissionAutoApproval, x)], self.auto_approval) + if self.managed_approval_required is not None: + result["managedApprovalRequired"] = from_union([from_none, from_bool], self.managed_approval_required) if self.tool_call_id is not None: result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) if self.warning is not None: @@ -4719,6 +5001,7 @@ class PermissionPromptRequestRead: path: str # Experimental: this field is part of an experimental API and may change or be removed. auto_approval: PermissionAutoApproval | None = None + managed_approval_required: bool | None = None tool_call_id: str | None = None @staticmethod @@ -4727,11 +5010,13 @@ def from_dict(obj: Any) -> "PermissionPromptRequestRead": intention = from_str(obj.get("intention")) path = from_str(obj.get("path")) auto_approval = from_union([from_none, PermissionAutoApproval.from_dict], obj.get("autoApproval")) + managed_approval_required = from_union([from_none, from_bool], obj.get("managedApprovalRequired")) tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) return PermissionPromptRequestRead( intention=intention, path=path, auto_approval=auto_approval, + managed_approval_required=managed_approval_required, tool_call_id=tool_call_id, ) @@ -4742,6 +5027,8 @@ def to_dict(self) -> dict: result["path"] = from_str(self.path) if self.auto_approval is not None: result["autoApproval"] = from_union([from_none, lambda x: to_class(PermissionAutoApproval, x)], self.auto_approval) + if self.managed_approval_required is not None: + result["managedApprovalRequired"] = from_union([from_none, from_bool], self.managed_approval_required) if self.tool_call_id is not None: result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) return result @@ -4755,6 +5042,7 @@ class PermissionPromptRequestUrl: url: str # Experimental: this field is part of an experimental API and may change or be removed. auto_approval: PermissionAutoApproval | None = None + managed_approval_required: bool | None = None request_sandbox_bypass: bool | None = None request_sandbox_bypass_reason: str | None = None tool_call_id: str | None = None @@ -4765,6 +5053,7 @@ def from_dict(obj: Any) -> "PermissionPromptRequestUrl": intention = from_str(obj.get("intention")) url = from_str(obj.get("url")) auto_approval = from_union([from_none, PermissionAutoApproval.from_dict], obj.get("autoApproval")) + managed_approval_required = from_union([from_none, from_bool], obj.get("managedApprovalRequired")) request_sandbox_bypass = from_union([from_none, from_bool], obj.get("requestSandboxBypass")) request_sandbox_bypass_reason = from_union([from_none, from_str], obj.get("requestSandboxBypassReason")) tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) @@ -4772,6 +5061,7 @@ def from_dict(obj: Any) -> "PermissionPromptRequestUrl": intention=intention, url=url, auto_approval=auto_approval, + managed_approval_required=managed_approval_required, request_sandbox_bypass=request_sandbox_bypass, request_sandbox_bypass_reason=request_sandbox_bypass_reason, tool_call_id=tool_call_id, @@ -4784,6 +5074,8 @@ def to_dict(self) -> dict: result["url"] = from_str(self.url) if self.auto_approval is not None: result["autoApproval"] = from_union([from_none, lambda x: to_class(PermissionAutoApproval, x)], self.auto_approval) + if self.managed_approval_required is not None: + result["managedApprovalRequired"] = from_union([from_none, from_bool], self.managed_approval_required) if self.request_sandbox_bypass is not None: result["requestSandboxBypass"] = from_union([from_none, from_bool], self.request_sandbox_bypass) if self.request_sandbox_bypass_reason is not None: @@ -4803,6 +5095,7 @@ class PermissionPromptRequestWrite: kind: ClassVar[str] = "write" # Experimental: this field is part of an experimental API and may change or be removed. auto_approval: PermissionAutoApproval | None = None + managed_approval_required: bool | None = None new_file_contents: str | None = None tool_call_id: str | None = None @@ -4814,6 +5107,7 @@ def from_dict(obj: Any) -> "PermissionPromptRequestWrite": file_name = from_str(obj.get("fileName")) intention = from_str(obj.get("intention")) auto_approval = from_union([from_none, PermissionAutoApproval.from_dict], obj.get("autoApproval")) + managed_approval_required = from_union([from_none, from_bool], obj.get("managedApprovalRequired")) new_file_contents = from_union([from_none, from_str], obj.get("newFileContents")) tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) return PermissionPromptRequestWrite( @@ -4822,6 +5116,7 @@ def from_dict(obj: Any) -> "PermissionPromptRequestWrite": file_name=file_name, intention=intention, auto_approval=auto_approval, + managed_approval_required=managed_approval_required, new_file_contents=new_file_contents, tool_call_id=tool_call_id, ) @@ -4835,6 +5130,8 @@ def to_dict(self) -> dict: result["kind"] = self.kind if self.auto_approval is not None: result["autoApproval"] = from_union([from_none, lambda x: to_class(PermissionAutoApproval, x)], self.auto_approval) + if self.managed_approval_required is not None: + result["managedApprovalRequired"] = from_union([from_none, from_bool], self.managed_approval_required) if self.new_file_contents is not None: result["newFileContents"] = from_union([from_none, from_str], self.new_file_contents) if self.tool_call_id is not None: @@ -5074,6 +5371,7 @@ class PermissionRequestRead: intention: str kind: ClassVar[str] = "read" path: str + managed_approval_required: bool | None = None request_sandbox_bypass: bool | None = None request_sandbox_bypass_reason: str | None = None tool_call_id: str | None = None @@ -5083,12 +5381,14 @@ def from_dict(obj: Any) -> "PermissionRequestRead": assert isinstance(obj, dict) intention = from_str(obj.get("intention")) path = from_str(obj.get("path")) + managed_approval_required = from_union([from_none, from_bool], obj.get("managedApprovalRequired")) request_sandbox_bypass = from_union([from_none, from_bool], obj.get("requestSandboxBypass")) request_sandbox_bypass_reason = from_union([from_none, from_str], obj.get("requestSandboxBypassReason")) tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) return PermissionRequestRead( intention=intention, path=path, + managed_approval_required=managed_approval_required, request_sandbox_bypass=request_sandbox_bypass, request_sandbox_bypass_reason=request_sandbox_bypass_reason, tool_call_id=tool_call_id, @@ -5099,6 +5399,8 @@ def to_dict(self) -> dict: result["intention"] = from_str(self.intention) result["kind"] = self.kind result["path"] = from_str(self.path) + if self.managed_approval_required is not None: + result["managedApprovalRequired"] = from_union([from_none, from_bool], self.managed_approval_required) if self.request_sandbox_bypass is not None: result["requestSandboxBypass"] = from_union([from_none, from_bool], self.request_sandbox_bypass) if self.request_sandbox_bypass_reason is not None: @@ -5119,6 +5421,8 @@ class PermissionRequestShell: kind: ClassVar[str] = "shell" possible_paths: list[str] possible_urls: list[PermissionRequestShellPossibleUrl] + command_segments: list[PermissionRequestShellCommandSegment] | None = None + managed_approval_required: bool | None = None request_sandbox_bypass: bool | None = None request_sandbox_bypass_reason: str | None = None tool_call_id: str | None = None @@ -5134,6 +5438,8 @@ def from_dict(obj: Any) -> "PermissionRequestShell": intention = from_str(obj.get("intention")) possible_paths = from_list(from_str, obj.get("possiblePaths")) possible_urls = from_list(PermissionRequestShellPossibleUrl.from_dict, obj.get("possibleUrls")) + command_segments = from_union([from_none, lambda x: from_list(PermissionRequestShellCommandSegment.from_dict, x)], obj.get("commandSegments")) + managed_approval_required = from_union([from_none, from_bool], obj.get("managedApprovalRequired")) request_sandbox_bypass = from_union([from_none, from_bool], obj.get("requestSandboxBypass")) request_sandbox_bypass_reason = from_union([from_none, from_str], obj.get("requestSandboxBypassReason")) tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) @@ -5146,6 +5452,8 @@ def from_dict(obj: Any) -> "PermissionRequestShell": intention=intention, possible_paths=possible_paths, possible_urls=possible_urls, + command_segments=command_segments, + managed_approval_required=managed_approval_required, request_sandbox_bypass=request_sandbox_bypass, request_sandbox_bypass_reason=request_sandbox_bypass_reason, tool_call_id=tool_call_id, @@ -5162,6 +5470,10 @@ def to_dict(self) -> dict: result["kind"] = self.kind result["possiblePaths"] = from_list(from_str, self.possible_paths) result["possibleUrls"] = from_list(lambda x: to_class(PermissionRequestShellPossibleUrl, x), self.possible_urls) + if self.command_segments is not None: + result["commandSegments"] = from_union([from_none, lambda x: from_list(lambda x: to_class(PermissionRequestShellCommandSegment, x), x)], self.command_segments) + if self.managed_approval_required is not None: + result["managedApprovalRequired"] = from_union([from_none, from_bool], self.managed_approval_required) if self.request_sandbox_bypass is not None: result["requestSandboxBypass"] = from_union([from_none, from_bool], self.request_sandbox_bypass) if self.request_sandbox_bypass_reason is not None: @@ -5196,6 +5508,29 @@ def to_dict(self) -> dict: return result +@dataclass +class PermissionRequestShellCommandSegment: + "A parsed shell command segment used for argument-aware managed policy matching." + full_command_text: str + identifier: str + + @staticmethod + def from_dict(obj: Any) -> "PermissionRequestShellCommandSegment": + assert isinstance(obj, dict) + full_command_text = from_str(obj.get("fullCommandText")) + identifier = from_str(obj.get("identifier")) + return PermissionRequestShellCommandSegment( + full_command_text=full_command_text, + identifier=identifier, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["fullCommandText"] = from_str(self.full_command_text) + result["identifier"] = from_str(self.identifier) + return result + + @dataclass class PermissionRequestShellPossibleUrl: "A URL that may be accessed by a command in a shell permission request." @@ -5221,6 +5556,7 @@ class PermissionRequestUrl: intention: str kind: ClassVar[str] = "url" url: str + managed_approval_required: bool | None = None request_sandbox_bypass: bool | None = None request_sandbox_bypass_reason: str | None = None tool_call_id: str | None = None @@ -5230,12 +5566,14 @@ def from_dict(obj: Any) -> "PermissionRequestUrl": assert isinstance(obj, dict) intention = from_str(obj.get("intention")) url = from_str(obj.get("url")) + managed_approval_required = from_union([from_none, from_bool], obj.get("managedApprovalRequired")) request_sandbox_bypass = from_union([from_none, from_bool], obj.get("requestSandboxBypass")) request_sandbox_bypass_reason = from_union([from_none, from_str], obj.get("requestSandboxBypassReason")) tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) return PermissionRequestUrl( intention=intention, url=url, + managed_approval_required=managed_approval_required, request_sandbox_bypass=request_sandbox_bypass, request_sandbox_bypass_reason=request_sandbox_bypass_reason, tool_call_id=tool_call_id, @@ -5246,6 +5584,8 @@ def to_dict(self) -> dict: result["intention"] = from_str(self.intention) result["kind"] = self.kind result["url"] = from_str(self.url) + if self.managed_approval_required is not None: + result["managedApprovalRequired"] = from_union([from_none, from_bool], self.managed_approval_required) if self.request_sandbox_bypass is not None: result["requestSandboxBypass"] = from_union([from_none, from_bool], self.request_sandbox_bypass) if self.request_sandbox_bypass_reason is not None: @@ -5263,6 +5603,7 @@ class PermissionRequestWrite: file_name: str intention: str kind: ClassVar[str] = "write" + managed_approval_required: bool | None = None new_file_contents: str | None = None request_sandbox_bypass: bool | None = None request_sandbox_bypass_reason: str | None = None @@ -5275,6 +5616,7 @@ def from_dict(obj: Any) -> "PermissionRequestWrite": diff = from_str(obj.get("diff")) file_name = from_str(obj.get("fileName")) intention = from_str(obj.get("intention")) + managed_approval_required = from_union([from_none, from_bool], obj.get("managedApprovalRequired")) new_file_contents = from_union([from_none, from_str], obj.get("newFileContents")) request_sandbox_bypass = from_union([from_none, from_bool], obj.get("requestSandboxBypass")) request_sandbox_bypass_reason = from_union([from_none, from_str], obj.get("requestSandboxBypassReason")) @@ -5284,6 +5626,7 @@ def from_dict(obj: Any) -> "PermissionRequestWrite": diff=diff, file_name=file_name, intention=intention, + managed_approval_required=managed_approval_required, new_file_contents=new_file_contents, request_sandbox_bypass=request_sandbox_bypass, request_sandbox_bypass_reason=request_sandbox_bypass_reason, @@ -5297,6 +5640,8 @@ def to_dict(self) -> dict: result["fileName"] = from_str(self.file_name) result["intention"] = from_str(self.intention) result["kind"] = self.kind + if self.managed_approval_required is not None: + result["managedApprovalRequired"] = from_union([from_none, from_bool], self.managed_approval_required) if self.new_file_contents is not None: result["newFileContents"] = from_union([from_none, from_str], self.new_file_contents) if self.request_sandbox_bypass is not None: @@ -5315,6 +5660,7 @@ class PermissionRequestedData: request_id: str prompt_request: PermissionPromptRequest | None = None resolved_by_hook: bool | None = None + risk_assessment: Any = None @staticmethod def from_dict(obj: Any) -> "PermissionRequestedData": @@ -5323,11 +5669,13 @@ def from_dict(obj: Any) -> "PermissionRequestedData": request_id = from_str(obj.get("requestId")) prompt_request = from_union([from_none, _load_PermissionPromptRequest], obj.get("promptRequest")) resolved_by_hook = from_union([from_none, from_bool], obj.get("resolvedByHook")) + risk_assessment = obj.get("riskAssessment") return PermissionRequestedData( permission_request=permission_request, request_id=request_id, prompt_request=prompt_request, resolved_by_hook=resolved_by_hook, + risk_assessment=risk_assessment, ) def to_dict(self) -> dict: @@ -5338,6 +5686,8 @@ def to_dict(self) -> dict: result["promptRequest"] = from_union([from_none, lambda x: x.to_dict()], self.prompt_request) if self.resolved_by_hook is not None: result["resolvedByHook"] = from_union([from_none, from_bool], self.resolved_by_hook) + if self.risk_assessment is not None: + result["riskAssessment"] = self.risk_assessment return result @@ -5552,8 +5902,10 @@ class SessionCompactionCompleteData: status_code: int | None = None summary_content: str | None = None system_tokens: int | None = None + token_limit: int | None = None tokens_removed: int | None = None tool_definitions_tokens: int | None = None + trigger: CompactionTrigger | None = None @staticmethod def from_dict(obj: Any) -> "SessionCompactionCompleteData": @@ -5574,8 +5926,10 @@ def from_dict(obj: Any) -> "SessionCompactionCompleteData": status_code = from_union([from_none, from_int], obj.get("statusCode")) summary_content = from_union([from_none, from_str], obj.get("summaryContent")) system_tokens = from_union([from_none, from_int], obj.get("systemTokens")) + token_limit = from_union([from_none, from_int], obj.get("tokenLimit")) tokens_removed = from_union([from_none, from_int], obj.get("tokensRemoved")) tool_definitions_tokens = from_union([from_none, from_int], obj.get("toolDefinitionsTokens")) + trigger = from_union([from_none, lambda x: parse_enum(CompactionTrigger, x)], obj.get("trigger")) return SessionCompactionCompleteData( success=success, checkpoint_number=checkpoint_number, @@ -5593,8 +5947,10 @@ def from_dict(obj: Any) -> "SessionCompactionCompleteData": status_code=status_code, summary_content=summary_content, system_tokens=system_tokens, + token_limit=token_limit, tokens_removed=tokens_removed, tool_definitions_tokens=tool_definitions_tokens, + trigger=trigger, ) def to_dict(self) -> dict: @@ -5630,10 +5986,14 @@ def to_dict(self) -> dict: result["summaryContent"] = from_union([from_none, from_str], self.summary_content) if self.system_tokens is not None: result["systemTokens"] = from_union([from_none, to_int], self.system_tokens) + if self.token_limit is not None: + result["tokenLimit"] = from_union([from_none, to_int], self.token_limit) if self.tokens_removed is not None: result["tokensRemoved"] = from_union([from_none, to_int], self.tokens_removed) if self.tool_definitions_tokens is not None: result["toolDefinitionsTokens"] = from_union([from_none, to_int], self.tool_definitions_tokens) + if self.trigger is not None: + result["trigger"] = from_union([from_none, lambda x: to_enum(CompactionTrigger, x)], self.trigger) return result @@ -5641,34 +6001,49 @@ def to_dict(self) -> dict: class SessionCompactionStartData: "Context window breakdown at the start of LLM-powered conversation compaction" conversation_tokens: int | None = None + current_tokens: int | None = None model: str | None = None system_tokens: int | None = None + token_limit: int | None = None tool_definitions_tokens: int | None = None + trigger: CompactionTrigger | None = None @staticmethod def from_dict(obj: Any) -> "SessionCompactionStartData": assert isinstance(obj, dict) conversation_tokens = from_union([from_none, from_int], obj.get("conversationTokens")) + current_tokens = from_union([from_none, from_int], obj.get("currentTokens")) model = from_union([from_none, from_str], obj.get("model")) system_tokens = from_union([from_none, from_int], obj.get("systemTokens")) + token_limit = from_union([from_none, from_int], obj.get("tokenLimit")) tool_definitions_tokens = from_union([from_none, from_int], obj.get("toolDefinitionsTokens")) + trigger = from_union([from_none, lambda x: parse_enum(CompactionTrigger, x)], obj.get("trigger")) return SessionCompactionStartData( conversation_tokens=conversation_tokens, + current_tokens=current_tokens, model=model, system_tokens=system_tokens, + token_limit=token_limit, tool_definitions_tokens=tool_definitions_tokens, + trigger=trigger, ) def to_dict(self) -> dict: result: dict = {} if self.conversation_tokens is not None: result["conversationTokens"] = from_union([from_none, to_int], self.conversation_tokens) + if self.current_tokens is not None: + result["currentTokens"] = from_union([from_none, to_int], self.current_tokens) if self.model is not None: result["model"] = from_union([from_none, from_str], self.model) if self.system_tokens is not None: result["systemTokens"] = from_union([from_none, to_int], self.system_tokens) + if self.token_limit is not None: + result["tokenLimit"] = from_union([from_none, to_int], self.token_limit) if self.tool_definitions_tokens is not None: result["toolDefinitionsTokens"] = from_union([from_none, to_int], self.tool_definitions_tokens) + if self.trigger is not None: + result["trigger"] = from_union([from_none, lambda x: to_enum(CompactionTrigger, x)], self.trigger) return result @@ -6404,6 +6779,7 @@ class SessionScheduleCreatedData: cron: str | None = None display_prompt: str | None = None interval: timedelta | None = None + origin: ScheduleOrigin | None = None recurring: bool | None = None self_paced: bool | None = None tz: str | None = None @@ -6417,6 +6793,7 @@ def from_dict(obj: Any) -> "SessionScheduleCreatedData": cron = from_union([from_none, from_str], obj.get("cron")) display_prompt = from_union([from_none, from_str], obj.get("displayPrompt")) interval = from_union([from_none, from_timedelta], obj.get("intervalMs")) + origin = from_union([from_none, lambda x: parse_enum(ScheduleOrigin, x)], obj.get("origin")) recurring = from_union([from_none, from_bool], obj.get("recurring")) self_paced = from_union([from_none, from_bool], obj.get("selfPaced")) tz = from_union([from_none, from_str], obj.get("tz")) @@ -6427,6 +6804,7 @@ def from_dict(obj: Any) -> "SessionScheduleCreatedData": cron=cron, display_prompt=display_prompt, interval=interval, + origin=origin, recurring=recurring, self_paced=self_paced, tz=tz, @@ -6444,6 +6822,8 @@ def to_dict(self) -> dict: result["displayPrompt"] = from_union([from_none, from_str], self.display_prompt) if self.interval is not None: result["intervalMs"] = from_union([from_none, to_timedelta_int], self.interval) + if self.origin is not None: + result["origin"] = from_union([from_none, lambda x: to_enum(ScheduleOrigin, x)], self.origin) if self.recurring is not None: result["recurring"] = from_union([from_none, from_bool], self.recurring) if self.self_paced is not None: @@ -7407,6 +7787,7 @@ class SystemMessageData: "System/developer instruction content with role and optional template metadata" content: str role: SystemMessageRole + interaction_id: str | None = None metadata: SystemMessageMetadata | None = None name: str | None = None @@ -7415,11 +7796,13 @@ def from_dict(obj: Any) -> "SystemMessageData": assert isinstance(obj, dict) content = from_str(obj.get("content")) role = parse_enum(SystemMessageRole, obj.get("role")) + interaction_id = from_union([from_none, from_str], obj.get("interactionId")) metadata = from_union([from_none, SystemMessageMetadata.from_dict], obj.get("metadata")) name = from_union([from_none, from_str], obj.get("name")) return SystemMessageData( content=content, role=role, + interaction_id=interaction_id, metadata=metadata, name=name, ) @@ -7428,6 +7811,8 @@ def to_dict(self) -> dict: result: dict = {} result["content"] = from_str(self.content) result["role"] = to_enum(SystemMessageRole, self.role) + if self.interaction_id is not None: + result["interactionId"] = from_union([from_none, from_str], self.interaction_id) if self.metadata is not None: result["metadata"] = from_union([from_none, lambda x: to_class(SystemMessageMetadata, x)], self.metadata) if self.name is not None: @@ -7676,6 +8061,28 @@ def to_dict(self) -> dict: return result +@dataclass +class SystemNotificationUnclassified: + "System notification metadata from an external host that does not match a runtime-owned notification kind." + type: ClassVar[str] = "unclassified" + metadata: Any = None + + @staticmethod + def from_dict(obj: Any) -> "SystemNotificationUnclassified": + assert isinstance(obj, dict) + metadata = obj.get("metadata") + return SystemNotificationUnclassified( + metadata=metadata, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["type"] = self.type + if self.metadata is not None: + result["metadata"] = self.metadata + return result + + @dataclass class ToolExecutionCompleteContentAudio: "Audio content block with base64-encoded data" @@ -7906,6 +8313,7 @@ class ToolExecutionCompleteData: # Deprecated: this field is deprecated. parent_tool_call_id: str | None = None result: ToolExecutionCompleteResult | None = None + rte: bool | None = None sandboxed: bool | None = None tool_description: ToolExecutionCompleteToolDescription | None = None tool_telemetry: dict[str, Any] | None = None @@ -7923,6 +8331,7 @@ def from_dict(obj: Any) -> "ToolExecutionCompleteData": model = from_union([from_none, from_str], obj.get("model")) parent_tool_call_id = from_union([from_none, from_str], obj.get("parentToolCallId")) result = from_union([from_none, ToolExecutionCompleteResult.from_dict], obj.get("result")) + rte = from_union([from_none, from_bool], obj.get("rte")) sandboxed = from_union([from_none, from_bool], obj.get("sandboxed")) tool_description = from_union([from_none, ToolExecutionCompleteToolDescription.from_dict], obj.get("toolDescription")) tool_telemetry = from_union([from_none, lambda x: from_dict(lambda x: x, x)], obj.get("toolTelemetry")) @@ -7937,6 +8346,7 @@ def from_dict(obj: Any) -> "ToolExecutionCompleteData": model=model, parent_tool_call_id=parent_tool_call_id, result=result, + rte=rte, sandboxed=sandboxed, tool_description=tool_description, tool_telemetry=tool_telemetry, @@ -7961,6 +8371,8 @@ def to_dict(self) -> dict: result["parentToolCallId"] = from_union([from_none, from_str], self.parent_tool_call_id) if self.result is not None: result["result"] = from_union([from_none, lambda x: to_class(ToolExecutionCompleteResult, x)], self.result) + if self.rte is not None: + result["rte"] = from_union([from_none, from_bool], self.rte) if self.sandboxed is not None: result["sandboxed"] = from_union([from_none, from_bool], self.sandboxed) if self.tool_description is not None: @@ -8396,6 +8808,7 @@ class ToolExecutionStartData: model: str | None = None # Deprecated: this field is deprecated. parent_tool_call_id: str | None = None + rte: bool | None = None shell_tool_info: ToolExecutionStartShellToolInfo | None = None tool_description: ToolExecutionStartToolDescription | None = None turn_id: str | None = None @@ -8411,6 +8824,7 @@ def from_dict(obj: Any) -> "ToolExecutionStartData": mcp_tool_name = from_union([from_none, from_str], obj.get("mcpToolName")) model = from_union([from_none, from_str], obj.get("model")) parent_tool_call_id = from_union([from_none, from_str], obj.get("parentToolCallId")) + rte = from_union([from_none, from_bool], obj.get("rte")) shell_tool_info = from_union([from_none, ToolExecutionStartShellToolInfo.from_dict], obj.get("shellToolInfo")) tool_description = from_union([from_none, ToolExecutionStartToolDescription.from_dict], obj.get("toolDescription")) turn_id = from_union([from_none, from_str], obj.get("turnId")) @@ -8423,6 +8837,7 @@ def from_dict(obj: Any) -> "ToolExecutionStartData": mcp_tool_name=mcp_tool_name, model=model, parent_tool_call_id=parent_tool_call_id, + rte=rte, shell_tool_info=shell_tool_info, tool_description=tool_description, turn_id=turn_id, @@ -8444,6 +8859,8 @@ def to_dict(self) -> dict: result["model"] = from_union([from_none, from_str], self.model) if self.parent_tool_call_id is not None: result["parentToolCallId"] = from_union([from_none, from_str], self.parent_tool_call_id) + if self.rte is not None: + result["rte"] = from_union([from_none, from_bool], self.rte) if self.shell_tool_info is not None: result["shellToolInfo"] = from_union([from_none, lambda x: to_class(ToolExecutionStartShellToolInfo, x)], self.shell_tool_info) if self.tool_description is not None: @@ -9012,6 +9429,17 @@ def _load_CitationLocation(obj: Any) -> "CitationLocation": case _: raise ValueError(f"Unknown CitationLocation type: {kind!r}") +def _load_McpDiagnosticDetail(obj: Any) -> "McpDiagnosticDetail": + assert isinstance(obj, dict) + kind = obj.get("kind") + match kind: + case "wire_message": return McpDiagnosticWireMessage.from_dict(obj) + case "http_exchange": return McpDiagnosticHttpExchange.from_dict(obj) + case "server_log": return McpDiagnosticServerLog.from_dict(obj) + case "process_lifecycle": return McpDiagnosticProcessLifecycle.from_dict(obj) + case _: raise ValueError(f"Unknown McpDiagnosticDetail kind: {kind!r}") + + def _load_PermissionPromptRequest(obj: Any) -> "PermissionPromptRequest": assert isinstance(obj, dict) kind = obj.get("kind") @@ -9073,6 +9501,7 @@ def _load_SystemNotification(obj: Any) -> "SystemNotification": case "shell_completed": return SystemNotificationShellCompleted.from_dict(obj) case "shell_detached_completed": return SystemNotificationShellDetachedCompleted.from_dict(obj) case "instruction_discovered": return SystemNotificationInstructionDiscovered.from_dict(obj) + case "unclassified": return SystemNotificationUnclassified.from_dict(obj) case _: raise ValueError(f"Unknown SystemNotification type: {kind!r}") @@ -9121,6 +9550,10 @@ def _load_UserToolSessionApproval(obj: Any) -> "UserToolSessionApproval": PermissionPromptRequest = PermissionPromptRequestCommands | PermissionPromptRequestWrite | PermissionPromptRequestRead | PermissionPromptRequestMcp | PermissionPromptRequestUrl | PermissionPromptRequestMemory | PermissionPromptRequestCustomTool | PermissionPromptRequestPath | PermissionPromptRequestHook | PermissionPromptRequestExtensionManagement | PermissionPromptRequestExtensionPermissionAccess +# Detailed MCP diagnostic payload discriminated by `kind`. +McpDiagnosticDetail = McpDiagnosticWireMessage | McpDiagnosticHttpExchange | McpDiagnosticServerLog | McpDiagnosticProcessLifecycle + + # Details of the permission being requested PermissionRequest = PermissionRequestShell | PermissionRequestWrite | PermissionRequestRead | PermissionRequestMcp | PermissionRequestUrl | PermissionRequestMemory | PermissionRequestCustomTool | PermissionRequestHook | PermissionRequestExtensionManagement | PermissionRequestExtensionPermissionAccess @@ -9130,7 +9563,7 @@ def _load_UserToolSessionApproval(obj: Any) -> "UserToolSessionApproval": # Structured metadata identifying what triggered this notification -SystemNotification = SystemNotificationAgentCompleted | SystemNotificationAgentIdle | SystemNotificationNewInboxMessage | SystemNotificationShellCompleted | SystemNotificationShellDetachedCompleted | SystemNotificationInstructionDiscovered +SystemNotification = SystemNotificationAgentCompleted | SystemNotificationAgentIdle | SystemNotificationNewInboxMessage | SystemNotificationShellCompleted | SystemNotificationShellDetachedCompleted | SystemNotificationInstructionDiscovered | SystemNotificationUnclassified # The approval to add as a session-scoped rule @@ -9145,6 +9578,21 @@ def _load_UserToolSessionApproval(obj: Any) -> "UserToolSessionApproval": PermissionResult = PermissionApproved | PermissionApprovedForSession | PermissionApprovedForLocation | PermissionCancelled | PermissionDeniedByRules | PermissionDeniedNoApprovalRuleAndCouldNotRequestFromUser | PermissionDeniedInteractivelyByUser | PermissionDeniedByContentExclusionPolicy | PermissionDeniedByPermissionRequestHook +# Experimental: this enum is part of an experimental API and may change or be removed. +class AutoApprovalJudgeFailureReason(Enum): + "Why the auto-approval judge produced no usable recommendation. Present only alongside an `error` recommendation, where the human-readable reason is a fixed string and therefore cannot distinguish these cases. Intended to make a judge failure reportable by a consumer that has no access to the host's logs." + # The judge model call exceeded its deadline. + TIMEOUT = "timeout" + # The judge model call was cancelled before it returned. + ABORT = "abort" + # The judge model call completed but returned no content. + EMPTY_RESPONSE = "empty_response" + # The judge model call failed (for example a transport, authentication, or rate-limit error). + MODEL_ERROR = "model_error" + # The judge model replied, but the reply carried no ALLOW/DENY verdict. + PARSE_ERROR = "parse_error" + + # Experimental: this enum is part of an experimental API and may change or be removed. class AutoApprovalRecommendation(Enum): "Outcome of the auto-approval safety judge for a permission request. Present only when auto mode is enabled; its absence means the judge did not evaluate the request (auto mode was off)." @@ -9278,6 +9726,20 @@ class BinaryAssetType(Enum): RESOURCE = "resource" +class CompactionTrigger(Enum): + "What initiated a conversation compaction" + # Background compaction started automatically because context utilization crossed the background threshold. + THRESHOLD = "threshold" + # Compaction forced by a context-limit model response (e.g. HTTP 413) before retrying the request. + CONTEXT_LIMIT_RETRY = "context_limit_retry" + # User-requested compaction, e.g. the /compact command or the history.compact API. + MANUAL = "manual" + # Emergency compaction triggered by high process memory usage. + MEMORY_PRESSURE = "memory_pressure" + # Compaction requested while switching to a model with a smaller context window. + MODEL_SWITCH = "model_switch" + + class ContextTier(Enum): "Allowed values for the `ContextTier` enumeration." # Default context tier with standard context window size. @@ -9378,6 +9840,40 @@ class ManagedSettingsResolvedSource(Enum): NONE = "none" +class McpDiagnosticHttpExchangePhase(Enum): + "Phase of an observed MCP HTTP exchange." + # The outgoing HTTP request. + REQUEST = "request" + # The incoming HTTP response. + RESPONSE = "response" + + +class McpDiagnosticServerLogStream(Enum): + "Output stream for an MCP server log line." + # The process standard-error stream. + STDERR = "stderr" + + +class McpDiagnosticWireMessageDirection(Enum): + "Direction of an MCP wire message." + # The runtime sent the message to the MCP server. + OUTBOUND = "outbound" + # The runtime received the message from the MCP server. + INBOUND = "inbound" + + +class McpDiagnosticWireMessageKind(Enum): + "Category of an MCP wire message." + # A JSON-RPC request. + REQUEST = "request" + # A JSON-RPC response. + RESPONSE = "response" + # A JSON-RPC notification. + NOTIFICATION = "notification" + # A protocol or transport error represented as a diagnostic message. + ERROR = "error" + + class McpHeadersRefreshCompletedOutcome(Enum): "How the pending MCP headers refresh request resolved." # The host supplied dynamic headers. @@ -9562,6 +10058,14 @@ class ReasoningSummary(Enum): DETAILED = "detailed" +class ScheduleOrigin(Enum): + "Who created the schedule: `user` (an explicit user action such as `/every` or `/after`) or `model` (the agent via the `manage_schedule` tool). Gates whether a scheduled skill that opted out of model invocation may fire: only user-created schedules may." + # The schedule was created by an explicit user action, such as `/every` or `/after`. + USER = "user" + # The schedule was created by the agent via the `manage_schedule` tool. + MODEL = "model" + + class SessionLimitsExhaustedResponseAction(Enum): "User action selected for an exhausted session limit." # Increase the current max by an exact AI Credits amount. @@ -9708,7 +10212,7 @@ class WorkspaceFileChangedOperation(Enum): UPDATE = "update" -SessionEventData = SessionStartData | SessionResumeData | SessionRemoteSteerableChangedData | SessionErrorData | SessionIdleData | SessionTitleChangedData | SessionScheduleCreatedData | SessionScheduleCancelledData | SessionScheduleRearmedData | SessionAutopilotObjectiveChangedData | SessionInfoData | SessionWarningData | SessionModelChangeData | SessionModeChangedData | SessionSessionLimitsChangedData | SessionPermissionsChangedData | SessionPlanChangedData | SessionTodosChangedData | SessionWorkspaceFileChangedData | SessionHandoffData | SessionTruncationData | SessionSnapshotRewindData | SessionShutdownData | SessionUsageCheckpointData | SessionContextChangedData | SessionUsageInfoData | SessionCompactionStartData | SessionCompactionCompleteData | SessionTaskCompleteData | UserMessageData | PendingMessagesModifiedData | AssistantTurnStartData | AssistantTurnRetryData | AssistantIntentData | AssistantServerToolProgressData | AssistantReasoningData | AssistantReasoningDeltaData | AssistantToolCallDeltaData | AssistantStreamingDeltaData | AssistantMessageData | AssistantMessageStartData | AssistantMessageDeltaData | AssistantTurnEndData | AssistantIdleData | AssistantUsageData | ModelCallFailureData | ModelCallStartData | AbortData | ToolUserRequestedData | ToolExecutionStartData | ToolExecutionPartialResultData | ToolExecutionProgressData | ToolExecutionCompleteData | ToolSearchActivatedData | SkillInvokedData | SubagentStartedData | SubagentCompletedData | SubagentFailedData | SubagentSelectedData | SubagentDeselectedData | HookStartData | HookEndData | HookProgressData | SessionBinaryAssetData | SystemMessageData | SystemNotificationData | PermissionRequestedData | PermissionCompletedData | UserInputRequestedData | UserInputCompletedData | ElicitationRequestedData | ElicitationCompletedData | SamplingRequestedData | SamplingCompletedData | McpOauthRequiredData | McpOauthCompletedData | McpHeadersRefreshRequiredData | McpHeadersRefreshCompletedData | SessionCustomNotificationData | ExternalToolRequestedData | ExternalToolCompletedData | CommandQueuedData | CommandExecuteData | CommandCompletedData | AutoModeSwitchRequestedData | AutoModeSwitchCompletedData | SessionLimitsExhaustedRequestedData | SessionLimitsExhaustedCompletedData | SessionAutoModeResolvedData | SessionManagedSettingsResolvedData | SessionManagedSettingsEnforcedData | CommandsChangedData | CapabilitiesChangedData | ExitPlanModeRequestedData | ExitPlanModeCompletedData | SessionToolsUpdatedData | SessionBackgroundTasksChangedData | SessionSkillsLoadedData | SessionCustomAgentsUpdatedData | SessionMcpServersLoadedData | SessionMcpServerStatusChangedData | McpToolsListChangedData | McpResourcesListChangedData | McpPromptsListChangedData | SessionExtensionsLoadedData | SessionCanvasOpenedData | SessionCanvasRegistryChangedData | SessionCanvasClosedData | SessionCanvasUnavailableData | SessionCanvasRecordedData | SessionCanvasRemovedData | SessionExtensionsAttachmentsPushedData | McpAppToolCallCompleteData | RawSessionEventData | Data +SessionEventData = SessionStartData | SessionResumeData | SessionRemoteSteerableChangedData | SessionErrorData | SessionIdleData | SessionTitleChangedData | SessionScheduleCreatedData | SessionScheduleCancelledData | SessionScheduleRearmedData | SessionAutopilotObjectiveChangedData | SessionInfoData | SessionWarningData | SessionModelChangeData | SessionModeChangedData | SessionSessionLimitsChangedData | SessionPermissionsChangedData | SessionPlanChangedData | SessionTodosChangedData | SessionWorkspaceFileChangedData | SessionHandoffData | SessionTruncationData | SessionSnapshotRewindData | SessionShutdownData | SessionUsageCheckpointData | SessionContextChangedData | SessionUsageInfoData | SessionCompactionStartData | SessionCompactionCompleteData | SessionTaskCompleteData | UserMessageData | PendingMessagesModifiedData | AssistantTurnStartData | AssistantTurnRetryData | AssistantIntentData | AssistantServerToolProgressData | AssistantReasoningData | AssistantReasoningDeltaData | AssistantToolCallDeltaData | AssistantStreamingDeltaData | AssistantMessageData | AssistantMessageStartData | AssistantMessageDeltaData | AssistantTurnEndData | AssistantIdleData | AssistantUsageData | ModelCallFailureData | ModelCallStartData | AbortData | ToolUserRequestedData | ToolExecutionStartData | ToolExecutionPartialResultData | ToolExecutionProgressData | ToolExecutionCompleteData | ToolSearchActivatedData | SkillInvokedData | SubagentStartedData | SubagentCompletedData | SubagentFailedData | SubagentSelectedData | SubagentDeselectedData | HookStartData | HookEndData | HookProgressData | SessionBinaryAssetData | SystemMessageData | SystemNotificationData | PermissionRequestedData | PermissionCompletedData | UserInputRequestedData | UserInputCompletedData | ElicitationRequestedData | ElicitationCompletedData | SamplingRequestedData | SamplingCompletedData | McpOauthRequiredData | McpOauthCompletedData | McpHeadersRefreshRequiredData | McpHeadersRefreshCompletedData | McpDiagnosticData | SessionCustomNotificationData | ExternalToolRequestedData | ExternalToolCompletedData | CommandQueuedData | CommandExecuteData | CommandCompletedData | AutoModeSwitchRequestedData | AutoModeSwitchCompletedData | SessionLimitsExhaustedRequestedData | SessionLimitsExhaustedCompletedData | SessionAutoModeResolvedData | SessionManagedSettingsResolvedData | SessionManagedSettingsEnforcedData | CommandsChangedData | CapabilitiesChangedData | ExitPlanModeRequestedData | ExitPlanModeCompletedData | SessionToolsUpdatedData | SessionBackgroundTasksChangedData | FactoryRunUpdatedData | SessionSkillsLoadedData | SessionCustomAgentsUpdatedData | SessionMcpServersLoadedData | SessionMcpServerStatusChangedData | McpToolsListChangedData | McpResourcesListChangedData | McpPromptsListChangedData | SessionExtensionsLoadedData | SessionCanvasOpenedData | SessionCanvasRegistryChangedData | SessionCanvasClosedData | SessionCanvasUnavailableData | SessionCanvasRecordedData | SessionCanvasRemovedData | SessionExtensionsAttachmentsPushedData | McpAppToolCallCompleteData | RawSessionEventData | Data @dataclass @@ -9812,6 +10316,7 @@ def from_dict(obj: Any) -> "SessionEvent": case SessionEventType.MCP_OAUTH_COMPLETED: data = McpOauthCompletedData.from_dict(data_obj) case SessionEventType.MCP_HEADERS_REFRESH_REQUIRED: data = McpHeadersRefreshRequiredData.from_dict(data_obj) case SessionEventType.MCP_HEADERS_REFRESH_COMPLETED: data = McpHeadersRefreshCompletedData.from_dict(data_obj) + case SessionEventType.MCP_DIAGNOSTIC: data = McpDiagnosticData.from_dict(data_obj) case SessionEventType.SESSION_CUSTOM_NOTIFICATION: data = SessionCustomNotificationData.from_dict(data_obj) case SessionEventType.EXTERNAL_TOOL_REQUESTED: data = ExternalToolRequestedData.from_dict(data_obj) case SessionEventType.EXTERNAL_TOOL_COMPLETED: data = ExternalToolCompletedData.from_dict(data_obj) @@ -9831,6 +10336,7 @@ def from_dict(obj: Any) -> "SessionEvent": case SessionEventType.EXIT_PLAN_MODE_COMPLETED: data = ExitPlanModeCompletedData.from_dict(data_obj) case SessionEventType.SESSION_TOOLS_UPDATED: data = SessionToolsUpdatedData.from_dict(data_obj) case SessionEventType.SESSION_BACKGROUND_TASKS_CHANGED: data = SessionBackgroundTasksChangedData.from_dict(data_obj) + case SessionEventType.FACTORY_RUN_UPDATED: data = FactoryRunUpdatedData.from_dict(data_obj) case SessionEventType.SESSION_SKILLS_LOADED: data = SessionSkillsLoadedData.from_dict(data_obj) case SessionEventType.SESSION_CUSTOM_AGENTS_UPDATED: data = SessionCustomAgentsUpdatedData.from_dict(data_obj) case SessionEventType.SESSION_MCP_SERVERS_LOADED: data = SessionMcpServersLoadedData.from_dict(data_obj) @@ -9926,6 +10432,7 @@ def session_event_to_dict(x: SessionEvent) -> Any: "AttachmentSelectionDetails", "AttachmentSelectionDetailsEnd", "AttachmentSelectionDetailsStart", + "AutoApprovalJudgeFailureReason", "AutoApprovalRecommendation", "AutoModeResolvedReasoningBucket", "AutoModeSwitchCompletedData", @@ -9957,6 +10464,7 @@ def session_event_to_dict(x: SessionEvent) -> Any: "CommandsChangedData", "CompactionCompleteCompactionTokensUsed", "CompactionCompleteCompactionTokensUsedCopilotUsageTokenDetail", + "CompactionTrigger", "ContextTier", "CustomAgentsUpdatedAgent", "Data", @@ -9975,6 +10483,7 @@ def session_event_to_dict(x: SessionEvent) -> Any: "ExtensionsLoadedExtensionStatus", "ExternalToolCompletedData", "ExternalToolRequestedData", + "FactoryRunUpdatedData", "GitHubRepoRef", "HandoffRepository", "HandoffSourceType", @@ -9990,6 +10499,16 @@ def session_event_to_dict(x: SessionEvent) -> Any: "McpAppToolCallCompleteError", "McpAppToolCallCompleteToolMeta", "McpAppToolCallCompleteToolMetaUI", + "McpDiagnosticData", + "McpDiagnosticDetail", + "McpDiagnosticHttpExchange", + "McpDiagnosticHttpExchangePhase", + "McpDiagnosticProcessLifecycle", + "McpDiagnosticServerLog", + "McpDiagnosticServerLogStream", + "McpDiagnosticWireMessage", + "McpDiagnosticWireMessageDirection", + "McpDiagnosticWireMessageKind", "McpHeadersRefreshCompletedData", "McpHeadersRefreshCompletedOutcome", "McpHeadersRefreshRequiredData", @@ -10056,6 +10575,7 @@ def session_event_to_dict(x: SessionEvent) -> Any: "PermissionRequestRead", "PermissionRequestShell", "PermissionRequestShellCommand", + "PermissionRequestShellCommandSegment", "PermissionRequestShellPossibleUrl", "PermissionRequestUrl", "PermissionRequestWrite", @@ -10070,6 +10590,7 @@ def session_event_to_dict(x: SessionEvent) -> Any: "ReasoningSummary", "SamplingCompletedData", "SamplingRequestedData", + "ScheduleOrigin", "SessionAutoModeResolvedData", "SessionAutopilotObjectiveChangedData", "SessionBackgroundTasksChangedData", @@ -10155,6 +10676,7 @@ def session_event_to_dict(x: SessionEvent) -> Any: "SystemNotificationNewInboxMessage", "SystemNotificationShellCompleted", "SystemNotificationShellDetachedCompleted", + "SystemNotificationUnclassified", "ToolExecutionCompleteContent", "ToolExecutionCompleteContentAudio", "ToolExecutionCompleteContentImage", diff --git a/rust/src/generated/session_events.rs b/rust/src/generated/session_events.rs index abc7dca626..9a3119c712 100644 --- a/rust/src/generated/session_events.rs +++ b/rust/src/generated/session_events.rs @@ -174,6 +174,15 @@ pub enum SessionEventType { McpHeadersRefreshRequired, #[serde(rename = "mcp.headers_refresh_completed")] McpHeadersRefreshCompleted, + /// + ///
+ /// + /// **Experimental.** This type is part of an experimental wire-protocol surface + /// and may change or be removed in future SDK or CLI releases. + /// + ///
+ #[serde(rename = "mcp.diagnostic")] + McpDiagnostic, #[serde(rename = "session.custom_notification")] SessionCustomNotification, #[serde(rename = "external_tool.requested")] @@ -233,6 +242,15 @@ pub enum SessionEventType { SessionToolsUpdated, #[serde(rename = "session.background_tasks_changed")] SessionBackgroundTasksChanged, + /// + ///
+ /// + /// **Experimental.** This type is part of an experimental wire-protocol surface + /// and may change or be removed in future SDK or CLI releases. + /// + ///
+ #[serde(rename = "factory.run_updated")] + FactoryRunUpdated, #[serde(rename = "session.skills_loaded")] SessionSkillsLoaded, #[serde(rename = "session.custom_agents_updated")] @@ -475,6 +493,8 @@ pub enum SessionEventData { McpHeadersRefreshRequired(McpHeadersRefreshRequiredData), #[serde(rename = "mcp.headers_refresh_completed")] McpHeadersRefreshCompleted(McpHeadersRefreshCompletedData), + #[serde(rename = "mcp.diagnostic")] + McpDiagnostic(McpDiagnosticData), #[serde(rename = "session.custom_notification")] SessionCustomNotification(SessionCustomNotificationData), #[serde(rename = "external_tool.requested")] @@ -534,6 +554,15 @@ pub enum SessionEventData { SessionToolsUpdated(SessionToolsUpdatedData), #[serde(rename = "session.background_tasks_changed")] SessionBackgroundTasksChanged(SessionBackgroundTasksChangedData), + /// + ///
+ /// + /// **Experimental.** This type is part of an experimental wire-protocol surface + /// and may change or be removed in future SDK or CLI releases. + /// + ///
+ #[serde(rename = "factory.run_updated")] + FactoryRunUpdated(FactoryRunUpdatedData), #[serde(rename = "session.skills_loaded")] SessionSkillsLoaded(SessionSkillsLoadedData), #[serde(rename = "session.custom_agents_updated")] @@ -734,7 +763,7 @@ pub struct SessionResumeData { /// Context tier currently selected at resume time; null when no tier is active #[serde(skip_serializing_if = "Option::is_none")] pub context_tier: Option, - /// When true, tool calls and permission requests left in flight by the previous session lifetime remain pending after resume and the agentic loop awaits their results. User sends are queued behind the pending work until all such requests reach a terminal state. When false (the default), any such tool calls and permission requests are immediately marked as interrupted on resume. + /// When true, tool calls and permission requests left in flight by the previous session lifetime remain pending after resume and the agentic loop awaits their results. User sends are queued behind the pending work until all such requests reach a terminal state. When false or omitted, pending work is normally marked as interrupted unless the resume passively joined live work owned by another client; sessionWasActive distinguishes that case. #[serde(skip_serializing_if = "Option::is_none")] pub continue_pending_work: Option, /// Total number of persisted events in the session at the time of resume @@ -759,7 +788,7 @@ pub struct SessionResumeData { /// Session limits currently configured at resume time; null when no limits are active #[serde(skip_serializing_if = "Option::is_none")] pub session_limits: Option, - /// True when this resume attached to a session that the runtime already had running in-memory (for example, an extension joining a session another client was actively driving). False (or omitted) for cold resumes — the runtime had to reconstitute the session from its persisted event log. + /// True when this resume passively joined a session that already had live work running in the runtime - an agent turn, a native queue run, a queued resume continuation, or an in-flight send (for example, an extension joining a session another client was actively driving). False (or omitted) when the session had no live work or when the resume explicitly abandoned pending work, including cold resumes and suspended sessions that remain resident in memory. #[serde(skip_serializing_if = "Option::is_none")] pub session_was_active: Option, /// Output verbosity level used for model calls, if applicable (e.g. "low", "medium", "high") @@ -841,6 +870,9 @@ pub struct SessionScheduleCreatedData { /// Interval between ticks in milliseconds (relative-interval schedules) #[serde(skip_serializing_if = "Option::is_none")] pub interval_ms: Option, + /// Who created the schedule (`user` or `model`). Persisted so a resumed session keeps gating non-user schedules from firing skills that opted out of model invocation. Absent on entries created before this field existed; a missing origin fails closed (treated the same as a non-user origin), so such a schedule may not resolve a `disable-model-invocation` skill. + #[serde(skip_serializing_if = "Option::is_none")] + pub origin: Option, /// Prompt text that gets enqueued on every tick pub prompt: String, /// Whether the schedule re-arms after each tick (`/every`) or fires once (`/after`) @@ -1328,15 +1360,24 @@ pub struct SessionCompactionStartData { /// Token count from non-system messages (user, assistant, tool) at compaction start #[serde(skip_serializing_if = "Option::is_none")] pub conversation_tokens: Option, + /// Total context tokens (system + conversation + tool definitions) at compaction start, when known + #[serde(skip_serializing_if = "Option::is_none")] + pub current_tokens: Option, /// Model identifier used for compaction, when known #[serde(skip_serializing_if = "Option::is_none")] pub model: Option, /// Token count from system message(s) at compaction start #[serde(skip_serializing_if = "Option::is_none")] pub system_tokens: Option, + /// Model context window token limit the compaction is targeting, when known + #[serde(skip_serializing_if = "Option::is_none")] + pub token_limit: Option, /// Token count from tool definitions at compaction start #[serde(skip_serializing_if = "Option::is_none")] pub tool_definitions_tokens: Option, + /// What initiated this compaction, when known + #[serde(skip_serializing_if = "Option::is_none")] + pub trigger: Option, } /// Token usage detail for a single billing category @@ -1445,12 +1486,18 @@ pub struct SessionCompactionCompleteData { /// Token count from system message(s) after compaction #[serde(skip_serializing_if = "Option::is_none")] pub system_tokens: Option, + /// Model context window token limit the compaction was targeting, when known + #[serde(skip_serializing_if = "Option::is_none")] + pub token_limit: Option, /// Number of tokens removed during compaction #[serde(skip_serializing_if = "Option::is_none")] pub tokens_removed: Option, /// Token count from tool definitions after compaction #[serde(skip_serializing_if = "Option::is_none")] pub tool_definitions_tokens: Option, + /// What initiated this compaction, when known + #[serde(skip_serializing_if = "Option::is_none")] + pub trigger: Option, } /// Session event "session.task_complete". Task completion notification with summary from the agent @@ -1492,7 +1539,7 @@ pub struct UserMessageData { /// Parent agent task ID for background telemetry correlated to this user turn #[serde(skip_serializing_if = "Option::is_none")] pub parent_agent_task_id: Option, - /// Origin of this message, used for timeline filtering (e.g., "skill-pdf" for skill-injected messages that should be hidden from the user) + /// Origin of this message, used for timeline filtering and attribution (e.g., `skill-pdf` for hidden skill injection or `agent-` for an inter-agent prompt) #[serde(skip_serializing_if = "Option::is_none")] pub source: Option, /// Normalized document MIME types that were sent natively instead of through tagged_files XML @@ -1564,6 +1611,8 @@ pub struct AssistantReasoningData { pub content: String, /// Unique identifier for this reasoning block pub reasoning_id: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub rte: Option, } /// Session event "assistant.reasoning_delta". Streaming reasoning delta for incremental extended thinking updates @@ -1792,6 +1841,8 @@ pub struct AssistantMessageData { /// GitHub request tracing ID (x-github-request-id header) for correlating with server-side logs #[serde(skip_serializing_if = "Option::is_none")] pub request_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub rte: Option, /// Neutral provider-tagged server-side tool-use payload (tool search, advisor) for verbatim round-tripping #[serde(skip_serializing_if = "Option::is_none")] pub server_tools: Option, @@ -1968,6 +2019,9 @@ pub struct AssistantUsageData { /// Number of input tokens consumed #[serde(skip_serializing_if = "Option::is_none")] pub input_tokens: Option, + /// Coarse classification of the interaction that produced this call, mirroring the session's per-request agent context (e.g. `conversation-agent`, `conversation-subagent`, `conversation-sampling`, `conversation-background`, `conversation-compaction`, `conversation-user`). Non-billing; lets consumers attribute a model call to a call class (e.g. sub-agent/sidekick) independently of the billing initiator. Absent when the runtime did not classify the request. + #[serde(skip_serializing_if = "Option::is_none")] + pub interaction_type: Option, /// Average inter-token latency in milliseconds. Only available for streaming requests #[serde(skip_serializing_if = "Option::is_none")] pub inter_token_latency_ms: Option, @@ -1994,6 +2048,8 @@ pub struct AssistantUsageData { /// Number of output tokens used for reasoning (e.g., chain-of-thought) #[serde(skip_serializing_if = "Option::is_none")] pub reasoning_tokens: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub rte: Option, /// Copilot service request ID (x-copilot-service-request-id header) for CAPI log correlation #[serde(skip_serializing_if = "Option::is_none")] pub service_request_id: Option, @@ -2082,6 +2138,8 @@ pub struct ModelCallFailureData { /// Content-free structural summary of the failing request. Contains only counts and shape flags (no prompt content), so it is safe for unrestricted telemetry. Populated only for client-error (4xx) failures. #[serde(skip_serializing_if = "Option::is_none")] pub request_fingerprint: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub rte: Option, /// Copilot service request ID (x-copilot-service-request-id header) for CAPI log correlation #[serde(skip_serializing_if = "Option::is_none")] pub service_request_id: Option, @@ -2102,6 +2160,10 @@ pub struct ModelCallStartData { /// Model identifier used for this API call, when known #[serde(skip_serializing_if = "Option::is_none")] pub model: Option, + /// Previous response or interaction identifier included in the model request, when present + #[doc(hidden)] + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) previous_response_id: Option, /// Identifier of the assistant turn that initiated the model call pub turn_id: String, } @@ -2196,6 +2258,8 @@ pub struct ToolExecutionStartData { #[deprecated] #[serde(skip_serializing_if = "Option::is_none")] pub parent_tool_call_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub rte: Option, /// Shell-tool path hints derived from the command at start time for shell tools (bash/powershell/local_shell). Produced by the same shell-aware extractor as PermissionRequestShell.possiblePaths, so it is present even when the command is auto-approved and no permission request fires. Absent for non-shell tools. #[serde(skip_serializing_if = "Option::is_none")] pub shell_tool_info: Option, @@ -2636,6 +2700,8 @@ pub struct ToolExecutionCompleteData { /// Tool execution result on success #[serde(skip_serializing_if = "Option::is_none")] pub result: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub rte: Option, /// Whether this tool execution ran inside a sandbox container #[serde(skip_serializing_if = "Option::is_none")] pub sandboxed: Option, @@ -2877,6 +2943,9 @@ pub struct SystemMessageMetadata { pub struct SystemMessageData { /// The system or developer prompt text sent as model input pub content: String, + /// Logical interaction identifier for the model run receiving this prompt + #[serde(skip_serializing_if = "Option::is_none")] + pub interaction_id: Option, /// Metadata about the prompt template and its construction #[serde(skip_serializing_if = "Option::is_none")] pub metadata: Option, @@ -2907,6 +2976,16 @@ pub struct PermissionRequestShellCommand { pub read_only: bool, } +/// A parsed shell command segment used for argument-aware managed policy matching. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PermissionRequestShellCommandSegment { + /// Full text of this command segment, including arguments + pub full_command_text: String, + /// Command identifier (e.g., executable name) + pub identifier: String, +} + /// A URL that may be accessed by a command in a shell permission request. #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -2923,6 +3002,9 @@ pub struct PermissionRequestShell { pub can_offer_session_approval: bool, /// Parsed command identifiers found in the command text pub commands: Vec, + /// Parsed command segments, including arguments, used for managed policy matching + #[serde(skip_serializing_if = "Option::is_none")] + pub command_segments: Option>, /// The complete shell command text to be executed pub full_command_text: String, /// Whether the command includes a file write redirection (e.g., > or >>) @@ -2931,6 +3013,9 @@ pub struct PermissionRequestShell { pub intention: String, /// Permission kind discriminator pub kind: PermissionRequestShellKind, + /// Whether managed policy requires a human response and forbids host auto-approval + #[serde(skip_serializing_if = "Option::is_none")] + pub managed_approval_required: Option, /// File paths that may be read or written by the command pub possible_paths: Vec, /// URLs that may be accessed by the command @@ -2963,6 +3048,9 @@ pub struct PermissionRequestWrite { pub intention: String, /// Permission kind discriminator pub kind: PermissionRequestWriteKind, + /// Whether managed policy requires a human response and forbids host auto-approval + #[serde(skip_serializing_if = "Option::is_none")] + pub managed_approval_required: Option, /// Complete new file contents for newly created files #[serde(skip_serializing_if = "Option::is_none")] pub new_file_contents: Option, @@ -2985,6 +3073,9 @@ pub struct PermissionRequestRead { pub intention: String, /// Permission kind discriminator pub kind: PermissionRequestReadKind, + /// Whether managed policy requires a human response and forbids host auto-approval + #[serde(skip_serializing_if = "Option::is_none")] + pub managed_approval_required: Option, /// Path of the file or directory being read pub path: String, /// True when the model has requested to run this search outside the sandbox (it set requestSandboxBypass: true and the host opted in via sandbox.allowBypass). This is a request, not a grant: the search runs unsandboxed only if the user approves this permission request. Hosts should highlight the elevated risk in the approval UI. @@ -3028,6 +3119,9 @@ pub struct PermissionRequestUrl { pub intention: String, /// Permission kind discriminator pub kind: PermissionRequestUrlKind, + /// Whether managed policy requires a human response and forbids host auto-approval + #[serde(skip_serializing_if = "Option::is_none")] + pub managed_approval_required: Option, /// True when this URL fetch is requesting to bypass the sandbox network policy: either the model set requestSandboxBypass: true, or the tool re-issued the request as an interactive bypass after the network policy denied the approved URL (host opted in via sandbox.allowBypass). This is a request, not a grant: the fetch runs only if the user approves this permission request. Hosts should highlight the elevated risk in the approval UI. #[serde(skip_serializing_if = "Option::is_none")] pub request_sandbox_bypass: Option, @@ -3148,6 +3242,12 @@ pub struct PermissionRequestExtensionPermissionAccess { #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PermissionAutoApproval { + /// Classified cause of an `error` recommendation. Absent for every other recommendation. + #[serde(skip_serializing_if = "Option::is_none")] + pub failure_reason: Option, + /// Model id that produced the recommendation, when the judge was consulted and reported one. Absent for `excluded` (the judge was not consulted) and for failures that occurred before a model was selected. + #[serde(skip_serializing_if = "Option::is_none")] + pub model: Option, /// Human-readable reason for the judge's recommendation, when available. #[serde(skip_serializing_if = "Option::is_none")] pub reason: Option, @@ -3179,6 +3279,9 @@ pub struct PermissionPromptRequestCommands { pub intention: String, /// Prompt kind discriminator pub kind: PermissionPromptRequestCommandsKind, + /// Whether managed policy requires a human response and forbids host auto-approval + #[serde(skip_serializing_if = "Option::is_none")] + pub managed_approval_required: Option, /// Tool call ID that triggered this permission request #[serde(skip_serializing_if = "Option::is_none")] pub tool_call_id: Option, @@ -3211,6 +3314,9 @@ pub struct PermissionPromptRequestWrite { pub intention: String, /// Prompt kind discriminator pub kind: PermissionPromptRequestWriteKind, + /// Whether managed policy requires a human response and forbids host auto-approval + #[serde(skip_serializing_if = "Option::is_none")] + pub managed_approval_required: Option, /// Complete new file contents for newly created files #[serde(skip_serializing_if = "Option::is_none")] pub new_file_contents: Option, @@ -3237,6 +3343,9 @@ pub struct PermissionPromptRequestRead { pub intention: String, /// Prompt kind discriminator pub kind: PermissionPromptRequestReadKind, + /// Whether managed policy requires a human response and forbids host auto-approval + #[serde(skip_serializing_if = "Option::is_none")] + pub managed_approval_required: Option, /// Path of the file or directory being read pub path: String, /// Tool call ID that triggered this permission request @@ -3292,6 +3401,9 @@ pub struct PermissionPromptRequestUrl { pub intention: String, /// Prompt kind discriminator pub kind: PermissionPromptRequestUrlKind, + /// Whether managed policy requires a human response and forbids host auto-approval + #[serde(skip_serializing_if = "Option::is_none")] + pub managed_approval_required: Option, /// True when this URL fetch is requesting to bypass the sandbox network policy: either the model set requestSandboxBypass: true, or the tool re-issued the request as an interactive bypass after the network policy denied the approved URL (host opted in via sandbox.allowBypass). This is a request, not a grant: the fetch runs only if the user approves this permission request. Hosts should highlight the elevated risk in the approval UI. #[serde(skip_serializing_if = "Option::is_none")] pub request_sandbox_bypass: Option, @@ -3490,6 +3602,9 @@ pub struct PermissionRequestedData { /// When true, this permission was already resolved by a permissionRequest hook and requires no client action #[serde(skip_serializing_if = "Option::is_none")] pub resolved_by_hook: Option, + /// Neutral risk metadata supplied by the tool host. Consumers may display this value but must not use it to bypass the permission decision. + #[serde(skip_serializing_if = "Option::is_none")] + pub risk_assessment: Option, } /// Permission response variant indicating the request was approved without persisting an approval rule. @@ -3911,6 +4026,112 @@ pub struct McpHeadersRefreshCompletedData { pub request_id: RequestId, } +/// A redacted MCP protocol message observed on the server transport. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct McpDiagnosticWireMessage { + /// Size in bytes of the observed message before any diagnostic payload clipping. + pub byte_size: i64, + /// Whether the runtime sent the message to the server or received it from the server. + pub direction: McpDiagnosticWireMessageDirection, + /// Diagnostic kind discriminator + pub kind: McpDiagnosticWireMessageKind, + /// MCP protocol message category. + pub message_kind: McpDiagnosticWireMessageKind, + /// MCP method name, when the diagnostic message is associated with a method. + #[serde(skip_serializing_if = "Option::is_none")] + pub method: Option, + /// Already-serialized JSON payload after redaction. Omitted when payload capture is unavailable or excluded; when present, it may be clipped as indicated by `truncated`. + #[serde(skip_serializing_if = "Option::is_none")] + pub payload: Option, + /// JSON-RPC request ID serialized as a string, when the diagnostic message is associated with a request or response. + #[serde(skip_serializing_if = "Option::is_none")] + pub request_id: Option, + /// Whether the captured `payload` was clipped to the diagnostic size cap. + pub truncated: bool, +} + +/// A redacted HTTP request or response observed while communicating with an MCP server. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct McpDiagnosticHttpExchange { + /// Elapsed duration in milliseconds for the HTTP exchange, when measured. + #[serde(skip_serializing_if = "Option::is_none")] + pub duration_ms: Option, + /// Redacted HTTP headers as name-to-value pairs, when available. + #[serde(skip_serializing_if = "Option::is_none")] + pub headers: Option>, + /// HTTP method used by the request, when available. + #[serde(skip_serializing_if = "Option::is_none")] + pub http_method: Option, + /// Diagnostic kind discriminator + pub kind: McpDiagnosticHttpExchangeKind, + /// Whether this diagnostic describes the outgoing HTTP request or incoming HTTP response. + pub phase: McpDiagnosticHttpExchangePhase, + /// Whether the exchange belongs to the Server-Sent Events stream leg, when applicable. + #[serde(skip_serializing_if = "Option::is_none")] + pub sse: Option, + /// HTTP response status code, when this diagnostic is associated with a response. + #[serde(skip_serializing_if = "Option::is_none")] + pub status_code: Option, + /// Redacted HTTP URL, when available. + #[serde(skip_serializing_if = "Option::is_none")] + pub url: Option, +} + +/// A line emitted by an MCP server process. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct McpDiagnosticServerLog { + /// Diagnostic kind discriminator + pub kind: McpDiagnosticServerLogKind, + /// One server log line, without a trailing line terminator. + pub line: String, + /// Operating-system process ID of the MCP server process, when known. + #[serde(skip_serializing_if = "Option::is_none")] + pub pid: Option, + /// Process output stream that emitted the line. + pub stream: McpDiagnosticServerLogStream, +} + +/// MCP server process startup or termination details. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct McpDiagnosticProcessLifecycle { + /// Arguments used to launch the MCP server process, when applicable. + #[serde(skip_serializing_if = "Option::is_none")] + pub args: Option>, + /// Command used to launch the MCP server process, when applicable. + #[serde(skip_serializing_if = "Option::is_none")] + pub command: Option, + /// Names of environment variables supplied to the MCP server process. Values are never included. + #[serde(skip_serializing_if = "Option::is_none")] + pub env_keys: Option>, + /// Process exit code, when the MCP server process has exited normally. + #[serde(skip_serializing_if = "Option::is_none")] + pub exit_code: Option, + /// Diagnostic kind discriminator + pub kind: McpDiagnosticProcessLifecycleKind, + /// Operating-system process ID of the MCP server process, when known. + #[serde(skip_serializing_if = "Option::is_none")] + pub pid: Option, + /// Signal that terminated the MCP server process, when applicable. + #[serde(skip_serializing_if = "Option::is_none")] + pub signal: Option, +} + +/// Session event "mcp.diagnostic". Verbose MCP diagnostic data. Payloads are redacted and size-capped; `truncated` indicates that a captured payload was clipped. This event is ephemeral and high-volume, and is emitted only when a consumer has registered interest in `mcp.diagnostic` via `session.eventLog.registerInterest`. +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct McpDiagnosticData { + /// Detailed diagnostic payload discriminated by `kind`. + pub detail: McpDiagnosticDetail, + /// Configured name of the MCP server associated with this diagnostic. + pub server_name: String, + /// Transport mechanism used by the MCP server: stdio, HTTP, Server-Sent Events, or in-memory. + pub transport: McpServerTransport, +} + /// Session event "session.custom_notification". Opaque custom notification data. Consumers may branch on source and name, but payload semantics are source-defined. #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -4104,6 +4325,9 @@ pub struct SessionManagedSettingsResolvedData { pub fail_closed: bool, /// The setting keys under enterprise management in the effective managed settings (e.g. `model`, `enabledPlugins`, `permissions`). Empty when no managed settings are in force. pub managed_keys: Vec, + /// Whether server and device each supplied a permission allowlist, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. + #[serde(skip_serializing_if = "Option::is_none")] + pub permissions_allow_intersected: Option, /// Whether the server (account/org) managed-settings layer was present pub server_managed: bool, /// The effective (resolved) managed settings values, so clients can render exactly what is enforced. Absent when no managed policy is in force. @@ -4229,6 +4453,22 @@ pub struct SessionToolsUpdatedData { #[serde(rename_all = "camelCase")] pub struct SessionBackgroundTasksChangedData {} +/// Session event "factory.run_updated". Ephemeral invalidation signal for a changed factory run. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct FactoryRunUpdatedData { + /// Monotonic revision now available for the run. + pub revision: i64, + pub run_id: String, +} + /// A single resolved skill in `session.skills_loaded`, including source, invocability, enabled state, path, and argument hint. #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -4703,6 +4943,21 @@ pub enum Verbosity { Unknown, } +/// Who created the schedule: `user` (an explicit user action such as `/every` or `/after`) or `model` (the agent via the `manage_schedule` tool). Gates whether a scheduled skill that opted out of model invocation may fire: only user-created schedules may. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ScheduleOrigin { + /// The schedule was created by an explicit user action, such as `/every` or `/after`. + #[serde(rename = "user")] + User, + /// The schedule was created by the agent via the `manage_schedule` tool. + #[serde(rename = "model")] + Model, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// The type of operation performed on the autopilot objective state file #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum AutopilotObjectiveChangedOperation { @@ -4848,6 +5103,30 @@ pub enum ShutdownType { Unknown, } +/// What initiated a conversation compaction +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum CompactionTrigger { + /// Background compaction started automatically because context utilization crossed the background threshold. + #[serde(rename = "threshold")] + Threshold, + /// Compaction forced by a context-limit model response (e.g. HTTP 413) before retrying the request. + #[serde(rename = "context_limit_retry")] + ContextLimitRetry, + /// User-requested compaction, e.g. the /compact command or the history.compact API. + #[serde(rename = "manual")] + Manual, + /// Emergency compaction triggered by high process memory usage. + #[serde(rename = "memory_pressure")] + MemoryPressure, + /// Compaction requested while switching to a model with a smaller context window. + #[serde(rename = "model_switch")] + ModelSwitch, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// The agent mode that was active when this message was sent #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum UserMessageAgentMode { @@ -5325,6 +5604,37 @@ pub enum PermissionRequest { ExtensionPermissionAccess(PermissionRequestExtensionPermissionAccess), } +/// Why the auto-approval judge produced no usable recommendation. Present only alongside an `error` recommendation, where the human-readable reason is a fixed string and therefore cannot distinguish these cases. Intended to make a judge failure reportable by a consumer that has no access to the host's logs. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum AutoApprovalJudgeFailureReason { + /// The judge model call exceeded its deadline. + #[serde(rename = "timeout")] + Timeout, + /// The judge model call was cancelled before it returned. + #[serde(rename = "abort")] + Abort, + /// The judge model call completed but returned no content. + #[serde(rename = "empty_response")] + EmptyResponse, + /// The judge model call failed (for example a transport, authentication, or rate-limit error). + #[serde(rename = "model_error")] + ModelError, + /// The judge model replied, but the reply carried no ALLOW/DENY verdict. + #[serde(rename = "parse_error")] + ParseError, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// Outcome of the auto-approval safety judge for a permission request. Present only when auto mode is enabled; its absence means the judge did not evaluate the request (auto mode was off). /// ///
@@ -5764,6 +6074,111 @@ pub enum McpHeadersRefreshCompletedOutcome { Unknown, } +/// Direction of an MCP wire message. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum McpDiagnosticWireMessageDirection { + /// The runtime sent the message to the MCP server. + #[serde(rename = "outbound")] + Outbound, + /// The runtime received the message from the MCP server. + #[serde(rename = "inbound")] + Inbound, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + +/// Diagnostic kind discriminator +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum McpDiagnosticWireMessageKind { + #[serde(rename = "wire_message")] + #[default] + WireMessage, +} + +/// Diagnostic kind discriminator +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum McpDiagnosticHttpExchangeKind { + #[serde(rename = "http_exchange")] + #[default] + HttpExchange, +} + +/// Phase of an observed MCP HTTP exchange. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum McpDiagnosticHttpExchangePhase { + /// The outgoing HTTP request. + #[serde(rename = "request")] + Request, + /// The incoming HTTP response. + #[serde(rename = "response")] + Response, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + +/// Diagnostic kind discriminator +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum McpDiagnosticServerLogKind { + #[serde(rename = "server_log")] + #[default] + ServerLog, +} + +/// Output stream for an MCP server log line. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum McpDiagnosticServerLogStream { + /// The process standard-error stream. + #[serde(rename = "stderr")] + Stderr, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + +/// Diagnostic kind discriminator +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum McpDiagnosticProcessLifecycleKind { + #[serde(rename = "process_lifecycle")] + #[default] + ProcessLifecycle, +} + +/// Detailed MCP diagnostic payload discriminated by `kind`. +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum McpDiagnosticDetail { + WireMessage(McpDiagnosticWireMessage), + HttpExchange(McpDiagnosticHttpExchange), + ServerLog(McpDiagnosticServerLog), + ProcessLifecycle(McpDiagnosticProcessLifecycle), +} + +/// Transport mechanism: stdio, http, sse (deprecated), or memory (in-process MCP server) +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum McpServerTransport { + /// Server communicates over stdio with a local child process. + #[serde(rename = "stdio")] + Stdio, + /// Server communicates over streamable HTTP. + #[serde(rename = "http")] + Http, + /// Server communicates over Server-Sent Events (deprecated). + #[serde(rename = "sse")] + Sse, + /// Server is backed by an in-memory runtime implementation. + #[serde(rename = "memory")] + Memory, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// The user's auto-mode-switch choice #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum AutoModeSwitchResponse { @@ -5974,27 +6389,6 @@ pub enum McpServerStatus { Unknown, } -/// Transport mechanism: stdio, http, sse (deprecated), or memory (in-process MCP server) -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -pub enum McpServerTransport { - /// Server communicates over stdio with a local child process. - #[serde(rename = "stdio")] - Stdio, - /// Server communicates over streamable HTTP. - #[serde(rename = "http")] - Http, - /// Server communicates over Server-Sent Events (deprecated). - #[serde(rename = "sse")] - Sse, - /// Server is backed by an in-memory runtime implementation. - #[serde(rename = "memory")] - Memory, - /// Unknown variant for forward compatibility. - #[default] - #[serde(other)] - Unknown, -} - /// Discovery source #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum ExtensionsLoadedExtensionSource {