diff --git a/dotnet/src/Generated/Rpc.cs b/dotnet/src/Generated/Rpc.cs index cbb91eca9..a4cd9abae 100644 --- a/dotnet/src/Generated/Rpc.cs +++ b/dotnet/src/Generated/Rpc.cs @@ -6185,6 +6185,8 @@ public static void RegisterClientSessionApiHandlers(JsonRpc rpc, FuncNested data type for EmbeddedTextResourceContents. +public partial class EmbeddedTextResourceContents +{ + /// MIME type of the text content. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("mimeType")] + public string? MimeType { get; set; } + + /// Text content of the resource. + [JsonPropertyName("text")] + public required string Text { get; set; } + + /// URI identifying the resource. + [JsonPropertyName("uri")] + public required string Uri { get; set; } +} + +/// Nested data type for EmbeddedBlobResourceContents. +public partial class EmbeddedBlobResourceContents +{ + /// Base64-encoded binary content of the resource. + [Base64String] + [JsonPropertyName("blob")] + public required string Blob { get; set; } + + /// MIME type of the blob content. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("mimeType")] + public string? MimeType { get; set; } + + /// URI identifying the resource. + [JsonPropertyName("uri")] + public required string Uri { get; set; } +} + +/// The embedded resource contents, either text or base64-encoded binary. +/// JSON union data type for ToolExecutionCompleteContentResourceDetails. +[JsonConverter(typeof(Converter))] +public sealed partial class ToolExecutionCompleteContentResourceDetails +{ + /// Gets the value when this instance contains . + public EmbeddedTextResourceContents? EmbeddedTextResourceContents { get; } + + /// Gets the value when this instance contains . + public EmbeddedBlobResourceContents? EmbeddedBlobResourceContents { get; } + + /// Initializes a new instance of the class from . + public ToolExecutionCompleteContentResourceDetails(EmbeddedTextResourceContents value) + { + ArgumentNullException.ThrowIfNull(value); + EmbeddedTextResourceContents = value; + } + + /// Converts to . + public static implicit operator ToolExecutionCompleteContentResourceDetails(EmbeddedTextResourceContents value) => new(value); + + /// Initializes a new instance of the class from . + public ToolExecutionCompleteContentResourceDetails(EmbeddedBlobResourceContents value) + { + ArgumentNullException.ThrowIfNull(value); + EmbeddedBlobResourceContents = value; + } + + /// Converts to . + public static implicit operator ToolExecutionCompleteContentResourceDetails(EmbeddedBlobResourceContents value) => new(value); + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override ToolExecutionCompleteContentResourceDetails Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.Null) + { + throw new JsonException("Expected JSON object for ToolExecutionCompleteContentResourceDetails."); + } + + using var document = JsonDocument.ParseValue(ref reader); + var element = document.RootElement; + if (element.ValueKind == JsonValueKind.Object && element.TryGetProperty("text", out _) && !element.TryGetProperty("blob", out _)) + { + var embeddedTextResourceContents = JsonSerializer.Deserialize(element, SessionEventsJsonContext.Default.EmbeddedTextResourceContents); + return embeddedTextResourceContents is null ? throw new JsonException("Expected EmbeddedTextResourceContents value.") : new ToolExecutionCompleteContentResourceDetails(embeddedTextResourceContents); + } + if (element.ValueKind == JsonValueKind.Object && element.TryGetProperty("blob", out _) && !element.TryGetProperty("text", out _)) + { + var embeddedBlobResourceContents = JsonSerializer.Deserialize(element, SessionEventsJsonContext.Default.EmbeddedBlobResourceContents); + return embeddedBlobResourceContents is null ? throw new JsonException("Expected EmbeddedBlobResourceContents value.") : new ToolExecutionCompleteContentResourceDetails(embeddedBlobResourceContents); + } + + throw new JsonException("JSON value did not match any ToolExecutionCompleteContentResourceDetails variant."); + } + + /// + public override void Write(Utf8JsonWriter writer, ToolExecutionCompleteContentResourceDetails value, JsonSerializerOptions options) + { + if (value.EmbeddedTextResourceContents is { } embeddedTextResourceContents) + { + JsonSerializer.Serialize(writer, embeddedTextResourceContents, SessionEventsJsonContext.Default.EmbeddedTextResourceContents); + return; + } + if (value.EmbeddedBlobResourceContents is { } embeddedBlobResourceContents) + { + JsonSerializer.Serialize(writer, embeddedBlobResourceContents, SessionEventsJsonContext.Default.EmbeddedBlobResourceContents); + return; + } + + throw new JsonException("No ToolExecutionCompleteContentResourceDetails variant value is set."); + } + } +} + /// Embedded resource content block with inline text or binary data. /// The resource variant of . public partial class ToolExecutionCompleteContentResource : ToolExecutionCompleteContent @@ -3694,7 +3807,7 @@ public partial class ToolExecutionCompleteContentResource : ToolExecutionComplet /// The embedded resource contents, either text or base64-encoded binary. [JsonPropertyName("resource")] - public required object Resource { get; set; } + public required ToolExecutionCompleteContentResourceDetails Resource { get; set; } } /// A content block within a tool result, which may be text, terminal output, image, audio, or a resource. @@ -6677,6 +6790,8 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(ElicitationRequestedData))] [JsonSerializable(typeof(ElicitationRequestedEvent))] [JsonSerializable(typeof(ElicitationRequestedSchema))] +[JsonSerializable(typeof(EmbeddedBlobResourceContents))] +[JsonSerializable(typeof(EmbeddedTextResourceContents))] [JsonSerializable(typeof(ExitPlanModeCompletedData))] [JsonSerializable(typeof(ExitPlanModeCompletedEvent))] [JsonSerializable(typeof(ExitPlanModeRequestedData))] @@ -6842,6 +6957,7 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(ToolExecutionCompleteContentAudio))] [JsonSerializable(typeof(ToolExecutionCompleteContentImage))] [JsonSerializable(typeof(ToolExecutionCompleteContentResource))] +[JsonSerializable(typeof(ToolExecutionCompleteContentResourceDetails))] [JsonSerializable(typeof(ToolExecutionCompleteContentResourceLink))] [JsonSerializable(typeof(ToolExecutionCompleteContentResourceLinkIcon))] [JsonSerializable(typeof(ToolExecutionCompleteContentTerminal))] diff --git a/go/zsession_encoding.go b/go/rpc/zsession_encoding.go similarity index 97% rename from go/zsession_encoding.go rename to go/rpc/zsession_encoding.go index fc603c5ca..15ca55139 100644 --- a/go/zsession_encoding.go +++ b/go/rpc/zsession_encoding.go @@ -1,7 +1,7 @@ // Code generated by scripts/codegen/go.ts; DO NOT EDIT. // Source: session-events.schema.json -package copilot +package rpc import ( "encoding/json" @@ -790,7 +790,7 @@ func (r ToolExecutionCompleteContentImage) MarshalJSON() ([]byte, error) { }) } -func matchesEmbeddedBlobResourceContents(data []byte) bool { +func matchesToolExecutionCompleteContentResourceDetailsEmbeddedBlobResourceContents(data []byte) bool { var rawGroup0 struct { Blob json.RawMessage `json:"blob"` Text json.RawMessage `json:"text"` @@ -804,7 +804,7 @@ func matchesEmbeddedBlobResourceContents(data []byte) bool { return rawGroup0.Text == nil } -func matchesEmbeddedTextResourceContents(data []byte) bool { +func matchesToolExecutionCompleteContentResourceDetailsEmbeddedTextResourceContents(data []byte) bool { var rawGroup0 struct { Blob json.RawMessage `json:"blob"` Text json.RawMessage `json:"text"` @@ -818,50 +818,38 @@ func matchesEmbeddedTextResourceContents(data []byte) bool { return rawGroup0.Blob == nil } -func unmarshalToolExecutionCompleteContentResourceDetails(data []byte) (ToolExecutionCompleteContentResourceDetails, error) { - if string(data) == "null" { - return nil, nil - } - if matchesEmbeddedBlobResourceContents(data) { - var d EmbeddedBlobResourceContents - if err := json.Unmarshal(data, &d); err != nil { - return nil, err - } - return &d, nil +func (r ToolExecutionCompleteContentResourceDetails) MarshalJSON() ([]byte, error) { + if r.EmbeddedBlobResourceContents != nil { + return json.Marshal(r.EmbeddedBlobResourceContents) } - if matchesEmbeddedTextResourceContents(data) { - var d EmbeddedTextResourceContents - if err := json.Unmarshal(data, &d); err != nil { - return nil, err - } - return &d, nil - } - return &RawToolExecutionCompleteContentResourceDetails{Raw: data}, nil -} - -func (r RawToolExecutionCompleteContentResourceDetails) MarshalJSON() ([]byte, error) { - if r.Raw != nil { - return r.Raw, nil + if r.EmbeddedTextResourceContents != nil { + return json.Marshal(r.EmbeddedTextResourceContents) } return []byte("null"), nil } -func (r *ToolExecutionCompleteContentResource) UnmarshalJSON(data []byte) error { - type rawToolExecutionCompleteContentResource struct { - Resource json.RawMessage `json:"resource"` +func (r *ToolExecutionCompleteContentResourceDetails) UnmarshalJSON(data []byte) error { + if string(data) == "null" { + *r = ToolExecutionCompleteContentResourceDetails{} + return nil } - var raw rawToolExecutionCompleteContentResource - if err := json.Unmarshal(data, &raw); err != nil { - return err + if matchesToolExecutionCompleteContentResourceDetailsEmbeddedBlobResourceContents(data) { + var value EmbeddedBlobResourceContents + if err := json.Unmarshal(data, &value); err != nil { + return err + } + *r = ToolExecutionCompleteContentResourceDetails{EmbeddedBlobResourceContents: &value} + return nil } - if raw.Resource != nil { - value, err := unmarshalToolExecutionCompleteContentResourceDetails(raw.Resource) - if err != nil { + if matchesToolExecutionCompleteContentResourceDetailsEmbeddedTextResourceContents(data) { + var value EmbeddedTextResourceContents + if err := json.Unmarshal(data, &value); err != nil { return err } - r.Resource = value + *r = ToolExecutionCompleteContentResourceDetails{EmbeddedTextResourceContents: &value} + return nil } - return nil + return errors.New("data did not match any union variant for ToolExecutionCompleteContentResourceDetails") } func (r ToolExecutionCompleteContentResource) MarshalJSON() ([]byte, error) { diff --git a/go/rpc/zsession_events.go b/go/rpc/zsession_events.go new file mode 100644 index 000000000..9d2589d95 --- /dev/null +++ b/go/rpc/zsession_events.go @@ -0,0 +1,3064 @@ +// Code generated by scripts/codegen/go.ts; DO NOT EDIT. +// Source: session-events.schema.json + +package rpc + +import ( + "encoding/json" + "time" +) + +// SessionEventData is the interface implemented by all per-event data types. +type SessionEventData interface { + sessionEventData() + Type() SessionEventType +} + +// SessionEvent represents a single session event with a typed data payload. +type SessionEvent struct { + // Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + AgentID *string `json:"agentId,omitempty"` + // Typed event payload. Use a type switch to access per-event fields. + Data SessionEventData `json:"-"` + // When true, the event is transient and not persisted to the session event log on disk + Ephemeral *bool `json:"ephemeral,omitempty"` + // Unique event identifier (UUID v4), generated when the event is emitted + ID string `json:"id"` + // ID of the chronologically preceding event in the session, forming a linked chain. Null for the first event. + ParentID *string `json:"parentId"` + // ISO 8601 timestamp when the event was created + Timestamp time.Time `json:"timestamp"` +} + +// Type returns the event type discriminator derived from Data. +func (e SessionEvent) Type() SessionEventType { + if e.Data == nil { + return "" + } + return e.Data.Type() +} + +// RawSessionEventData holds unparsed JSON data for unrecognized event types. +type RawSessionEventData struct { + EventType SessionEventType + Raw json.RawMessage +} + +func (RawSessionEventData) sessionEventData() {} +func (r RawSessionEventData) Type() SessionEventType { + return r.EventType +} + +// SessionEventType identifies the kind of session event. +type SessionEventType string + +const ( + SessionEventTypeAbort SessionEventType = "abort" + SessionEventTypeAssistantIntent SessionEventType = "assistant.intent" + SessionEventTypeAssistantMessage SessionEventType = "assistant.message" + SessionEventTypeAssistantMessageDelta SessionEventType = "assistant.message_delta" + SessionEventTypeAssistantMessageStart SessionEventType = "assistant.message_start" + SessionEventTypeAssistantReasoning SessionEventType = "assistant.reasoning" + SessionEventTypeAssistantReasoningDelta SessionEventType = "assistant.reasoning_delta" + SessionEventTypeAssistantStreamingDelta SessionEventType = "assistant.streaming_delta" + SessionEventTypeAssistantTurnEnd SessionEventType = "assistant.turn_end" + SessionEventTypeAssistantTurnStart SessionEventType = "assistant.turn_start" + SessionEventTypeAssistantUsage SessionEventType = "assistant.usage" + SessionEventTypeAutoModeSwitchCompleted SessionEventType = "auto_mode_switch.completed" + SessionEventTypeAutoModeSwitchRequested SessionEventType = "auto_mode_switch.requested" + SessionEventTypeCapabilitiesChanged SessionEventType = "capabilities.changed" + SessionEventTypeCommandCompleted SessionEventType = "command.completed" + SessionEventTypeCommandExecute SessionEventType = "command.execute" + SessionEventTypeCommandQueued SessionEventType = "command.queued" + SessionEventTypeCommandsChanged SessionEventType = "commands.changed" + SessionEventTypeElicitationCompleted SessionEventType = "elicitation.completed" + SessionEventTypeElicitationRequested SessionEventType = "elicitation.requested" + SessionEventTypeExitPlanModeCompleted SessionEventType = "exit_plan_mode.completed" + SessionEventTypeExitPlanModeRequested SessionEventType = "exit_plan_mode.requested" + SessionEventTypeExternalToolCompleted SessionEventType = "external_tool.completed" + SessionEventTypeExternalToolRequested SessionEventType = "external_tool.requested" + SessionEventTypeHookEnd SessionEventType = "hook.end" + SessionEventTypeHookStart SessionEventType = "hook.start" + SessionEventTypeMcpOauthCompleted SessionEventType = "mcp.oauth_completed" + SessionEventTypeMcpOauthRequired SessionEventType = "mcp.oauth_required" + SessionEventTypeModelCallFailure SessionEventType = "model.call_failure" + SessionEventTypePendingMessagesModified SessionEventType = "pending_messages.modified" + SessionEventTypePermissionCompleted SessionEventType = "permission.completed" + SessionEventTypePermissionRequested SessionEventType = "permission.requested" + SessionEventTypeSamplingCompleted SessionEventType = "sampling.completed" + SessionEventTypeSamplingRequested SessionEventType = "sampling.requested" + SessionEventTypeSessionBackgroundTasksChanged SessionEventType = "session.background_tasks_changed" + SessionEventTypeSessionCompactionComplete SessionEventType = "session.compaction_complete" + SessionEventTypeSessionCompactionStart SessionEventType = "session.compaction_start" + SessionEventTypeSessionContextChanged SessionEventType = "session.context_changed" + SessionEventTypeSessionCustomAgentsUpdated SessionEventType = "session.custom_agents_updated" + SessionEventTypeSessionError SessionEventType = "session.error" + SessionEventTypeSessionExtensionsLoaded SessionEventType = "session.extensions_loaded" + SessionEventTypeSessionHandoff SessionEventType = "session.handoff" + SessionEventTypeSessionIdle SessionEventType = "session.idle" + SessionEventTypeSessionInfo SessionEventType = "session.info" + SessionEventTypeSessionMcpServersLoaded SessionEventType = "session.mcp_servers_loaded" + SessionEventTypeSessionMcpServerStatusChanged SessionEventType = "session.mcp_server_status_changed" + SessionEventTypeSessionModeChanged SessionEventType = "session.mode_changed" + SessionEventTypeSessionModelChange SessionEventType = "session.model_change" + SessionEventTypeSessionPlanChanged SessionEventType = "session.plan_changed" + SessionEventTypeSessionRemoteSteerableChanged SessionEventType = "session.remote_steerable_changed" + SessionEventTypeSessionResume SessionEventType = "session.resume" + SessionEventTypeSessionScheduleCancelled SessionEventType = "session.schedule_cancelled" + SessionEventTypeSessionScheduleCreated SessionEventType = "session.schedule_created" + SessionEventTypeSessionShutdown SessionEventType = "session.shutdown" + SessionEventTypeSessionSkillsLoaded SessionEventType = "session.skills_loaded" + SessionEventTypeSessionSnapshotRewind SessionEventType = "session.snapshot_rewind" + SessionEventTypeSessionStart SessionEventType = "session.start" + SessionEventTypeSessionTaskComplete SessionEventType = "session.task_complete" + SessionEventTypeSessionTitleChanged SessionEventType = "session.title_changed" + SessionEventTypeSessionToolsUpdated SessionEventType = "session.tools_updated" + SessionEventTypeSessionTruncation SessionEventType = "session.truncation" + SessionEventTypeSessionUsageInfo SessionEventType = "session.usage_info" + SessionEventTypeSessionWarning SessionEventType = "session.warning" + SessionEventTypeSessionWorkspaceFileChanged SessionEventType = "session.workspace_file_changed" + SessionEventTypeSkillInvoked SessionEventType = "skill.invoked" + SessionEventTypeSubagentCompleted SessionEventType = "subagent.completed" + SessionEventTypeSubagentDeselected SessionEventType = "subagent.deselected" + SessionEventTypeSubagentFailed SessionEventType = "subagent.failed" + SessionEventTypeSubagentSelected SessionEventType = "subagent.selected" + SessionEventTypeSubagentStarted SessionEventType = "subagent.started" + SessionEventTypeSystemMessage SessionEventType = "system.message" + SessionEventTypeSystemNotification SessionEventType = "system.notification" + SessionEventTypeToolExecutionComplete SessionEventType = "tool.execution_complete" + SessionEventTypeToolExecutionPartialResult SessionEventType = "tool.execution_partial_result" + SessionEventTypeToolExecutionProgress SessionEventType = "tool.execution_progress" + SessionEventTypeToolExecutionStart SessionEventType = "tool.execution_start" + SessionEventTypeToolUserRequested SessionEventType = "tool.user_requested" + SessionEventTypeUserInputCompleted SessionEventType = "user_input.completed" + SessionEventTypeUserInputRequested SessionEventType = "user_input.requested" + SessionEventTypeUserMessage SessionEventType = "user.message" +) + +// Agent intent description for current activity or plan +type AssistantIntentData struct { + // Short description of what the agent is currently doing or planning to do + Intent string `json:"intent"` +} + +func (*AssistantIntentData) sessionEventData() {} +func (*AssistantIntentData) Type() SessionEventType { return SessionEventTypeAssistantIntent } + +// Agent mode change details including previous and new modes +type SessionModeChangedData struct { + // Agent mode after the change (e.g., "interactive", "plan", "autopilot") + NewMode string `json:"newMode"` + // Agent mode before the change (e.g., "interactive", "plan", "autopilot") + PreviousMode string `json:"previousMode"` +} + +func (*SessionModeChangedData) sessionEventData() {} +func (*SessionModeChangedData) Type() SessionEventType { return SessionEventTypeSessionModeChanged } + +// Assistant reasoning content for timeline display with complete thinking text +type AssistantReasoningData struct { + // The complete extended thinking text from the model + Content string `json:"content"` + // Unique identifier for this reasoning block + ReasoningID string `json:"reasoningId"` +} + +func (*AssistantReasoningData) sessionEventData() {} +func (*AssistantReasoningData) Type() SessionEventType { return SessionEventTypeAssistantReasoning } + +// Assistant response containing text content, optional tool requests, and interaction metadata +type AssistantMessageData struct { + // Raw Anthropic content array with advisor blocks (server_tool_use, advisor_tool_result) for verbatim round-tripping + AnthropicAdvisorBlocks []any `json:"anthropicAdvisorBlocks,omitempty"` + // Anthropic advisor model ID used for this response, for timeline display on replay + AnthropicAdvisorModel *string `json:"anthropicAdvisorModel,omitempty"` + // The assistant's text response content + Content string `json:"content"` + // Encrypted reasoning content from OpenAI models. Session-bound and stripped on resume. + EncryptedContent *string `json:"encryptedContent,omitempty"` + // CAPI interaction ID for correlating this message with upstream telemetry + InteractionID *string `json:"interactionId,omitempty"` + // Unique identifier for this assistant message + MessageID string `json:"messageId"` + // Model that produced this assistant message, if known + Model *string `json:"model,omitempty"` + // Actual output token count from the API response (completion_tokens), used for accurate token accounting + OutputTokens *float64 `json:"outputTokens,omitempty"` + // Tool call ID of the parent tool invocation when this event originates from a sub-agent + // Deprecated: ParentToolCallID is deprecated. + ParentToolCallID *string `json:"parentToolCallId,omitempty"` + // Generation phase for phased-output models (e.g., thinking vs. response phases) + Phase *string `json:"phase,omitempty"` + // Opaque/encrypted extended thinking data from Anthropic models. Session-bound and stripped on resume. + ReasoningOpaque *string `json:"reasoningOpaque,omitempty"` + // Readable reasoning text from the model's extended thinking + ReasoningText *string `json:"reasoningText,omitempty"` + // GitHub request tracing ID (x-github-request-id header) for correlating with server-side logs + RequestID *string `json:"requestId,omitempty"` + // Tool invocations requested by the assistant in this message + ToolRequests []AssistantMessageToolRequest `json:"toolRequests,omitempty"` + // Identifier for the agent loop turn that produced this message, matching the corresponding assistant.turn_start event + TurnID *string `json:"turnId,omitempty"` +} + +func (*AssistantMessageData) sessionEventData() {} +func (*AssistantMessageData) Type() SessionEventType { return SessionEventTypeAssistantMessage } + +// Auto mode switch completion notification +type AutoModeSwitchCompletedData struct { + // Request ID of the resolved request; clients should dismiss any UI for this request + RequestID string `json:"requestId"` + // The user's choice: 'yes', 'yes_always', or 'no' + Response string `json:"response"` +} + +func (*AutoModeSwitchCompletedData) sessionEventData() {} +func (*AutoModeSwitchCompletedData) Type() SessionEventType { + return SessionEventTypeAutoModeSwitchCompleted +} + +// Auto mode switch request notification requiring user approval +type AutoModeSwitchRequestedData struct { + // The rate limit error code that triggered this request + ErrorCode *string `json:"errorCode,omitempty"` + // Unique identifier for this request; used to respond via session.respondToAutoModeSwitch() + RequestID string `json:"requestId"` + // Seconds until the rate limit resets, when known. Lets clients render a humanized reset time alongside the prompt. + RetryAfterSeconds *float64 `json:"retryAfterSeconds,omitempty"` +} + +func (*AutoModeSwitchRequestedData) sessionEventData() {} +func (*AutoModeSwitchRequestedData) Type() SessionEventType { + return SessionEventTypeAutoModeSwitchRequested +} + +// Context window breakdown at the start of LLM-powered conversation compaction +type SessionCompactionStartData struct { + // Token count from non-system messages (user, assistant, tool) at compaction start + ConversationTokens *float64 `json:"conversationTokens,omitempty"` + // Token count from system message(s) at compaction start + SystemTokens *float64 `json:"systemTokens,omitempty"` + // Token count from tool definitions at compaction start + ToolDefinitionsTokens *float64 `json:"toolDefinitionsTokens,omitempty"` +} + +func (*SessionCompactionStartData) sessionEventData() {} +func (*SessionCompactionStartData) Type() SessionEventType { + return SessionEventTypeSessionCompactionStart +} + +// Conversation compaction results including success status, metrics, and optional error details +type SessionCompactionCompleteData struct { + // Checkpoint snapshot number created for recovery + CheckpointNumber *float64 `json:"checkpointNumber,omitempty"` + // File path where the checkpoint was stored + CheckpointPath *string `json:"checkpointPath,omitempty"` + // Token usage breakdown for the compaction LLM call (aligned with assistant.usage format) + CompactionTokensUsed *CompactionCompleteCompactionTokensUsed `json:"compactionTokensUsed,omitempty"` + // Token count from non-system messages (user, assistant, tool) after compaction + ConversationTokens *float64 `json:"conversationTokens,omitempty"` + // Error message if compaction failed + Error *string `json:"error,omitempty"` + // Number of messages removed during compaction + MessagesRemoved *float64 `json:"messagesRemoved,omitempty"` + // Total tokens in conversation after compaction + PostCompactionTokens *float64 `json:"postCompactionTokens,omitempty"` + // Number of messages before compaction + PreCompactionMessagesLength *float64 `json:"preCompactionMessagesLength,omitempty"` + // Total tokens in conversation before compaction + PreCompactionTokens *float64 `json:"preCompactionTokens,omitempty"` + // GitHub request tracing ID (x-github-request-id header) for the compaction LLM call + RequestID *string `json:"requestId,omitempty"` + // Whether compaction completed successfully + Success bool `json:"success"` + // LLM-generated summary of the compacted conversation history + SummaryContent *string `json:"summaryContent,omitempty"` + // Token count from system message(s) after compaction + SystemTokens *float64 `json:"systemTokens,omitempty"` + // Number of tokens removed during compaction + TokensRemoved *float64 `json:"tokensRemoved,omitempty"` + // Token count from tool definitions after compaction + ToolDefinitionsTokens *float64 `json:"toolDefinitionsTokens,omitempty"` +} + +func (*SessionCompactionCompleteData) sessionEventData() {} +func (*SessionCompactionCompleteData) Type() SessionEventType { + return SessionEventTypeSessionCompactionComplete +} + +// Conversation truncation statistics including token counts and removed content metrics +type SessionTruncationData struct { + // Number of messages removed by truncation + MessagesRemovedDuringTruncation float64 `json:"messagesRemovedDuringTruncation"` + // Identifier of the component that performed truncation (e.g., "BasicTruncator") + PerformedBy string `json:"performedBy"` + // Number of conversation messages after truncation + PostTruncationMessagesLength float64 `json:"postTruncationMessagesLength"` + // Total tokens in conversation messages after truncation + PostTruncationTokensInMessages float64 `json:"postTruncationTokensInMessages"` + // Number of conversation messages before truncation + PreTruncationMessagesLength float64 `json:"preTruncationMessagesLength"` + // Total tokens in conversation messages before truncation + PreTruncationTokensInMessages float64 `json:"preTruncationTokensInMessages"` + // Maximum token count for the model's context window + TokenLimit float64 `json:"tokenLimit"` + // Number of tokens removed by truncation + TokensRemovedDuringTruncation float64 `json:"tokensRemovedDuringTruncation"` +} + +func (*SessionTruncationData) sessionEventData() {} +func (*SessionTruncationData) Type() SessionEventType { return SessionEventTypeSessionTruncation } + +// Current context window usage statistics including token and message counts +type SessionUsageInfoData struct { + // Token count from non-system messages (user, assistant, tool) + ConversationTokens *float64 `json:"conversationTokens,omitempty"` + // Current number of tokens in the context window + CurrentTokens float64 `json:"currentTokens"` + // Whether this is the first usage_info event emitted in this session + IsInitial *bool `json:"isInitial,omitempty"` + // Current number of messages in the conversation + MessagesLength float64 `json:"messagesLength"` + // Token count from system message(s) + SystemTokens *float64 `json:"systemTokens,omitempty"` + // Maximum token count for the model's context window + TokenLimit float64 `json:"tokenLimit"` + // Token count from tool definitions + ToolDefinitionsTokens *float64 `json:"toolDefinitionsTokens,omitempty"` +} + +func (*SessionUsageInfoData) sessionEventData() {} +func (*SessionUsageInfoData) Type() SessionEventType { return SessionEventTypeSessionUsageInfo } + +// Custom agent selection details including name and available tools +type SubagentSelectedData struct { + // Human-readable display name of the selected custom agent + AgentDisplayName string `json:"agentDisplayName"` + // Internal name of the selected custom agent + AgentName string `json:"agentName"` + // List of tool names available to this agent, or null for all tools + Tools []string `json:"tools"` +} + +func (*SubagentSelectedData) sessionEventData() {} +func (*SubagentSelectedData) Type() SessionEventType { return SessionEventTypeSubagentSelected } + +// Elicitation request completion with the user's response +type ElicitationCompletedData struct { + // The user action: "accept" (submitted form), "decline" (explicitly refused), or "cancel" (dismissed) + Action *ElicitationCompletedAction `json:"action,omitempty"` + // The submitted form data when action is 'accept'; keys match the requested schema fields + Content map[string]ElicitationCompletedContent `json:"content,omitempty"` + // Request ID of the resolved elicitation request; clients should dismiss any UI for this request + RequestID string `json:"requestId"` +} + +func (*ElicitationCompletedData) sessionEventData() {} +func (*ElicitationCompletedData) Type() SessionEventType { return SessionEventTypeElicitationCompleted } + +// Elicitation request; may be form-based (structured input) or URL-based (browser redirect) +type ElicitationRequestedData struct { + // The source that initiated the request (MCP server name, or absent for agent-initiated) + ElicitationSource *string `json:"elicitationSource,omitempty"` + // Message describing what information is needed from the user + Message string `json:"message"` + // Elicitation mode; "form" for structured input, "url" for browser-based. Defaults to "form" when absent. + Mode *ElicitationRequestedMode `json:"mode,omitempty"` + // JSON Schema describing the form fields to present to the user (form mode only) + RequestedSchema *ElicitationRequestedSchema `json:"requestedSchema,omitempty"` + // Unique identifier for this elicitation request; used to respond via session.respondToElicitation() + RequestID string `json:"requestId"` + // Tool call ID from the LLM completion; used to correlate with CompletionChunk.toolCall.id for remote UIs + ToolCallID *string `json:"toolCallId,omitempty"` + // URL to open in the user's browser (url mode only) + URL *string `json:"url,omitempty"` +} + +func (*ElicitationRequestedData) sessionEventData() {} +func (*ElicitationRequestedData) Type() SessionEventType { return SessionEventTypeElicitationRequested } + +// Empty payload; the event signals that the custom agent was deselected, returning to the default agent +type SubagentDeselectedData struct { +} + +func (*SubagentDeselectedData) sessionEventData() {} +func (*SubagentDeselectedData) Type() SessionEventType { return SessionEventTypeSubagentDeselected } + +// Empty payload; the event signals that the pending message queue has changed +type PendingMessagesModifiedData struct { +} + +func (*PendingMessagesModifiedData) sessionEventData() {} +func (*PendingMessagesModifiedData) Type() SessionEventType { + return SessionEventTypePendingMessagesModified +} + +// Error details for timeline display including message and optional diagnostic information +type SessionErrorData struct { + // Only set on `errorType: "rate_limit"`. When `true`, the runtime will follow this error with an `auto_mode_switch.requested` event (or silently switch if `continueOnAutoMode` is enabled). UI clients can use this flag to suppress duplicate rendering of the rate-limit error when they show their own auto-mode-switch prompt. + EligibleForAutoSwitch *bool `json:"eligibleForAutoSwitch,omitempty"` + // Fine-grained error code from the upstream provider, when available. For `errorType: "rate_limit"`, this is one of the `RateLimitErrorCode` values (e.g., `"user_weekly_rate_limited"`, `"user_global_rate_limited"`, `"rate_limited"`, `"user_model_rate_limited"`, `"integration_rate_limited"`). + ErrorCode *string `json:"errorCode,omitempty"` + // Category of error (e.g., "authentication", "authorization", "quota", "rate_limit", "context_limit", "query") + ErrorType string `json:"errorType"` + // Human-readable error message + Message string `json:"message"` + // GitHub request tracing ID (x-github-request-id header) for correlating with server-side logs + ProviderCallID *string `json:"providerCallId,omitempty"` + // Error stack trace, when available + Stack *string `json:"stack,omitempty"` + // HTTP status code from the upstream request, if applicable + StatusCode *int64 `json:"statusCode,omitempty"` + // Optional URL associated with this error that the user can open in a browser + URL *string `json:"url,omitempty"` +} + +func (*SessionErrorData) sessionEventData() {} +func (*SessionErrorData) Type() SessionEventType { return SessionEventTypeSessionError } + +// External tool completion notification signaling UI dismissal +type ExternalToolCompletedData struct { + // Request ID of the resolved external tool request; clients should dismiss any UI for this request + RequestID string `json:"requestId"` +} + +func (*ExternalToolCompletedData) sessionEventData() {} +func (*ExternalToolCompletedData) Type() SessionEventType { + return SessionEventTypeExternalToolCompleted +} + +// External tool invocation request for client-side tool execution +type ExternalToolRequestedData struct { + // Arguments to pass to the external tool + Arguments any `json:"arguments,omitempty"` + // Unique identifier for this request; used to respond via session.respondToExternalTool() + RequestID string `json:"requestId"` + // Session ID that this external tool request belongs to + SessionID string `json:"sessionId"` + // Tool call ID assigned to this external tool invocation + ToolCallID string `json:"toolCallId"` + // Name of the external tool to invoke + ToolName string `json:"toolName"` + // W3C Trace Context traceparent header for the execute_tool span + Traceparent *string `json:"traceparent,omitempty"` + // W3C Trace Context tracestate header for the execute_tool span + Tracestate *string `json:"tracestate,omitempty"` +} + +func (*ExternalToolRequestedData) sessionEventData() {} +func (*ExternalToolRequestedData) Type() SessionEventType { + return SessionEventTypeExternalToolRequested +} + +// Failed LLM API call metadata for telemetry +type ModelCallFailureData struct { + // Completion ID from the model provider (e.g., chatcmpl-abc123) + APICallID *string `json:"apiCallId,omitempty"` + // Duration of the failed API call in milliseconds + DurationMs *float64 `json:"durationMs,omitempty"` + // Raw provider/runtime error message for restricted telemetry + ErrorMessage *string `json:"errorMessage,omitempty"` + // What initiated this API call (e.g., "sub-agent", "mcp-sampling"); absent for user-initiated calls + Initiator *string `json:"initiator,omitempty"` + // Model identifier used for the failed API call + Model *string `json:"model,omitempty"` + // GitHub request tracing ID (x-github-request-id header) for server-side log correlation + ProviderCallID *string `json:"providerCallId,omitempty"` + // Where the failed model call originated + Source ModelCallFailureSource `json:"source"` + // HTTP status code from the failed request + StatusCode *int64 `json:"statusCode,omitempty"` +} + +func (*ModelCallFailureData) sessionEventData() {} +func (*ModelCallFailureData) Type() SessionEventType { return SessionEventTypeModelCallFailure } + +// Hook invocation completion details including output, success status, and error information +type HookEndData struct { + // Error details when the hook failed + Error *HookEndError `json:"error,omitempty"` + // Identifier matching the corresponding hook.start event + HookInvocationID string `json:"hookInvocationId"` + // Type of hook that was invoked (e.g., "preToolUse", "postToolUse", "sessionStart") + HookType string `json:"hookType"` + // Output data produced by the hook + Output any `json:"output,omitempty"` + // Whether the hook completed successfully + Success bool `json:"success"` +} + +func (*HookEndData) sessionEventData() {} +func (*HookEndData) Type() SessionEventType { return SessionEventTypeHookEnd } + +// Hook invocation start details including type and input data +type HookStartData struct { + // Unique identifier for this hook invocation + HookInvocationID string `json:"hookInvocationId"` + // Type of hook being invoked (e.g., "preToolUse", "postToolUse", "sessionStart") + HookType string `json:"hookType"` + // Input data passed to the hook + Input any `json:"input,omitempty"` +} + +func (*HookStartData) sessionEventData() {} +func (*HookStartData) Type() SessionEventType { return SessionEventTypeHookStart } + +// Informational message for timeline display with categorization +type SessionInfoData struct { + // Category of informational message (e.g., "notification", "timing", "context_window", "mcp", "snapshot", "configuration", "authentication", "model") + InfoType string `json:"infoType"` + // Human-readable informational message for display in the timeline + Message string `json:"message"` + // Optional actionable tip displayed with this message + Tip *string `json:"tip,omitempty"` + // Optional URL associated with this message that the user can open in a browser + URL *string `json:"url,omitempty"` +} + +func (*SessionInfoData) sessionEventData() {} +func (*SessionInfoData) Type() SessionEventType { return SessionEventTypeSessionInfo } + +// LLM API call usage metrics including tokens, costs, quotas, and billing information +type AssistantUsageData struct { + // Completion ID from the model provider (e.g., chatcmpl-abc123) + APICallID *string `json:"apiCallId,omitempty"` + // API endpoint used for this model call, matching CAPI supported_endpoints vocabulary + APIEndpoint *AssistantUsageAPIEndpoint `json:"apiEndpoint,omitempty"` + // Number of tokens read from prompt cache + CacheReadTokens *float64 `json:"cacheReadTokens,omitempty"` + // Number of tokens written to prompt cache + CacheWriteTokens *float64 `json:"cacheWriteTokens,omitempty"` + // Per-request cost and usage data from the CAPI copilot_usage response field + CopilotUsage *AssistantUsageCopilotUsage `json:"copilotUsage,omitempty"` + // Model multiplier cost for billing purposes + Cost *float64 `json:"cost,omitempty"` + // Duration of the API call in milliseconds + Duration *float64 `json:"duration,omitempty"` + // What initiated this API call (e.g., "sub-agent", "mcp-sampling"); absent for user-initiated calls + Initiator *string `json:"initiator,omitempty"` + // Number of input tokens consumed + InputTokens *float64 `json:"inputTokens,omitempty"` + // Average inter-token latency in milliseconds. Only available for streaming requests + InterTokenLatencyMs *float64 `json:"interTokenLatencyMs,omitempty"` + // Model identifier used for this API call + Model string `json:"model"` + // Number of output tokens produced + OutputTokens *float64 `json:"outputTokens,omitempty"` + // Parent tool call ID when this usage originates from a sub-agent + // Deprecated: ParentToolCallID is deprecated. + ParentToolCallID *string `json:"parentToolCallId,omitempty"` + // GitHub request tracing ID (x-github-request-id header) for server-side log correlation + ProviderCallID *string `json:"providerCallId,omitempty"` + // Per-quota resource usage snapshots, keyed by quota identifier + QuotaSnapshots map[string]AssistantUsageQuotaSnapshot `json:"quotaSnapshots,omitempty"` + // Reasoning effort level used for model calls, if applicable (e.g. "low", "medium", "high", "xhigh") + ReasoningEffort *string `json:"reasoningEffort,omitempty"` + // Number of output tokens used for reasoning (e.g., chain-of-thought) + ReasoningTokens *float64 `json:"reasoningTokens,omitempty"` + // Time to first token in milliseconds. Only available for streaming requests + TtftMs *float64 `json:"ttftMs,omitempty"` +} + +func (*AssistantUsageData) sessionEventData() {} +func (*AssistantUsageData) Type() SessionEventType { return SessionEventTypeAssistantUsage } + +// MCP OAuth request completion notification +type McpOauthCompletedData struct { + // Request ID of the resolved OAuth request + RequestID string `json:"requestId"` +} + +func (*McpOauthCompletedData) sessionEventData() {} +func (*McpOauthCompletedData) Type() SessionEventType { return SessionEventTypeMcpOauthCompleted } + +// Model change details including previous and new model identifiers +type SessionModelChangeData struct { + // Reason the change happened, when not user-initiated. Currently `"rate_limit_auto_switch"` for changes triggered by the auto-mode-switch rate-limit recovery path. UI clients can use this to render contextual copy. + Cause *string `json:"cause,omitempty"` + // Newly selected model identifier + NewModel string `json:"newModel"` + // Model that was previously selected, if any + PreviousModel *string `json:"previousModel,omitempty"` + // Reasoning effort level before the model change, if applicable + PreviousReasoningEffort *string `json:"previousReasoningEffort,omitempty"` + // Reasoning effort level after the model change, if applicable + ReasoningEffort *string `json:"reasoningEffort,omitempty"` +} + +func (*SessionModelChangeData) sessionEventData() {} +func (*SessionModelChangeData) Type() SessionEventType { return SessionEventTypeSessionModelChange } + +// Notifies Mission Control that the session's remote steering capability has changed +type SessionRemoteSteerableChangedData struct { + // Whether this session now supports remote steering via Mission Control + RemoteSteerable bool `json:"remoteSteerable"` +} + +func (*SessionRemoteSteerableChangedData) sessionEventData() {} +func (*SessionRemoteSteerableChangedData) Type() SessionEventType { + return SessionEventTypeSessionRemoteSteerableChanged +} + +// OAuth authentication request for an MCP server +type McpOauthRequiredData struct { + // Unique identifier for this OAuth request; used to respond via session.respondToMcpOAuth() + RequestID string `json:"requestId"` + // Display name of the MCP server that requires OAuth + ServerName string `json:"serverName"` + // URL of the MCP server that requires OAuth + ServerURL string `json:"serverUrl"` + // Static OAuth client configuration, if the server specifies one + StaticClientConfig *McpOauthRequiredStaticClientConfig `json:"staticClientConfig,omitempty"` +} + +func (*McpOauthRequiredData) sessionEventData() {} +func (*McpOauthRequiredData) Type() SessionEventType { return SessionEventTypeMcpOauthRequired } + +// Payload indicating the session is idle with no background agents in flight +type SessionIdleData struct { + // True when the preceding agentic loop was cancelled via abort signal + Aborted *bool `json:"aborted,omitempty"` +} + +func (*SessionIdleData) sessionEventData() {} +func (*SessionIdleData) Type() SessionEventType { return SessionEventTypeSessionIdle } + +// Permission request completion notification signaling UI dismissal +type PermissionCompletedData struct { + // Request ID of the resolved permission request; clients should dismiss any UI for this request + RequestID string `json:"requestId"` + // The result of the permission request + Result PermissionResult `json:"result"` + // Optional tool call ID associated with this permission prompt; clients may use it to correlate UI created from tool-scoped prompts + ToolCallID *string `json:"toolCallId,omitempty"` +} + +func (*PermissionCompletedData) sessionEventData() {} +func (*PermissionCompletedData) Type() SessionEventType { return SessionEventTypePermissionCompleted } + +// Permission request notification requiring client approval with request details +type PermissionRequestedData struct { + // Details of the permission being requested + PermissionRequest PermissionRequest `json:"permissionRequest"` + // Derived user-facing permission prompt details for UI consumers + PromptRequest PermissionPromptRequest `json:"promptRequest,omitempty"` + // Unique identifier for this permission request; used to respond via session.respondToPermission() + 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"` +} + +func (*PermissionRequestedData) sessionEventData() {} +func (*PermissionRequestedData) Type() SessionEventType { return SessionEventTypePermissionRequested } + +// Plan approval request with plan content and available user actions +type ExitPlanModeRequestedData struct { + // Available actions the user can take (e.g., approve, edit, reject) + Actions []string `json:"actions"` + // Full content of the plan file + PlanContent string `json:"planContent"` + // The recommended action for the user to take + RecommendedAction string `json:"recommendedAction"` + // Unique identifier for this request; used to respond via session.respondToExitPlanMode() + RequestID string `json:"requestId"` + // Summary of the plan that was created + Summary string `json:"summary"` +} + +func (*ExitPlanModeRequestedData) sessionEventData() {} +func (*ExitPlanModeRequestedData) Type() SessionEventType { + return SessionEventTypeExitPlanModeRequested +} + +// Plan file operation details indicating what changed +type SessionPlanChangedData struct { + // The type of operation performed on the plan file + Operation PlanChangedOperation `json:"operation"` +} + +func (*SessionPlanChangedData) sessionEventData() {} +func (*SessionPlanChangedData) Type() SessionEventType { return SessionEventTypeSessionPlanChanged } + +// Plan mode exit completion with the user's approval decision and optional feedback +type ExitPlanModeCompletedData struct { + // Whether the plan was approved by the user + Approved *bool `json:"approved,omitempty"` + // Whether edits should be auto-approved without confirmation + AutoApproveEdits *bool `json:"autoApproveEdits,omitempty"` + // Free-form feedback from the user if they requested changes to the plan + Feedback *string `json:"feedback,omitempty"` + // Request ID of the resolved exit plan mode request; clients should dismiss any UI for this request + RequestID string `json:"requestId"` + // Which action the user selected (e.g. 'autopilot', 'interactive', 'exit_only') + SelectedAction *string `json:"selectedAction,omitempty"` +} + +func (*ExitPlanModeCompletedData) sessionEventData() {} +func (*ExitPlanModeCompletedData) Type() SessionEventType { + return SessionEventTypeExitPlanModeCompleted +} + +// Queued command completion notification signaling UI dismissal +type CommandCompletedData struct { + // Request ID of the resolved command request; clients should dismiss any UI for this request + RequestID string `json:"requestId"` +} + +func (*CommandCompletedData) sessionEventData() {} +func (*CommandCompletedData) Type() SessionEventType { return SessionEventTypeCommandCompleted } + +// Queued slash command dispatch request for client execution +type CommandQueuedData struct { + // The slash command text to be executed (e.g., /help, /clear) + Command string `json:"command"` + // Unique identifier for this request; used to respond via session.respondToQueuedCommand() + RequestID string `json:"requestId"` +} + +func (*CommandQueuedData) sessionEventData() {} +func (*CommandQueuedData) Type() SessionEventType { return SessionEventTypeCommandQueued } + +// Registered command dispatch request routed to the owning client +type CommandExecuteData struct { + // Raw argument string after the command name + Args string `json:"args"` + // The full command text (e.g., /deploy production) + Command string `json:"command"` + // Command name without leading / + CommandName string `json:"commandName"` + // Unique identifier; used to respond via session.commands.handlePendingCommand() + RequestID string `json:"requestId"` +} + +func (*CommandExecuteData) sessionEventData() {} +func (*CommandExecuteData) Type() SessionEventType { return SessionEventTypeCommandExecute } + +// SDK command registration change notification +type CommandsChangedData struct { + // Current list of registered SDK commands + Commands []CommandsChangedCommand `json:"commands"` +} + +func (*CommandsChangedData) sessionEventData() {} +func (*CommandsChangedData) Type() SessionEventType { return SessionEventTypeCommandsChanged } + +// Sampling request completion notification signaling UI dismissal +type SamplingCompletedData struct { + // Request ID of the resolved sampling request; clients should dismiss any UI for this request + RequestID string `json:"requestId"` +} + +func (*SamplingCompletedData) sessionEventData() {} +func (*SamplingCompletedData) Type() SessionEventType { return SessionEventTypeSamplingCompleted } + +// Sampling request from an MCP server; contains the server name and a requestId for correlation +type SamplingRequestedData struct { + // The JSON-RPC request ID from the MCP protocol + McpRequestID any `json:"mcpRequestId"` + // Unique identifier for this sampling request; used to respond via session.respondToSampling() + RequestID string `json:"requestId"` + // Name of the MCP server that initiated the sampling request + ServerName string `json:"serverName"` +} + +func (*SamplingRequestedData) sessionEventData() {} +func (*SamplingRequestedData) Type() SessionEventType { return SessionEventTypeSamplingRequested } + +// Scheduled prompt cancelled from the schedule manager dialog +type SessionScheduleCancelledData struct { + // Id of the scheduled prompt that was cancelled + ID int64 `json:"id"` +} + +func (*SessionScheduleCancelledData) sessionEventData() {} +func (*SessionScheduleCancelledData) Type() SessionEventType { + return SessionEventTypeSessionScheduleCancelled +} + +// Scheduled prompt registered via /every or /after +type SessionScheduleCreatedData struct { + // Sequential id assigned to the scheduled prompt within the session + ID int64 `json:"id"` + // Interval between ticks in milliseconds + IntervalMs int64 `json:"intervalMs"` + // 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`) + Recurring *bool `json:"recurring,omitempty"` +} + +func (*SessionScheduleCreatedData) sessionEventData() {} +func (*SessionScheduleCreatedData) Type() SessionEventType { + return SessionEventTypeSessionScheduleCreated +} + +// Session capability change notification +type CapabilitiesChangedData struct { + // UI capability changes + UI *CapabilitiesChangedUI `json:"ui,omitempty"` +} + +func (*CapabilitiesChangedData) sessionEventData() {} +func (*CapabilitiesChangedData) Type() SessionEventType { return SessionEventTypeCapabilitiesChanged } + +// Session handoff metadata including source, context, and repository information +type SessionHandoffData struct { + // Additional context information for the handoff + Context *string `json:"context,omitempty"` + // ISO 8601 timestamp when the handoff occurred + HandoffTime time.Time `json:"handoffTime"` + // GitHub host URL for the source session (e.g., https://github.com or https://tenant.ghe.com) + Host *string `json:"host,omitempty"` + // Session ID of the remote session being handed off + RemoteSessionID *string `json:"remoteSessionId,omitempty"` + // Repository context for the handed-off session + Repository *HandoffRepository `json:"repository,omitempty"` + // Origin type of the session being handed off + SourceType HandoffSourceType `json:"sourceType"` + // Summary of the work done in the source session + Summary *string `json:"summary,omitempty"` +} + +func (*SessionHandoffData) sessionEventData() {} +func (*SessionHandoffData) Type() SessionEventType { return SessionEventTypeSessionHandoff } + +// Session initialization metadata including context and configuration +type SessionStartData struct { + // Whether the session was already in use by another client at start time + AlreadyInUse *bool `json:"alreadyInUse,omitempty"` + // Working directory and git context at session start + Context *WorkingDirectoryContext `json:"context,omitempty"` + // Version string of the Copilot application + CopilotVersion string `json:"copilotVersion"` + // When set, identifies a parent session whose context this session continues — e.g., a detached headless rem-agent run launched on the parent's interactive shutdown. Telemetry from this session is reported under the parent's session_id. + DetachedFromSpawningParentSessionID *string `json:"detachedFromSpawningParentSessionId,omitempty"` + // Identifier of the software producing the events (e.g., "copilot-agent") + Producer string `json:"producer"` + // Reasoning effort level used for model calls, if applicable (e.g. "low", "medium", "high", "xhigh") + ReasoningEffort *string `json:"reasoningEffort,omitempty"` + // Whether this session supports remote steering via Mission Control + RemoteSteerable *bool `json:"remoteSteerable,omitempty"` + // Model selected at session creation time, if any + SelectedModel *string `json:"selectedModel,omitempty"` + // Unique identifier for the session + SessionID string `json:"sessionId"` + // ISO 8601 timestamp when the session was created + StartTime time.Time `json:"startTime"` + // Schema version number for the session event format + Version float64 `json:"version"` +} + +func (*SessionStartData) sessionEventData() {} +func (*SessionStartData) Type() SessionEventType { return SessionEventTypeSessionStart } + +// Session resume metadata including current context and event count +type SessionResumeData struct { + // Whether the session was already in use by another client at resume time + AlreadyInUse *bool `json:"alreadyInUse,omitempty"` + // Updated working directory and git context at resume time + Context *WorkingDirectoryContext `json:"context,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. + ContinuePendingWork *bool `json:"continuePendingWork,omitempty"` + // Total number of persisted events in the session at the time of resume + EventCount float64 `json:"eventCount"` + // Reasoning effort level used for model calls, if applicable (e.g. "low", "medium", "high", "xhigh") + ReasoningEffort *string `json:"reasoningEffort,omitempty"` + // Whether this session supports remote steering via Mission Control + RemoteSteerable *bool `json:"remoteSteerable,omitempty"` + // ISO 8601 timestamp when the session was resumed + ResumeTime time.Time `json:"resumeTime"` + // Model currently selected at resume time + SelectedModel *string `json:"selectedModel,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. + SessionWasActive *bool `json:"sessionWasActive,omitempty"` +} + +func (*SessionResumeData) sessionEventData() {} +func (*SessionResumeData) Type() SessionEventType { return SessionEventTypeSessionResume } + +// Session rewind details including target event and count of removed events +type SessionSnapshotRewindData struct { + // Number of events that were removed by the rewind + EventsRemoved float64 `json:"eventsRemoved"` + // Event ID that was rewound to; this event and all after it were removed + UpToEventID string `json:"upToEventId"` +} + +func (*SessionSnapshotRewindData) sessionEventData() {} +func (*SessionSnapshotRewindData) Type() SessionEventType { + return SessionEventTypeSessionSnapshotRewind +} + +// Session termination metrics including usage statistics, code changes, and shutdown reason +type SessionShutdownData struct { + // Aggregate code change metrics for the session + CodeChanges ShutdownCodeChanges `json:"codeChanges"` + // Non-system message token count at shutdown + ConversationTokens *float64 `json:"conversationTokens,omitempty"` + // Model that was selected at the time of shutdown + CurrentModel *string `json:"currentModel,omitempty"` + // Total tokens in context window at shutdown + CurrentTokens *float64 `json:"currentTokens,omitempty"` + // Error description when shutdownType is "error" + ErrorReason *string `json:"errorReason,omitempty"` + // Per-model usage breakdown, keyed by model identifier + ModelMetrics map[string]ShutdownModelMetric `json:"modelMetrics"` + // Unix timestamp (milliseconds) when the session started + SessionStartTime float64 `json:"sessionStartTime"` + // Whether the session ended normally ("routine") or due to a crash/fatal error ("error") + ShutdownType ShutdownType `json:"shutdownType"` + // System message token count at shutdown + SystemTokens *float64 `json:"systemTokens,omitempty"` + // Session-wide per-token-type accumulated token counts + TokenDetails map[string]ShutdownTokenDetail `json:"tokenDetails,omitempty"` + // Tool definitions token count at shutdown + ToolDefinitionsTokens *float64 `json:"toolDefinitionsTokens,omitempty"` + // Cumulative time spent in API calls during the session, in milliseconds + TotalAPIDurationMs float64 `json:"totalApiDurationMs"` + // Session-wide accumulated nano-AI units cost + TotalNanoAiu *float64 `json:"totalNanoAiu,omitempty"` + // Total number of premium API requests used during the session + TotalPremiumRequests float64 `json:"totalPremiumRequests"` +} + +func (*SessionShutdownData) sessionEventData() {} +func (*SessionShutdownData) Type() SessionEventType { return SessionEventTypeSessionShutdown } + +// Session title change payload containing the new display title +type SessionTitleChangedData struct { + // The new display title for the session + Title string `json:"title"` +} + +func (*SessionTitleChangedData) sessionEventData() {} +func (*SessionTitleChangedData) Type() SessionEventType { return SessionEventTypeSessionTitleChanged } + +// SessionBackgroundTasksChangedData holds the payload for session.background_tasks_changed events. +type SessionBackgroundTasksChangedData struct { +} + +func (*SessionBackgroundTasksChangedData) sessionEventData() {} +func (*SessionBackgroundTasksChangedData) Type() SessionEventType { + return SessionEventTypeSessionBackgroundTasksChanged +} + +// SessionCustomAgentsUpdatedData holds the payload for session.custom_agents_updated events. +type SessionCustomAgentsUpdatedData struct { + // Array of loaded custom agent metadata + Agents []CustomAgentsUpdatedAgent `json:"agents"` + // Fatal errors from agent loading + Errors []string `json:"errors"` + // Non-fatal warnings from agent loading + Warnings []string `json:"warnings"` +} + +func (*SessionCustomAgentsUpdatedData) sessionEventData() {} +func (*SessionCustomAgentsUpdatedData) Type() SessionEventType { + return SessionEventTypeSessionCustomAgentsUpdated +} + +// SessionExtensionsLoadedData holds the payload for session.extensions_loaded events. +type SessionExtensionsLoadedData struct { + // Array of discovered extensions and their status + Extensions []ExtensionsLoadedExtension `json:"extensions"` +} + +func (*SessionExtensionsLoadedData) sessionEventData() {} +func (*SessionExtensionsLoadedData) Type() SessionEventType { + return SessionEventTypeSessionExtensionsLoaded +} + +// SessionMcpServerStatusChangedData holds the payload for session.mcp_server_status_changed events. +type SessionMcpServerStatusChangedData struct { + // Name of the MCP server whose status changed + ServerName string `json:"serverName"` + // New connection status: connected, failed, needs-auth, pending, disabled, or not_configured + Status McpServerStatusChangedStatus `json:"status"` +} + +func (*SessionMcpServerStatusChangedData) sessionEventData() {} +func (*SessionMcpServerStatusChangedData) Type() SessionEventType { + return SessionEventTypeSessionMcpServerStatusChanged +} + +// SessionMcpServersLoadedData holds the payload for session.mcp_servers_loaded events. +type SessionMcpServersLoadedData struct { + // Array of MCP server status summaries + Servers []McpServersLoadedServer `json:"servers"` +} + +func (*SessionMcpServersLoadedData) sessionEventData() {} +func (*SessionMcpServersLoadedData) Type() SessionEventType { + return SessionEventTypeSessionMcpServersLoaded +} + +// SessionSkillsLoadedData holds the payload for session.skills_loaded events. +type SessionSkillsLoadedData struct { + // Array of resolved skill metadata + Skills []SkillsLoadedSkill `json:"skills"` +} + +func (*SessionSkillsLoadedData) sessionEventData() {} +func (*SessionSkillsLoadedData) Type() SessionEventType { return SessionEventTypeSessionSkillsLoaded } + +// SessionToolsUpdatedData holds the payload for session.tools_updated events. +type SessionToolsUpdatedData struct { + Model string `json:"model"` +} + +func (*SessionToolsUpdatedData) sessionEventData() {} +func (*SessionToolsUpdatedData) Type() SessionEventType { return SessionEventTypeSessionToolsUpdated } + +// Skill invocation details including content, allowed tools, and plugin metadata +type SkillInvokedData struct { + // Tool names that should be auto-approved when this skill is active + AllowedTools []string `json:"allowedTools,omitempty"` + // Full content of the skill file, injected into the conversation for the model + Content string `json:"content"` + // Description of the skill from its SKILL.md frontmatter + Description *string `json:"description,omitempty"` + // Name of the invoked skill + Name string `json:"name"` + // File path to the SKILL.md definition + Path string `json:"path"` + // Name of the plugin this skill originated from, when applicable + PluginName *string `json:"pluginName,omitempty"` + // Version of the plugin this skill originated from, when applicable + PluginVersion *string `json:"pluginVersion,omitempty"` +} + +func (*SkillInvokedData) sessionEventData() {} +func (*SkillInvokedData) Type() SessionEventType { return SessionEventTypeSkillInvoked } + +// Streaming assistant message delta for incremental response updates +type AssistantMessageDeltaData struct { + // Incremental text chunk to append to the message content + DeltaContent string `json:"deltaContent"` + // Message ID this delta belongs to, matching the corresponding assistant.message event + MessageID string `json:"messageId"` + // 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"` +} + +func (*AssistantMessageDeltaData) sessionEventData() {} +func (*AssistantMessageDeltaData) Type() SessionEventType { + return SessionEventTypeAssistantMessageDelta +} + +// Streaming assistant message start metadata +type AssistantMessageStartData struct { + // Message ID this start event belongs to, matching subsequent deltas and assistant.message + MessageID string `json:"messageId"` + // Generation phase this message belongs to for phased-output models + Phase *string `json:"phase,omitempty"` +} + +func (*AssistantMessageStartData) sessionEventData() {} +func (*AssistantMessageStartData) Type() SessionEventType { + return SessionEventTypeAssistantMessageStart +} + +// Streaming reasoning delta for incremental extended thinking updates +type AssistantReasoningDeltaData struct { + // Incremental text chunk to append to the reasoning content + DeltaContent string `json:"deltaContent"` + // Reasoning block ID this delta belongs to, matching the corresponding assistant.reasoning event + ReasoningID string `json:"reasoningId"` +} + +func (*AssistantReasoningDeltaData) sessionEventData() {} +func (*AssistantReasoningDeltaData) Type() SessionEventType { + return SessionEventTypeAssistantReasoningDelta +} + +// Streaming response progress with cumulative byte count +type AssistantStreamingDeltaData struct { + // Cumulative total bytes received from the streaming response so far + TotalResponseSizeBytes float64 `json:"totalResponseSizeBytes"` +} + +func (*AssistantStreamingDeltaData) sessionEventData() {} +func (*AssistantStreamingDeltaData) Type() SessionEventType { + return SessionEventTypeAssistantStreamingDelta +} + +// Streaming tool execution output for incremental result display +type ToolExecutionPartialResultData struct { + // Incremental output chunk from the running tool + PartialOutput string `json:"partialOutput"` + // Tool call ID this partial result belongs to + ToolCallID string `json:"toolCallId"` +} + +func (*ToolExecutionPartialResultData) sessionEventData() {} +func (*ToolExecutionPartialResultData) Type() SessionEventType { + return SessionEventTypeToolExecutionPartialResult +} + +// Sub-agent completion details for successful execution +type SubagentCompletedData struct { + // Human-readable display name of the sub-agent + AgentDisplayName string `json:"agentDisplayName"` + // Internal name of the sub-agent + AgentName string `json:"agentName"` + // Wall-clock duration of the sub-agent execution in milliseconds + DurationMs *float64 `json:"durationMs,omitempty"` + // Model used by the sub-agent + Model *string `json:"model,omitempty"` + // Tool call ID of the parent tool invocation that spawned this sub-agent + ToolCallID string `json:"toolCallId"` + // Total tokens (input + output) consumed by the sub-agent + TotalTokens *float64 `json:"totalTokens,omitempty"` + // Total number of tool calls made by the sub-agent + TotalToolCalls *float64 `json:"totalToolCalls,omitempty"` +} + +func (*SubagentCompletedData) sessionEventData() {} +func (*SubagentCompletedData) Type() SessionEventType { return SessionEventTypeSubagentCompleted } + +// Sub-agent failure details including error message and agent information +type SubagentFailedData struct { + // Human-readable display name of the sub-agent + AgentDisplayName string `json:"agentDisplayName"` + // Internal name of the sub-agent + AgentName string `json:"agentName"` + // Wall-clock duration of the sub-agent execution in milliseconds + DurationMs *float64 `json:"durationMs,omitempty"` + // Error message describing why the sub-agent failed + Error string `json:"error"` + // Model used by the sub-agent (if any model calls succeeded before failure) + Model *string `json:"model,omitempty"` + // Tool call ID of the parent tool invocation that spawned this sub-agent + ToolCallID string `json:"toolCallId"` + // Total tokens (input + output) consumed before the sub-agent failed + TotalTokens *float64 `json:"totalTokens,omitempty"` + // Total number of tool calls made before the sub-agent failed + TotalToolCalls *float64 `json:"totalToolCalls,omitempty"` +} + +func (*SubagentFailedData) sessionEventData() {} +func (*SubagentFailedData) Type() SessionEventType { return SessionEventTypeSubagentFailed } + +// Sub-agent startup details including parent tool call and agent information +type SubagentStartedData struct { + // Description of what the sub-agent does + AgentDescription string `json:"agentDescription"` + // Human-readable display name of the sub-agent + AgentDisplayName string `json:"agentDisplayName"` + // Internal name of the sub-agent + AgentName string `json:"agentName"` + // Model the sub-agent will run with, when known at start. Surfaced in the timeline for auto-selected sub-agents (e.g. rubber-duck). + Model *string `json:"model,omitempty"` + // Tool call ID of the parent tool invocation that spawned this sub-agent + ToolCallID string `json:"toolCallId"` +} + +func (*SubagentStartedData) sessionEventData() {} +func (*SubagentStartedData) Type() SessionEventType { return SessionEventTypeSubagentStarted } + +// System-generated notification for runtime events like background task completion +type SystemNotificationData struct { + // The notification text, typically wrapped in XML tags + Content string `json:"content"` + // Structured metadata identifying what triggered this notification + Kind SystemNotification `json:"kind"` +} + +func (*SystemNotificationData) sessionEventData() {} +func (*SystemNotificationData) Type() SessionEventType { return SessionEventTypeSystemNotification } + +// System/developer instruction content with role and optional template metadata +type SystemMessageData struct { + // The system or developer prompt text sent as model input + Content string `json:"content"` + // Metadata about the prompt template and its construction + Metadata *SystemMessageMetadata `json:"metadata,omitempty"` + // Optional name identifier for the message source + Name *string `json:"name,omitempty"` + // Message role: "system" for system prompts, "developer" for developer-injected instructions + Role SystemMessageRole `json:"role"` +} + +func (*SystemMessageData) sessionEventData() {} +func (*SystemMessageData) Type() SessionEventType { return SessionEventTypeSystemMessage } + +// Task completion notification with summary from the agent +type SessionTaskCompleteData struct { + // Whether the tool call succeeded. False when validation failed (e.g., invalid arguments) + Success *bool `json:"success,omitempty"` + // Summary of the completed task, provided by the agent + Summary *string `json:"summary,omitempty"` +} + +func (*SessionTaskCompleteData) sessionEventData() {} +func (*SessionTaskCompleteData) Type() SessionEventType { return SessionEventTypeSessionTaskComplete } + +// Tool execution completion results including success status, detailed output, and error information +type ToolExecutionCompleteData struct { + // Error details when the tool execution failed + Error *ToolExecutionCompleteError `json:"error,omitempty"` + // CAPI interaction ID for correlating this tool execution with upstream telemetry + InteractionID *string `json:"interactionId,omitempty"` + // Whether this tool call was explicitly requested by the user rather than the assistant + IsUserRequested *bool `json:"isUserRequested,omitempty"` + // Model identifier that generated this tool call + Model *string `json:"model,omitempty"` + // Tool call ID of the parent tool invocation when this event originates from a sub-agent + // Deprecated: ParentToolCallID is deprecated. + ParentToolCallID *string `json:"parentToolCallId,omitempty"` + // Tool execution result on success + Result *ToolExecutionCompleteResult `json:"result,omitempty"` + // Whether the tool execution completed successfully + Success bool `json:"success"` + // Unique identifier for the completed tool call + ToolCallID string `json:"toolCallId"` + // Tool-specific telemetry data (e.g., CodeQL check counts, grep match counts) + ToolTelemetry map[string]any `json:"toolTelemetry,omitempty"` + // Identifier for the agent loop turn this tool was invoked in, matching the corresponding assistant.turn_start event + TurnID *string `json:"turnId,omitempty"` +} + +func (*ToolExecutionCompleteData) sessionEventData() {} +func (*ToolExecutionCompleteData) Type() SessionEventType { + return SessionEventTypeToolExecutionComplete +} + +// Tool execution progress notification with status message +type ToolExecutionProgressData struct { + // Human-readable progress status message (e.g., from an MCP server) + ProgressMessage string `json:"progressMessage"` + // Tool call ID this progress notification belongs to + ToolCallID string `json:"toolCallId"` +} + +func (*ToolExecutionProgressData) sessionEventData() {} +func (*ToolExecutionProgressData) Type() SessionEventType { + return SessionEventTypeToolExecutionProgress +} + +// Tool execution startup details including MCP server information when applicable +type ToolExecutionStartData struct { + // Arguments passed to the tool + Arguments any `json:"arguments,omitempty"` + // Name of the MCP server hosting this tool, when the tool is an MCP tool + McpServerName *string `json:"mcpServerName,omitempty"` + // Original tool name on the MCP server, when the tool is an MCP tool + McpToolName *string `json:"mcpToolName,omitempty"` + // Tool call ID of the parent tool invocation when this event originates from a sub-agent + // Deprecated: ParentToolCallID is deprecated. + ParentToolCallID *string `json:"parentToolCallId,omitempty"` + // Unique identifier for this tool call + ToolCallID string `json:"toolCallId"` + // Name of the tool being executed + ToolName string `json:"toolName"` + // Identifier for the agent loop turn this tool was invoked in, matching the corresponding assistant.turn_start event + TurnID *string `json:"turnId,omitempty"` +} + +func (*ToolExecutionStartData) sessionEventData() {} +func (*ToolExecutionStartData) Type() SessionEventType { return SessionEventTypeToolExecutionStart } + +// Turn abort information including the reason for termination +type AbortData struct { + // Finite reason code describing why the current turn was aborted + Reason AbortReason `json:"reason"` +} + +func (*AbortData) sessionEventData() {} +func (*AbortData) Type() SessionEventType { return SessionEventTypeAbort } + +// Turn completion metadata including the turn identifier +type AssistantTurnEndData struct { + // Identifier of the turn that has ended, matching the corresponding assistant.turn_start event + TurnID string `json:"turnId"` +} + +func (*AssistantTurnEndData) sessionEventData() {} +func (*AssistantTurnEndData) Type() SessionEventType { return SessionEventTypeAssistantTurnEnd } + +// Turn initialization metadata including identifier and interaction tracking +type AssistantTurnStartData struct { + // CAPI interaction ID for correlating this turn with upstream telemetry + InteractionID *string `json:"interactionId,omitempty"` + // Identifier for this turn within the agentic loop, typically a stringified turn number + TurnID string `json:"turnId"` +} + +func (*AssistantTurnStartData) sessionEventData() {} +func (*AssistantTurnStartData) Type() SessionEventType { return SessionEventTypeAssistantTurnStart } + +// User input request completion with the user's response +type UserInputCompletedData struct { + // The user's answer to the input request + Answer *string `json:"answer,omitempty"` + // Request ID of the resolved user input request; clients should dismiss any UI for this request + RequestID string `json:"requestId"` + // Whether the answer was typed as free-form text rather than selected from choices + WasFreeform *bool `json:"wasFreeform,omitempty"` +} + +func (*UserInputCompletedData) sessionEventData() {} +func (*UserInputCompletedData) Type() SessionEventType { return SessionEventTypeUserInputCompleted } + +// User input request notification with question and optional predefined choices +type UserInputRequestedData struct { + // Whether the user can provide a free-form text response in addition to predefined choices + AllowFreeform *bool `json:"allowFreeform,omitempty"` + // Predefined choices for the user to select from, if applicable + Choices []string `json:"choices,omitempty"` + // The question or prompt to present to the user + Question string `json:"question"` + // Unique identifier for this input request; used to respond via session.respondToUserInput() + RequestID string `json:"requestId"` + // The LLM-assigned tool call ID that triggered this request; used by remote UIs to correlate responses + ToolCallID *string `json:"toolCallId,omitempty"` +} + +func (*UserInputRequestedData) sessionEventData() {} +func (*UserInputRequestedData) Type() SessionEventType { return SessionEventTypeUserInputRequested } + +// User-initiated tool invocation request with tool name and arguments +type ToolUserRequestedData struct { + // Arguments for the tool invocation + Arguments any `json:"arguments,omitempty"` + // Unique identifier for this tool call + ToolCallID string `json:"toolCallId"` + // Name of the tool the user wants to invoke + ToolName string `json:"toolName"` +} + +func (*ToolUserRequestedData) sessionEventData() {} +func (*ToolUserRequestedData) Type() SessionEventType { return SessionEventTypeToolUserRequested } + +// UserMessageData holds the payload for user.message events. +type UserMessageData struct { + // The agent mode that was active when this message was sent + AgentMode *UserMessageAgentMode `json:"agentMode,omitempty"` + // Files, selections, or GitHub references attached to the message + Attachments []UserMessageAttachment `json:"attachments,omitempty"` + // The user's message text as displayed in the timeline + Content string `json:"content"` + // CAPI interaction ID for correlating this user message with its turn + InteractionID *string `json:"interactionId,omitempty"` + // True when this user message was auto-injected by autopilot's continuation loop rather than typed by the user; used to distinguish autopilot-driven turns in telemetry. + IsAutopilotContinuation *bool `json:"isAutopilotContinuation,omitempty"` + // Path-backed native document attachments that stayed on the tagged_files path flow because native upload would exceed the request size limit + NativeDocumentPathFallbackPaths []string `json:"nativeDocumentPathFallbackPaths,omitempty"` + // 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) + Source *string `json:"source,omitempty"` + // Normalized document MIME types that were sent natively instead of through tagged_files XML + SupportedNativeDocumentMIMETypes []string `json:"supportedNativeDocumentMimeTypes,omitempty"` + // Transformed version of the message sent to the model, with XML wrapping, timestamps, and other augmentations for prompt caching + TransformedContent *string `json:"transformedContent,omitempty"` +} + +func (*UserMessageData) sessionEventData() {} +func (*UserMessageData) Type() SessionEventType { return SessionEventTypeUserMessage } + +// Warning message for timeline display with categorization +type SessionWarningData struct { + // Human-readable warning message for display in the timeline + Message string `json:"message"` + // Optional URL associated with this warning that the user can open in a browser + URL *string `json:"url,omitempty"` + // Category of warning (e.g., "subscription", "policy", "mcp") + WarningType string `json:"warningType"` +} + +func (*SessionWarningData) sessionEventData() {} +func (*SessionWarningData) Type() SessionEventType { return SessionEventTypeSessionWarning } + +// Working directory and git context at session start +type SessionContextChangedData struct { + // Base commit of current git branch at session start time + BaseCommit *string `json:"baseCommit,omitempty"` + // Current git branch name + Branch *string `json:"branch,omitempty"` + // Current working directory path + Cwd string `json:"cwd"` + // Root directory of the git repository, resolved via git rev-parse + GitRoot *string `json:"gitRoot,omitempty"` + // Head commit of current git branch at session start time + HeadCommit *string `json:"headCommit,omitempty"` + // Hosting platform type of the repository (github or ado) + HostType *WorkingDirectoryContextHostType `json:"hostType,omitempty"` + // Repository identifier derived from the git remote URL ("owner/name" for GitHub, "org/project/repo" for Azure DevOps) + Repository *string `json:"repository,omitempty"` + // Raw host string from the git remote URL (e.g. "github.com", "mycompany.ghe.com", "dev.azure.com") + RepositoryHost *string `json:"repositoryHost,omitempty"` +} + +func (*SessionContextChangedData) sessionEventData() {} +func (*SessionContextChangedData) Type() SessionEventType { + return SessionEventTypeSessionContextChanged +} + +// Workspace file change details including path and operation type +type SessionWorkspaceFileChangedData struct { + // Whether the file was newly created or updated + Operation WorkspaceFileChangedOperation `json:"operation"` + // Relative path within the session workspace files directory + Path string `json:"path"` +} + +func (*SessionWorkspaceFileChangedData) sessionEventData() {} +func (*SessionWorkspaceFileChangedData) Type() SessionEventType { + return SessionEventTypeSessionWorkspaceFileChanged +} + +// A tool invocation request from the assistant +type AssistantMessageToolRequest struct { + // Arguments to pass to the tool, format depends on the tool + Arguments any `json:"arguments,omitempty"` + // Resolved intention summary describing what this specific call does + IntentionSummary *string `json:"intentionSummary,omitempty"` + // Name of the MCP server hosting this tool, when the tool is an MCP tool + McpServerName *string `json:"mcpServerName,omitempty"` + // Original tool name on the MCP server, when the tool is an MCP tool + McpToolName *string `json:"mcpToolName,omitempty"` + // Name of the tool being invoked + Name string `json:"name"` + // Unique identifier for this tool call + ToolCallID string `json:"toolCallId"` + // Human-readable display title for the tool + ToolTitle *string `json:"toolTitle,omitempty"` + // Tool call type: "function" for standard tool calls, "custom" for grammar-based tool calls. Defaults to "function" when absent. + Type *AssistantMessageToolRequestType `json:"type,omitempty"` +} + +// Per-request cost and usage data from the CAPI copilot_usage response field +type AssistantUsageCopilotUsage struct { + // Itemized token usage breakdown + TokenDetails []AssistantUsageCopilotUsageTokenDetail `json:"tokenDetails"` + // Total cost in nano-AI units for this request + TotalNanoAiu float64 `json:"totalNanoAiu"` +} + +// Token usage detail for a single billing category +type AssistantUsageCopilotUsageTokenDetail struct { + // Number of tokens in this billing batch + BatchSize float64 `json:"batchSize"` + // Cost per batch of tokens + CostPerBatch float64 `json:"costPerBatch"` + // Total token count for this entry + TokenCount float64 `json:"tokenCount"` + // Token category (e.g., "input", "output") + TokenType string `json:"tokenType"` +} + +type AssistantUsageQuotaSnapshot struct { + // Total requests allowed by the entitlement + EntitlementRequests float64 `json:"entitlementRequests"` + // Whether the user has an unlimited usage entitlement + IsUnlimitedEntitlement bool `json:"isUnlimitedEntitlement"` + // Number of requests over the entitlement limit + Overage float64 `json:"overage"` + // Whether overage is allowed when quota is exhausted + OverageAllowedWithExhaustedQuota bool `json:"overageAllowedWithExhaustedQuota"` + // Percentage of quota remaining (0.0 to 1.0) + RemainingPercentage float64 `json:"remainingPercentage"` + // Date when the quota resets + ResetDate *time.Time `json:"resetDate,omitempty"` + // Whether usage is still permitted after quota exhaustion + UsageAllowedWithExhaustedQuota bool `json:"usageAllowedWithExhaustedQuota"` + // Number of requests already consumed + UsedRequests float64 `json:"usedRequests"` +} + +// UI capability changes +type CapabilitiesChangedUI struct { + // Whether elicitation is now supported + Elicitation *bool `json:"elicitation,omitempty"` +} + +type CommandsChangedCommand struct { + Description *string `json:"description,omitempty"` + Name string `json:"name"` +} + +// Token usage breakdown for the compaction LLM call (aligned with assistant.usage format) +type CompactionCompleteCompactionTokensUsed struct { + // Cached input tokens reused in the compaction LLM call + CacheReadTokens *float64 `json:"cacheReadTokens,omitempty"` + // Tokens written to prompt cache in the compaction LLM call + CacheWriteTokens *float64 `json:"cacheWriteTokens,omitempty"` + // Per-request cost and usage data from the CAPI copilot_usage response field + CopilotUsage *CompactionCompleteCompactionTokensUsedCopilotUsage `json:"copilotUsage,omitempty"` + // Duration of the compaction LLM call in milliseconds + Duration *float64 `json:"duration,omitempty"` + // Input tokens consumed by the compaction LLM call + InputTokens *float64 `json:"inputTokens,omitempty"` + // Model identifier used for the compaction LLM call + Model *string `json:"model,omitempty"` + // Output tokens produced by the compaction LLM call + OutputTokens *float64 `json:"outputTokens,omitempty"` +} + +// Per-request cost and usage data from the CAPI copilot_usage response field +type CompactionCompleteCompactionTokensUsedCopilotUsage struct { + // Itemized token usage breakdown + TokenDetails []CompactionCompleteCompactionTokensUsedCopilotUsageTokenDetail `json:"tokenDetails"` + // Total cost in nano-AI units for this request + TotalNanoAiu float64 `json:"totalNanoAiu"` +} + +// Token usage detail for a single billing category +type CompactionCompleteCompactionTokensUsedCopilotUsageTokenDetail struct { + // Number of tokens in this billing batch + BatchSize float64 `json:"batchSize"` + // Cost per batch of tokens + CostPerBatch float64 `json:"costPerBatch"` + // Total token count for this entry + TokenCount float64 `json:"tokenCount"` + // Token category (e.g., "input", "output") + TokenType string `json:"tokenType"` +} + +type CustomAgentsUpdatedAgent struct { + // Description of what the agent does + Description string `json:"description"` + // Human-readable display name + DisplayName string `json:"displayName"` + // Unique identifier for the agent + ID string `json:"id"` + // Model override for this agent, if set + Model *string `json:"model,omitempty"` + // Internal name of the agent + Name string `json:"name"` + // Source location: user, project, inherited, remote, or plugin + Source string `json:"source"` + // List of tool names available to this agent, or null when all tools are available + Tools []string `json:"tools"` + // Whether the agent can be selected by the user + UserInvocable bool `json:"userInvocable"` +} + +type ElicitationCompletedContent interface { + elicitationCompletedContent() +} + +type ElicitationCompletedBooleanContent bool + +func (ElicitationCompletedBooleanContent) elicitationCompletedContent() {} + +type ElicitationCompletedNumberContent float64 + +func (ElicitationCompletedNumberContent) elicitationCompletedContent() {} + +type ElicitationCompletedStringArrayContent []string + +func (ElicitationCompletedStringArrayContent) elicitationCompletedContent() {} + +type ElicitationCompletedStringContent string + +func (ElicitationCompletedStringContent) elicitationCompletedContent() {} + +// JSON Schema describing the form fields to present to the user (form mode only) +type ElicitationRequestedSchema struct { + // Form field definitions, keyed by field name + Properties map[string]any `json:"properties"` + // List of required field names + Required []string `json:"required,omitempty"` + // Schema type indicator (always 'object') + Type ElicitationRequestedSchemaType `json:"type"` +} + +type ExtensionsLoadedExtension struct { + // Source-qualified extension ID (e.g., 'project:my-ext', 'user:auth-helper') + ID string `json:"id"` + // Extension name (directory name) + Name string `json:"name"` + // Discovery source + Source ExtensionsLoadedExtensionSource `json:"source"` + // Current status: running, disabled, failed, or starting + Status ExtensionsLoadedExtensionStatus `json:"status"` +} + +// Repository context for the handed-off session +type HandoffRepository struct { + // Git branch name, if applicable + Branch *string `json:"branch,omitempty"` + // Repository name + Name string `json:"name"` + // Repository owner (user or organization) + Owner string `json:"owner"` +} + +// Error details when the hook failed +type HookEndError struct { + // Human-readable error message + Message string `json:"message"` + // Error stack trace, when available + Stack *string `json:"stack,omitempty"` +} + +// Static OAuth client configuration, if the server specifies one +type McpOauthRequiredStaticClientConfig struct { + // OAuth client ID for the server + ClientID string `json:"clientId"` + // Optional non-default OAuth grant type. When set to 'client_credentials', the OAuth flow runs headlessly using the client_id + keychain-stored secret (no browser, no callback server). + GrantType *McpOauthRequiredStaticClientConfigGrantType `json:"grantType,omitempty"` + // Whether this is a public OAuth client + PublicClient *bool `json:"publicClient,omitempty"` +} + +type McpServersLoadedServer struct { + // Error message if the server failed to connect + Error *string `json:"error,omitempty"` + // Server name (config key) + Name string `json:"name"` + // Configuration source: user, workspace, plugin, or builtin + Source *string `json:"source,omitempty"` + // Connection status: connected, failed, needs-auth, pending, disabled, or not_configured + Status McpServersLoadedServerStatus `json:"status"` +} + +// Derived user-facing permission prompt details for UI consumers +type PermissionPromptRequest interface { + permissionPromptRequest() + Kind() PermissionPromptRequestKind +} + +type RawPermissionPromptRequest struct { + Discriminator PermissionPromptRequestKind + Raw json.RawMessage +} + +func (RawPermissionPromptRequest) permissionPromptRequest() {} +func (r RawPermissionPromptRequest) Kind() PermissionPromptRequestKind { + return r.Discriminator +} + +// Shell command permission prompt +type PermissionPromptRequestCommands struct { + // Whether the UI can offer session-wide approval for this command pattern + CanOfferSessionApproval bool `json:"canOfferSessionApproval"` + // Command identifiers covered by this approval prompt + CommandIdentifiers []string `json:"commandIdentifiers"` + // The complete shell command text to be executed + FullCommandText string `json:"fullCommandText"` + // Human-readable description of what the command intends to do + Intention string `json:"intention"` + // Tool call ID that triggered this permission request + ToolCallID *string `json:"toolCallId,omitempty"` + // Optional warning message about risks of running this command + Warning *string `json:"warning,omitempty"` +} + +func (PermissionPromptRequestCommands) permissionPromptRequest() {} +func (PermissionPromptRequestCommands) Kind() PermissionPromptRequestKind { + return PermissionPromptRequestKindCommands +} + +// Custom tool invocation permission prompt +type PermissionPromptRequestCustomTool struct { + // Arguments to pass to the custom tool + Args any `json:"args,omitempty"` + // Tool call ID that triggered this permission request + ToolCallID *string `json:"toolCallId,omitempty"` + // Description of what the custom tool does + ToolDescription string `json:"toolDescription"` + // Name of the custom tool + ToolName string `json:"toolName"` +} + +func (PermissionPromptRequestCustomTool) permissionPromptRequest() {} +func (PermissionPromptRequestCustomTool) Kind() PermissionPromptRequestKind { + return PermissionPromptRequestKindCustomTool +} + +// Extension management permission prompt +type PermissionPromptRequestExtensionManagement struct { + // Name of the extension being managed + ExtensionName *string `json:"extensionName,omitempty"` + // The extension management operation (scaffold, reload) + Operation string `json:"operation"` + // Tool call ID that triggered this permission request + ToolCallID *string `json:"toolCallId,omitempty"` +} + +func (PermissionPromptRequestExtensionManagement) permissionPromptRequest() {} +func (PermissionPromptRequestExtensionManagement) Kind() PermissionPromptRequestKind { + return PermissionPromptRequestKindExtensionManagement +} + +// Extension permission access prompt +type PermissionPromptRequestExtensionPermissionAccess struct { + // Capabilities the extension is requesting + Capabilities []string `json:"capabilities"` + // Name of the extension requesting permission access + ExtensionName string `json:"extensionName"` + // Tool call ID that triggered this permission request + ToolCallID *string `json:"toolCallId,omitempty"` +} + +func (PermissionPromptRequestExtensionPermissionAccess) permissionPromptRequest() {} +func (PermissionPromptRequestExtensionPermissionAccess) Kind() PermissionPromptRequestKind { + return PermissionPromptRequestKindExtensionPermissionAccess +} + +// Hook confirmation permission prompt +type PermissionPromptRequestHook struct { + // Optional message from the hook explaining why confirmation is needed + HookMessage *string `json:"hookMessage,omitempty"` + // Arguments of the tool call being gated + ToolArgs any `json:"toolArgs,omitempty"` + // Tool call ID that triggered this permission request + ToolCallID *string `json:"toolCallId,omitempty"` + // Name of the tool the hook is gating + ToolName string `json:"toolName"` +} + +func (PermissionPromptRequestHook) permissionPromptRequest() {} +func (PermissionPromptRequestHook) Kind() PermissionPromptRequestKind { + return PermissionPromptRequestKindHook +} + +// MCP tool invocation permission prompt +type PermissionPromptRequestMcp struct { + // Arguments to pass to the MCP tool + Args *any `json:"args,omitempty"` + // Name of the MCP server providing the tool + ServerName string `json:"serverName"` + // Tool call ID that triggered this permission request + ToolCallID *string `json:"toolCallId,omitempty"` + // Internal name of the MCP tool + ToolName string `json:"toolName"` + // Human-readable title of the MCP tool + ToolTitle string `json:"toolTitle"` +} + +func (PermissionPromptRequestMcp) permissionPromptRequest() {} +func (PermissionPromptRequestMcp) Kind() PermissionPromptRequestKind { + return PermissionPromptRequestKindMcp +} + +// Memory operation permission prompt +type PermissionPromptRequestMemory struct { + // Whether this is a store or vote memory operation + Action *PermissionPromptRequestMemoryAction `json:"action,omitempty"` + // Source references for the stored fact (store only) + Citations *string `json:"citations,omitempty"` + // Vote direction (vote only) + Direction *PermissionPromptRequestMemoryDirection `json:"direction,omitempty"` + // The fact being stored or voted on + Fact string `json:"fact"` + // Reason for the vote (vote only) + Reason *string `json:"reason,omitempty"` + // Topic or subject of the memory (store only) + Subject *string `json:"subject,omitempty"` + // Tool call ID that triggered this permission request + ToolCallID *string `json:"toolCallId,omitempty"` +} + +func (PermissionPromptRequestMemory) permissionPromptRequest() {} +func (PermissionPromptRequestMemory) Kind() PermissionPromptRequestKind { + return PermissionPromptRequestKindMemory +} + +// Path access permission prompt +type PermissionPromptRequestPath struct { + // Underlying permission kind that needs path approval + AccessKind PermissionPromptRequestPathAccessKind `json:"accessKind"` + // File paths that require explicit approval + Paths []string `json:"paths"` + // Tool call ID that triggered this permission request + ToolCallID *string `json:"toolCallId,omitempty"` +} + +func (PermissionPromptRequestPath) permissionPromptRequest() {} +func (PermissionPromptRequestPath) Kind() PermissionPromptRequestKind { + return PermissionPromptRequestKindPath +} + +// File read permission prompt +type PermissionPromptRequestRead struct { + // Human-readable description of why the file is being read + Intention string `json:"intention"` + // Path of the file or directory being read + Path string `json:"path"` + // Tool call ID that triggered this permission request + ToolCallID *string `json:"toolCallId,omitempty"` +} + +func (PermissionPromptRequestRead) permissionPromptRequest() {} +func (PermissionPromptRequestRead) Kind() PermissionPromptRequestKind { + return PermissionPromptRequestKindRead +} + +// URL access permission prompt +type PermissionPromptRequestURL struct { + // Human-readable description of why the URL is being accessed + Intention string `json:"intention"` + // Tool call ID that triggered this permission request + ToolCallID *string `json:"toolCallId,omitempty"` + // URL to be fetched + URL string `json:"url"` +} + +func (PermissionPromptRequestURL) permissionPromptRequest() {} +func (PermissionPromptRequestURL) Kind() PermissionPromptRequestKind { + return PermissionPromptRequestKindURL +} + +// File write permission prompt +type PermissionPromptRequestWrite struct { + // Whether the UI can offer session-wide approval for file write operations + CanOfferSessionApproval bool `json:"canOfferSessionApproval"` + // Unified diff showing the proposed changes + Diff string `json:"diff"` + // Path of the file being written to + FileName string `json:"fileName"` + // Human-readable description of the intended file change + Intention string `json:"intention"` + // Complete new file contents for newly created files + NewFileContents *string `json:"newFileContents,omitempty"` + // Tool call ID that triggered this permission request + ToolCallID *string `json:"toolCallId,omitempty"` +} + +func (PermissionPromptRequestWrite) permissionPromptRequest() {} +func (PermissionPromptRequestWrite) Kind() PermissionPromptRequestKind { + return PermissionPromptRequestKindWrite +} + +// Details of the permission being requested +type PermissionRequest interface { + permissionRequest() + Kind() PermissionRequestKind +} + +type RawPermissionRequest struct { + Discriminator PermissionRequestKind + Raw json.RawMessage +} + +func (RawPermissionRequest) permissionRequest() {} +func (r RawPermissionRequest) Kind() PermissionRequestKind { + return r.Discriminator +} + +// Custom tool invocation permission request +type PermissionRequestCustomTool struct { + // Arguments to pass to the custom tool + Args any `json:"args,omitempty"` + // Tool call ID that triggered this permission request + ToolCallID *string `json:"toolCallId,omitempty"` + // Description of what the custom tool does + ToolDescription string `json:"toolDescription"` + // Name of the custom tool + ToolName string `json:"toolName"` +} + +func (PermissionRequestCustomTool) permissionRequest() {} +func (PermissionRequestCustomTool) Kind() PermissionRequestKind { + return PermissionRequestKindCustomTool +} + +// Extension management permission request +type PermissionRequestExtensionManagement struct { + // Name of the extension being managed + ExtensionName *string `json:"extensionName,omitempty"` + // The extension management operation (scaffold, reload) + Operation string `json:"operation"` + // Tool call ID that triggered this permission request + ToolCallID *string `json:"toolCallId,omitempty"` +} + +func (PermissionRequestExtensionManagement) permissionRequest() {} +func (PermissionRequestExtensionManagement) Kind() PermissionRequestKind { + return PermissionRequestKindExtensionManagement +} + +// Extension permission access request +type PermissionRequestExtensionPermissionAccess struct { + // Capabilities the extension is requesting + Capabilities []string `json:"capabilities"` + // Name of the extension requesting permission access + ExtensionName string `json:"extensionName"` + // Tool call ID that triggered this permission request + ToolCallID *string `json:"toolCallId,omitempty"` +} + +func (PermissionRequestExtensionPermissionAccess) permissionRequest() {} +func (PermissionRequestExtensionPermissionAccess) Kind() PermissionRequestKind { + return PermissionRequestKindExtensionPermissionAccess +} + +// Hook confirmation permission request +type PermissionRequestHook struct { + // Optional message from the hook explaining why confirmation is needed + HookMessage *string `json:"hookMessage,omitempty"` + // Arguments of the tool call being gated + ToolArgs any `json:"toolArgs,omitempty"` + // Tool call ID that triggered this permission request + ToolCallID *string `json:"toolCallId,omitempty"` + // Name of the tool the hook is gating + ToolName string `json:"toolName"` +} + +func (PermissionRequestHook) permissionRequest() {} +func (PermissionRequestHook) Kind() PermissionRequestKind { + return PermissionRequestKindHook +} + +// MCP tool invocation permission request +type PermissionRequestMcp struct { + // Arguments to pass to the MCP tool + Args any `json:"args,omitempty"` + // Whether this MCP tool is read-only (no side effects) + ReadOnly bool `json:"readOnly"` + // Name of the MCP server providing the tool + ServerName string `json:"serverName"` + // Tool call ID that triggered this permission request + ToolCallID *string `json:"toolCallId,omitempty"` + // Internal name of the MCP tool + ToolName string `json:"toolName"` + // Human-readable title of the MCP tool + ToolTitle string `json:"toolTitle"` +} + +func (PermissionRequestMcp) permissionRequest() {} +func (PermissionRequestMcp) Kind() PermissionRequestKind { + return PermissionRequestKindMcp +} + +// Memory operation permission request +type PermissionRequestMemory struct { + // Whether this is a store or vote memory operation + Action *PermissionRequestMemoryAction `json:"action,omitempty"` + // Source references for the stored fact (store only) + Citations *string `json:"citations,omitempty"` + // Vote direction (vote only) + Direction *PermissionRequestMemoryDirection `json:"direction,omitempty"` + // The fact being stored or voted on + Fact string `json:"fact"` + // Reason for the vote (vote only) + Reason *string `json:"reason,omitempty"` + // Topic or subject of the memory (store only) + Subject *string `json:"subject,omitempty"` + // Tool call ID that triggered this permission request + ToolCallID *string `json:"toolCallId,omitempty"` +} + +func (PermissionRequestMemory) permissionRequest() {} +func (PermissionRequestMemory) Kind() PermissionRequestKind { + return PermissionRequestKindMemory +} + +// File or directory read permission request +type PermissionRequestRead struct { + // Human-readable description of why the file is being read + Intention string `json:"intention"` + // Path of the file or directory being read + Path string `json:"path"` + // Tool call ID that triggered this permission request + ToolCallID *string `json:"toolCallId,omitempty"` +} + +func (PermissionRequestRead) permissionRequest() {} +func (PermissionRequestRead) Kind() PermissionRequestKind { + return PermissionRequestKindRead +} + +// Shell command permission request +type PermissionRequestShell struct { + // Whether the UI can offer session-wide approval for this command pattern + CanOfferSessionApproval bool `json:"canOfferSessionApproval"` + // Parsed command identifiers found in the command text + Commands []PermissionRequestShellCommand `json:"commands"` + // 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"` + // File paths that may be read or written by the command + PossiblePaths []string `json:"possiblePaths"` + // URLs that may be accessed by the command + PossibleUrls []PermissionRequestShellPossibleURL `json:"possibleUrls"` + // Tool call ID that triggered this permission request + ToolCallID *string `json:"toolCallId,omitempty"` + // Optional warning message about risks of running this command + Warning *string `json:"warning,omitempty"` +} + +func (PermissionRequestShell) permissionRequest() {} +func (PermissionRequestShell) Kind() PermissionRequestKind { + return PermissionRequestKindShell +} + +// URL access permission request +type PermissionRequestURL struct { + // Human-readable description of why the URL is being accessed + Intention string `json:"intention"` + // Tool call ID that triggered this permission request + ToolCallID *string `json:"toolCallId,omitempty"` + // URL to be fetched + URL string `json:"url"` +} + +func (PermissionRequestURL) permissionRequest() {} +func (PermissionRequestURL) Kind() PermissionRequestKind { + return PermissionRequestKindURL +} + +// File write permission request +type PermissionRequestWrite struct { + // Whether the UI can offer session-wide approval for file write operations + CanOfferSessionApproval bool `json:"canOfferSessionApproval"` + // Unified diff showing the proposed changes + Diff string `json:"diff"` + // Path of the file being written to + FileName string `json:"fileName"` + // Human-readable description of the intended file change + Intention string `json:"intention"` + // Complete new file contents for newly created files + NewFileContents *string `json:"newFileContents,omitempty"` + // Tool call ID that triggered this permission request + ToolCallID *string `json:"toolCallId,omitempty"` +} + +func (PermissionRequestWrite) permissionRequest() {} +func (PermissionRequestWrite) Kind() PermissionRequestKind { + return PermissionRequestKindWrite +} + +type PermissionRequestShellCommand struct { + // Command identifier (e.g., executable name) + Identifier string `json:"identifier"` + // Whether this command is read-only (no side effects) + ReadOnly bool `json:"readOnly"` +} + +type PermissionRequestShellPossibleURL struct { + // URL that may be accessed by the command + URL string `json:"url"` +} + +// The result of the permission request +type PermissionResult interface { + permissionResult() + Kind() PermissionResultKind +} + +type RawPermissionResult struct { + Discriminator PermissionResultKind + Raw json.RawMessage +} + +func (RawPermissionResult) permissionResult() {} +func (r RawPermissionResult) Kind() PermissionResultKind { + return r.Discriminator +} + +type PermissionApproved struct { +} + +func (PermissionApproved) permissionResult() {} +func (PermissionApproved) Kind() PermissionResultKind { + return PermissionResultKindApproved +} + +type PermissionApprovedForLocation struct { + // The approval to persist for this location + Approval UserToolSessionApproval `json:"approval"` + // The location key (git root or cwd) to persist the approval to + LocationKey string `json:"locationKey"` +} + +func (PermissionApprovedForLocation) permissionResult() {} +func (PermissionApprovedForLocation) Kind() PermissionResultKind { + return PermissionResultKindApprovedForLocation +} + +type PermissionApprovedForSession struct { + // The approval to add as a session-scoped rule + Approval UserToolSessionApproval `json:"approval"` +} + +func (PermissionApprovedForSession) permissionResult() {} +func (PermissionApprovedForSession) Kind() PermissionResultKind { + return PermissionResultKindApprovedForSession +} + +type PermissionCancelled struct { + // Optional explanation of why the request was cancelled + Reason *string `json:"reason,omitempty"` +} + +func (PermissionCancelled) permissionResult() {} +func (PermissionCancelled) Kind() PermissionResultKind { + return PermissionResultKindCancelled +} + +type PermissionDeniedByContentExclusionPolicy struct { + // Human-readable explanation of why the path was excluded + Message string `json:"message"` + // File path that triggered the exclusion + Path string `json:"path"` +} + +func (PermissionDeniedByContentExclusionPolicy) permissionResult() {} +func (PermissionDeniedByContentExclusionPolicy) Kind() PermissionResultKind { + return PermissionResultKindDeniedByContentExclusionPolicy +} + +type PermissionDeniedByPermissionRequestHook struct { + // Whether to interrupt the current agent turn + Interrupt *bool `json:"interrupt,omitempty"` + // Optional message from the hook explaining the denial + Message *string `json:"message,omitempty"` +} + +func (PermissionDeniedByPermissionRequestHook) permissionResult() {} +func (PermissionDeniedByPermissionRequestHook) Kind() PermissionResultKind { + return PermissionResultKindDeniedByPermissionRequestHook +} + +type PermissionDeniedByRules struct { + // Rules that denied the request + Rules []PermissionRule `json:"rules"` +} + +func (PermissionDeniedByRules) permissionResult() {} +func (PermissionDeniedByRules) Kind() PermissionResultKind { + return PermissionResultKindDeniedByRules +} + +type PermissionDeniedInteractivelyByUser struct { + // Optional feedback from the user explaining the denial + Feedback *string `json:"feedback,omitempty"` + // Whether to force-reject the current agent turn + ForceReject *bool `json:"forceReject,omitempty"` +} + +func (PermissionDeniedInteractivelyByUser) permissionResult() {} +func (PermissionDeniedInteractivelyByUser) Kind() PermissionResultKind { + return PermissionResultKindDeniedInteractivelyByUser +} + +type PermissionDeniedNoApprovalRuleAndCouldNotRequestFromUser struct { +} + +func (PermissionDeniedNoApprovalRuleAndCouldNotRequestFromUser) permissionResult() {} +func (PermissionDeniedNoApprovalRuleAndCouldNotRequestFromUser) Kind() PermissionResultKind { + return PermissionResultKindDeniedNoApprovalRuleAndCouldNotRequestFromUser +} + +type PermissionRule struct { + // Optional rule argument matched against the request + Argument *string `json:"argument"` + // The rule kind, such as Shell or GitHubMCP + Kind string `json:"kind"` +} + +// Aggregate code change metrics for the session +type ShutdownCodeChanges struct { + // List of file paths that were modified during the session + FilesModified []string `json:"filesModified"` + // Total number of lines added during the session + LinesAdded float64 `json:"linesAdded"` + // Total number of lines removed during the session + LinesRemoved float64 `json:"linesRemoved"` +} + +type ShutdownModelMetric struct { + // Request count and cost metrics + Requests ShutdownModelMetricRequests `json:"requests"` + // Token count details per type + TokenDetails map[string]ShutdownModelMetricTokenDetail `json:"tokenDetails,omitempty"` + // Accumulated nano-AI units cost for this model + TotalNanoAiu *float64 `json:"totalNanoAiu,omitempty"` + // Token usage breakdown + Usage ShutdownModelMetricUsage `json:"usage"` +} + +// Request count and cost metrics +type ShutdownModelMetricRequests struct { + // Cumulative cost multiplier for requests to this model + Cost float64 `json:"cost"` + // Total number of API requests made to this model + Count float64 `json:"count"` +} + +type ShutdownModelMetricTokenDetail struct { + // Accumulated token count for this token type + TokenCount float64 `json:"tokenCount"` +} + +// Token usage breakdown +type ShutdownModelMetricUsage struct { + // Total tokens read from prompt cache across all requests + CacheReadTokens float64 `json:"cacheReadTokens"` + // Total tokens written to prompt cache across all requests + CacheWriteTokens float64 `json:"cacheWriteTokens"` + // Total input tokens consumed across all requests to this model + InputTokens float64 `json:"inputTokens"` + // Total output tokens produced across all requests to this model + OutputTokens float64 `json:"outputTokens"` + // Total reasoning tokens produced across all requests to this model + ReasoningTokens *float64 `json:"reasoningTokens,omitempty"` +} + +type ShutdownTokenDetail struct { + // Accumulated token count for this token type + TokenCount float64 `json:"tokenCount"` +} + +type SkillsLoadedSkill struct { + // Description of what the skill does + Description string `json:"description"` + // Whether the skill is currently enabled + Enabled bool `json:"enabled"` + // Unique identifier for the skill + Name string `json:"name"` + // Absolute path to the skill file, if available + Path *string `json:"path,omitempty"` + // Source location type of the skill (e.g., project, personal, plugin) + Source string `json:"source"` + // Whether the skill can be invoked by the user as a slash command + UserInvocable bool `json:"userInvocable"` +} + +// Metadata about the prompt template and its construction +type SystemMessageMetadata struct { + // Version identifier of the prompt template used + PromptVersion *string `json:"promptVersion,omitempty"` + // Template variables used when constructing the prompt + Variables map[string]any `json:"variables,omitempty"` +} + +// Structured metadata identifying what triggered this notification +type SystemNotification interface { + systemNotification() + Type() SystemNotificationType +} + +type RawSystemNotification struct { + Discriminator SystemNotificationType + Raw json.RawMessage +} + +func (RawSystemNotification) systemNotification() {} +func (r RawSystemNotification) Type() SystemNotificationType { + return r.Discriminator +} + +type SystemNotificationAgentCompleted struct { + // Unique identifier of the background agent + AgentID string `json:"agentId"` + // Type of the agent (e.g., explore, task, general-purpose) + AgentType string `json:"agentType"` + // Human-readable description of the agent task + Description *string `json:"description,omitempty"` + // The full prompt given to the background agent + Prompt *string `json:"prompt,omitempty"` + // Whether the agent completed successfully or failed + Status SystemNotificationAgentCompletedStatus `json:"status"` +} + +func (SystemNotificationAgentCompleted) systemNotification() {} +func (SystemNotificationAgentCompleted) Type() SystemNotificationType { + return SystemNotificationTypeAgentCompleted +} + +type SystemNotificationAgentIdle struct { + // Unique identifier of the background agent + AgentID string `json:"agentId"` + // Type of the agent (e.g., explore, task, general-purpose) + AgentType string `json:"agentType"` + // Human-readable description of the agent task + Description *string `json:"description,omitempty"` +} + +func (SystemNotificationAgentIdle) systemNotification() {} +func (SystemNotificationAgentIdle) Type() SystemNotificationType { + return SystemNotificationTypeAgentIdle +} + +type SystemNotificationInstructionDiscovered struct { + // Human-readable label for the timeline (e.g., 'AGENTS.md from packages/billing/') + Description *string `json:"description,omitempty"` + // Relative path to the discovered instruction file + SourcePath string `json:"sourcePath"` + // Path of the file access that triggered discovery + TriggerFile string `json:"triggerFile"` + // Tool command that triggered discovery (currently always 'view') + TriggerTool string `json:"triggerTool"` +} + +func (SystemNotificationInstructionDiscovered) systemNotification() {} +func (SystemNotificationInstructionDiscovered) Type() SystemNotificationType { + return SystemNotificationTypeInstructionDiscovered +} + +type SystemNotificationNewInboxMessage struct { + // Unique identifier of the inbox entry + EntryID string `json:"entryId"` + // Human-readable name of the sender + SenderName string `json:"senderName"` + // Category of the sender (e.g., sidekick-agent, plugin, hook) + SenderType string `json:"senderType"` + // Short summary shown before the agent decides whether to read the inbox + Summary string `json:"summary"` +} + +func (SystemNotificationNewInboxMessage) systemNotification() {} +func (SystemNotificationNewInboxMessage) Type() SystemNotificationType { + return SystemNotificationTypeNewInboxMessage +} + +type SystemNotificationShellCompleted struct { + // Human-readable description of the command + Description *string `json:"description,omitempty"` + // Exit code of the shell command, if available + ExitCode *float64 `json:"exitCode,omitempty"` + // Unique identifier of the shell session + ShellID string `json:"shellId"` +} + +func (SystemNotificationShellCompleted) systemNotification() {} +func (SystemNotificationShellCompleted) Type() SystemNotificationType { + return SystemNotificationTypeShellCompleted +} + +type SystemNotificationShellDetachedCompleted struct { + // Human-readable description of the command + Description *string `json:"description,omitempty"` + // Unique identifier of the detached shell session + ShellID string `json:"shellId"` +} + +func (SystemNotificationShellDetachedCompleted) systemNotification() {} +func (SystemNotificationShellDetachedCompleted) Type() SystemNotificationType { + return SystemNotificationTypeShellDetachedCompleted +} + +// A content block within a tool result, which may be text, terminal output, image, audio, or a resource +type ToolExecutionCompleteContent interface { + toolExecutionCompleteContent() + Type() ToolExecutionCompleteContentType +} + +type RawToolExecutionCompleteContent struct { + Discriminator ToolExecutionCompleteContentType + Raw json.RawMessage +} + +func (RawToolExecutionCompleteContent) toolExecutionCompleteContent() {} +func (r RawToolExecutionCompleteContent) Type() ToolExecutionCompleteContentType { + return r.Discriminator +} + +// Audio content block with base64-encoded data +type ToolExecutionCompleteContentAudio struct { + // Base64-encoded audio data + Data string `json:"data"` + // MIME type of the audio (e.g., audio/wav, audio/mpeg) + MIMEType string `json:"mimeType"` +} + +func (ToolExecutionCompleteContentAudio) toolExecutionCompleteContent() {} +func (ToolExecutionCompleteContentAudio) Type() ToolExecutionCompleteContentType { + return ToolExecutionCompleteContentTypeAudio +} + +// Image content block with base64-encoded data +type ToolExecutionCompleteContentImage struct { + // Base64-encoded image data + Data string `json:"data"` + // MIME type of the image (e.g., image/png, image/jpeg) + MIMEType string `json:"mimeType"` +} + +func (ToolExecutionCompleteContentImage) toolExecutionCompleteContent() {} +func (ToolExecutionCompleteContentImage) Type() ToolExecutionCompleteContentType { + return ToolExecutionCompleteContentTypeImage +} + +// Embedded resource content block with inline text or binary data +type ToolExecutionCompleteContentResource struct { + // The embedded resource contents, either text or base64-encoded binary + Resource ToolExecutionCompleteContentResourceDetails `json:"resource"` +} + +func (ToolExecutionCompleteContentResource) toolExecutionCompleteContent() {} +func (ToolExecutionCompleteContentResource) Type() ToolExecutionCompleteContentType { + return ToolExecutionCompleteContentTypeResource +} + +// Resource link content block referencing an external resource +type ToolExecutionCompleteContentResourceLink struct { + // Human-readable description of the resource + Description *string `json:"description,omitempty"` + // Icons associated with this resource + Icons []ToolExecutionCompleteContentResourceLinkIcon `json:"icons,omitempty"` + // MIME type of the resource content + MIMEType *string `json:"mimeType,omitempty"` + // Resource name identifier + Name string `json:"name"` + // Size of the resource in bytes + Size *float64 `json:"size,omitempty"` + // Human-readable display title for the resource + Title *string `json:"title,omitempty"` + // URI identifying the resource + URI string `json:"uri"` +} + +func (ToolExecutionCompleteContentResourceLink) toolExecutionCompleteContent() {} +func (ToolExecutionCompleteContentResourceLink) Type() ToolExecutionCompleteContentType { + return ToolExecutionCompleteContentTypeResourceLink +} + +// Terminal/shell output content block with optional exit code and working directory +type ToolExecutionCompleteContentTerminal struct { + // Working directory where the command was executed + Cwd *string `json:"cwd,omitempty"` + // Process exit code, if the command has completed + ExitCode *float64 `json:"exitCode,omitempty"` + // Terminal/shell output text + Text string `json:"text"` +} + +func (ToolExecutionCompleteContentTerminal) toolExecutionCompleteContent() {} +func (ToolExecutionCompleteContentTerminal) Type() ToolExecutionCompleteContentType { + return ToolExecutionCompleteContentTypeTerminal +} + +// Plain text content block +type ToolExecutionCompleteContentText struct { + // The text content + Text string `json:"text"` +} + +func (ToolExecutionCompleteContentText) toolExecutionCompleteContent() {} +func (ToolExecutionCompleteContentText) Type() ToolExecutionCompleteContentType { + return ToolExecutionCompleteContentTypeText +} + +// The embedded resource contents, either text or base64-encoded binary +type ToolExecutionCompleteContentResourceDetails struct { + EmbeddedBlobResourceContents *EmbeddedBlobResourceContents + EmbeddedTextResourceContents *EmbeddedTextResourceContents +} + +// Icon image for a resource +type ToolExecutionCompleteContentResourceLinkIcon struct { + // MIME type of the icon image + MIMEType *string `json:"mimeType,omitempty"` + // Available icon sizes (e.g., ['16x16', '32x32']) + Sizes []string `json:"sizes,omitempty"` + // URL or path to the icon image + Src string `json:"src"` + // Theme variant this icon is intended for + Theme *ToolExecutionCompleteContentResourceLinkIconTheme `json:"theme,omitempty"` +} + +// Error details when the tool execution failed +type ToolExecutionCompleteError struct { + // Machine-readable error code + Code *string `json:"code,omitempty"` + // Human-readable error message + Message string `json:"message"` +} + +// Tool execution result on success +type ToolExecutionCompleteResult struct { + // Concise tool result text sent to the LLM for chat completion, potentially truncated for token efficiency + Content string `json:"content"` + // Structured content blocks (text, images, audio, resources) returned by the tool in their native format + Contents []ToolExecutionCompleteContent `json:"contents,omitempty"` + // Full detailed tool result for UI/timeline display, preserving complete content such as diffs. Falls back to content when absent. + DetailedContent *string `json:"detailedContent,omitempty"` +} + +// A user message attachment — a file, directory, code selection, blob, or GitHub reference +type UserMessageAttachment interface { + userMessageAttachment() + Type() UserMessageAttachmentType +} + +type RawUserMessageAttachment struct { + Discriminator UserMessageAttachmentType + Raw json.RawMessage +} + +func (RawUserMessageAttachment) userMessageAttachment() {} +func (r RawUserMessageAttachment) Type() UserMessageAttachmentType { + return r.Discriminator +} + +// Blob attachment with inline base64-encoded data +type UserMessageAttachmentBlob struct { + // Base64-encoded content + Data string `json:"data"` + // User-facing display name for the attachment + DisplayName *string `json:"displayName,omitempty"` + // MIME type of the inline data + MIMEType string `json:"mimeType"` +} + +func (UserMessageAttachmentBlob) userMessageAttachment() {} +func (UserMessageAttachmentBlob) Type() UserMessageAttachmentType { + return UserMessageAttachmentTypeBlob +} + +// Directory attachment +type UserMessageAttachmentDirectory struct { + // User-facing display name for the attachment + DisplayName string `json:"displayName"` + // Absolute directory path + Path string `json:"path"` +} + +func (UserMessageAttachmentDirectory) userMessageAttachment() {} +func (UserMessageAttachmentDirectory) Type() UserMessageAttachmentType { + return UserMessageAttachmentTypeDirectory +} + +// File attachment +type UserMessageAttachmentFile struct { + // User-facing display name for the attachment + DisplayName string `json:"displayName"` + // Optional line range to scope the attachment to a specific section of the file + LineRange *UserMessageAttachmentFileLineRange `json:"lineRange,omitempty"` + // Absolute file path + Path string `json:"path"` +} + +func (UserMessageAttachmentFile) userMessageAttachment() {} +func (UserMessageAttachmentFile) Type() UserMessageAttachmentType { + return UserMessageAttachmentTypeFile +} + +// GitHub issue, pull request, or discussion reference +type UserMessageAttachmentGithubReference struct { + // Issue, pull request, or discussion number + Number float64 `json:"number"` + // Type of GitHub reference + ReferenceType UserMessageAttachmentGithubReferenceType `json:"referenceType"` + // Current state of the referenced item (e.g., open, closed, merged) + State string `json:"state"` + // Title of the referenced item + Title string `json:"title"` + // URL to the referenced item on GitHub + URL string `json:"url"` +} + +func (UserMessageAttachmentGithubReference) userMessageAttachment() {} +func (UserMessageAttachmentGithubReference) Type() UserMessageAttachmentType { + return UserMessageAttachmentTypeGithubReference +} + +// Code selection attachment from an editor +type UserMessageAttachmentSelection struct { + // User-facing display name for the selection + DisplayName string `json:"displayName"` + // Absolute path to the file containing the selection + FilePath string `json:"filePath"` + // Position range of the selection within the file + Selection UserMessageAttachmentSelectionDetails `json:"selection"` + // The selected text content + Text string `json:"text"` +} + +func (UserMessageAttachmentSelection) userMessageAttachment() {} +func (UserMessageAttachmentSelection) Type() UserMessageAttachmentType { + return UserMessageAttachmentTypeSelection +} + +// Optional line range to scope the attachment to a specific section of the file +type UserMessageAttachmentFileLineRange struct { + // End line number (1-based, inclusive) + End float64 `json:"end"` + // Start line number (1-based) + Start float64 `json:"start"` +} + +// Position range of the selection within the file +type UserMessageAttachmentSelectionDetails struct { + // End position of the selection + End UserMessageAttachmentSelectionDetailsEnd `json:"end"` + // Start position of the selection + Start UserMessageAttachmentSelectionDetailsStart `json:"start"` +} + +// End position of the selection +type UserMessageAttachmentSelectionDetailsEnd struct { + // End character offset within the line (0-based) + Character float64 `json:"character"` + // End line number (0-based) + Line float64 `json:"line"` +} + +// Start position of the selection +type UserMessageAttachmentSelectionDetailsStart struct { + // Start character offset within the line (0-based) + Character float64 `json:"character"` + // Start line number (0-based) + Line float64 `json:"line"` +} + +// The approval to add as a session-scoped rule +type UserToolSessionApproval interface { + userToolSessionApproval() + Kind() UserToolSessionApprovalKind +} + +type RawUserToolSessionApproval struct { + Discriminator UserToolSessionApprovalKind + Raw json.RawMessage +} + +func (RawUserToolSessionApproval) userToolSessionApproval() {} +func (r RawUserToolSessionApproval) Kind() UserToolSessionApprovalKind { + return r.Discriminator +} + +type UserToolSessionApprovalCommands struct { + // Command identifiers approved by the user + CommandIdentifiers []string `json:"commandIdentifiers"` +} + +func (UserToolSessionApprovalCommands) userToolSessionApproval() {} +func (UserToolSessionApprovalCommands) Kind() UserToolSessionApprovalKind { + return UserToolSessionApprovalKindCommands +} + +type UserToolSessionApprovalCustomTool struct { + // Custom tool name + ToolName string `json:"toolName"` +} + +func (UserToolSessionApprovalCustomTool) userToolSessionApproval() {} +func (UserToolSessionApprovalCustomTool) Kind() UserToolSessionApprovalKind { + return UserToolSessionApprovalKindCustomTool +} + +type UserToolSessionApprovalExtensionManagement struct { + // Optional operation identifier + Operation *string `json:"operation,omitempty"` +} + +func (UserToolSessionApprovalExtensionManagement) userToolSessionApproval() {} +func (UserToolSessionApprovalExtensionManagement) Kind() UserToolSessionApprovalKind { + return UserToolSessionApprovalKindExtensionManagement +} + +type UserToolSessionApprovalExtensionPermissionAccess struct { + // Extension name + ExtensionName string `json:"extensionName"` +} + +func (UserToolSessionApprovalExtensionPermissionAccess) userToolSessionApproval() {} +func (UserToolSessionApprovalExtensionPermissionAccess) Kind() UserToolSessionApprovalKind { + return UserToolSessionApprovalKindExtensionPermissionAccess +} + +type UserToolSessionApprovalMcp struct { + // MCP server name + ServerName string `json:"serverName"` + // Optional MCP tool name, or null for all tools on the server + ToolName *string `json:"toolName"` +} + +func (UserToolSessionApprovalMcp) userToolSessionApproval() {} +func (UserToolSessionApprovalMcp) Kind() UserToolSessionApprovalKind { + return UserToolSessionApprovalKindMcp +} + +type UserToolSessionApprovalMemory struct { +} + +func (UserToolSessionApprovalMemory) userToolSessionApproval() {} +func (UserToolSessionApprovalMemory) Kind() UserToolSessionApprovalKind { + return UserToolSessionApprovalKindMemory +} + +type UserToolSessionApprovalRead struct { +} + +func (UserToolSessionApprovalRead) userToolSessionApproval() {} +func (UserToolSessionApprovalRead) Kind() UserToolSessionApprovalKind { + return UserToolSessionApprovalKindRead +} + +type UserToolSessionApprovalWrite struct { +} + +func (UserToolSessionApprovalWrite) userToolSessionApproval() {} +func (UserToolSessionApprovalWrite) Kind() UserToolSessionApprovalKind { + return UserToolSessionApprovalKindWrite +} + +// Working directory and git context at session start +type WorkingDirectoryContext struct { + // Base commit of current git branch at session start time + BaseCommit *string `json:"baseCommit,omitempty"` + // Current git branch name + Branch *string `json:"branch,omitempty"` + // Current working directory path + Cwd string `json:"cwd"` + // Root directory of the git repository, resolved via git rev-parse + GitRoot *string `json:"gitRoot,omitempty"` + // Head commit of current git branch at session start time + HeadCommit *string `json:"headCommit,omitempty"` + // Hosting platform type of the repository (github or ado) + HostType *WorkingDirectoryContextHostType `json:"hostType,omitempty"` + // Repository identifier derived from the git remote URL ("owner/name" for GitHub, "org/project/repo" for Azure DevOps) + Repository *string `json:"repository,omitempty"` + // Raw host string from the git remote URL (e.g. "github.com", "mycompany.ghe.com", "dev.azure.com") + RepositoryHost *string `json:"repositoryHost,omitempty"` +} + +// Finite reason code describing why the current turn was aborted +type AbortReason string + +const ( + AbortReasonRemoteCommand AbortReason = "remote_command" + AbortReasonUserAbort AbortReason = "user_abort" + AbortReasonUserInitiated AbortReason = "user_initiated" +) + +// Tool call type: "function" for standard tool calls, "custom" for grammar-based tool calls. Defaults to "function" when absent. +type AssistantMessageToolRequestType string + +const ( + AssistantMessageToolRequestTypeCustom AssistantMessageToolRequestType = "custom" + AssistantMessageToolRequestTypeFunction AssistantMessageToolRequestType = "function" +) + +// API endpoint used for this model call, matching CAPI supported_endpoints vocabulary +type AssistantUsageAPIEndpoint string + +const ( + AssistantUsageAPIEndpointChatCompletions AssistantUsageAPIEndpoint = "/chat/completions" + AssistantUsageAPIEndpointResponses AssistantUsageAPIEndpoint = "/responses" + AssistantUsageAPIEndpointV1Messages AssistantUsageAPIEndpoint = "/v1/messages" + AssistantUsageAPIEndpointWsResponses AssistantUsageAPIEndpoint = "ws:/responses" +) + +// The user action: "accept" (submitted form), "decline" (explicitly refused), or "cancel" (dismissed) +type ElicitationCompletedAction string + +const ( + ElicitationCompletedActionAccept ElicitationCompletedAction = "accept" + ElicitationCompletedActionCancel ElicitationCompletedAction = "cancel" + ElicitationCompletedActionDecline ElicitationCompletedAction = "decline" +) + +// Elicitation mode; "form" for structured input, "url" for browser-based. Defaults to "form" when absent. +type ElicitationRequestedMode string + +const ( + ElicitationRequestedModeForm ElicitationRequestedMode = "form" + ElicitationRequestedModeURL ElicitationRequestedMode = "url" +) + +// Schema type indicator (always 'object') +type ElicitationRequestedSchemaType string + +const ( + ElicitationRequestedSchemaTypeObject ElicitationRequestedSchemaType = "object" +) + +// Discovery source +type ExtensionsLoadedExtensionSource string + +const ( + ExtensionsLoadedExtensionSourceProject ExtensionsLoadedExtensionSource = "project" + ExtensionsLoadedExtensionSourceUser ExtensionsLoadedExtensionSource = "user" +) + +// Current status: running, disabled, failed, or starting +type ExtensionsLoadedExtensionStatus string + +const ( + ExtensionsLoadedExtensionStatusDisabled ExtensionsLoadedExtensionStatus = "disabled" + ExtensionsLoadedExtensionStatusFailed ExtensionsLoadedExtensionStatus = "failed" + ExtensionsLoadedExtensionStatusRunning ExtensionsLoadedExtensionStatus = "running" + ExtensionsLoadedExtensionStatusStarting ExtensionsLoadedExtensionStatus = "starting" +) + +// Origin type of the session being handed off +type HandoffSourceType string + +const ( + HandoffSourceTypeLocal HandoffSourceType = "local" + HandoffSourceTypeRemote HandoffSourceType = "remote" +) + +// Optional non-default OAuth grant type. When set to 'client_credentials', the OAuth flow runs headlessly using the client_id + keychain-stored secret (no browser, no callback server). +type McpOauthRequiredStaticClientConfigGrantType string + +const ( + McpOauthRequiredStaticClientConfigGrantTypeClientCredentials McpOauthRequiredStaticClientConfigGrantType = "client_credentials" +) + +// Connection status: connected, failed, needs-auth, pending, disabled, or not_configured +type McpServersLoadedServerStatus string + +const ( + McpServersLoadedServerStatusConnected McpServersLoadedServerStatus = "connected" + McpServersLoadedServerStatusDisabled McpServersLoadedServerStatus = "disabled" + McpServersLoadedServerStatusFailed McpServersLoadedServerStatus = "failed" + McpServersLoadedServerStatusNeedsAuth McpServersLoadedServerStatus = "needs-auth" + McpServersLoadedServerStatusNotConfigured McpServersLoadedServerStatus = "not_configured" + McpServersLoadedServerStatusPending McpServersLoadedServerStatus = "pending" +) + +// New connection status: connected, failed, needs-auth, pending, disabled, or not_configured +type McpServerStatusChangedStatus string + +const ( + McpServerStatusChangedStatusConnected McpServerStatusChangedStatus = "connected" + McpServerStatusChangedStatusDisabled McpServerStatusChangedStatus = "disabled" + McpServerStatusChangedStatusFailed McpServerStatusChangedStatus = "failed" + McpServerStatusChangedStatusNeedsAuth McpServerStatusChangedStatus = "needs-auth" + McpServerStatusChangedStatusNotConfigured McpServerStatusChangedStatus = "not_configured" + McpServerStatusChangedStatusPending McpServerStatusChangedStatus = "pending" +) + +// Where the failed model call originated +type ModelCallFailureSource string + +const ( + ModelCallFailureSourceMcpSampling ModelCallFailureSource = "mcp_sampling" + ModelCallFailureSourceSubagent ModelCallFailureSource = "subagent" + ModelCallFailureSourceTopLevel ModelCallFailureSource = "top_level" +) + +// Kind discriminator for PermissionPromptRequest. +type PermissionPromptRequestKind string + +const ( + PermissionPromptRequestKindCommands PermissionPromptRequestKind = "commands" + PermissionPromptRequestKindCustomTool PermissionPromptRequestKind = "custom-tool" + PermissionPromptRequestKindExtensionManagement PermissionPromptRequestKind = "extension-management" + PermissionPromptRequestKindExtensionPermissionAccess PermissionPromptRequestKind = "extension-permission-access" + PermissionPromptRequestKindHook PermissionPromptRequestKind = "hook" + PermissionPromptRequestKindMcp PermissionPromptRequestKind = "mcp" + PermissionPromptRequestKindMemory PermissionPromptRequestKind = "memory" + PermissionPromptRequestKindPath PermissionPromptRequestKind = "path" + PermissionPromptRequestKindRead PermissionPromptRequestKind = "read" + PermissionPromptRequestKindURL PermissionPromptRequestKind = "url" + PermissionPromptRequestKindWrite PermissionPromptRequestKind = "write" +) + +// Whether this is a store or vote memory operation +type PermissionPromptRequestMemoryAction string + +const ( + PermissionPromptRequestMemoryActionStore PermissionPromptRequestMemoryAction = "store" + PermissionPromptRequestMemoryActionVote PermissionPromptRequestMemoryAction = "vote" +) + +// Vote direction (vote only) +type PermissionPromptRequestMemoryDirection string + +const ( + PermissionPromptRequestMemoryDirectionDownvote PermissionPromptRequestMemoryDirection = "downvote" + PermissionPromptRequestMemoryDirectionUpvote PermissionPromptRequestMemoryDirection = "upvote" +) + +// Underlying permission kind that needs path approval +type PermissionPromptRequestPathAccessKind string + +const ( + PermissionPromptRequestPathAccessKindRead PermissionPromptRequestPathAccessKind = "read" + PermissionPromptRequestPathAccessKindShell PermissionPromptRequestPathAccessKind = "shell" + PermissionPromptRequestPathAccessKindWrite PermissionPromptRequestPathAccessKind = "write" +) + +// Kind discriminator for PermissionRequest. +type PermissionRequestKind string + +const ( + PermissionRequestKindCustomTool PermissionRequestKind = "custom-tool" + PermissionRequestKindExtensionManagement PermissionRequestKind = "extension-management" + PermissionRequestKindExtensionPermissionAccess PermissionRequestKind = "extension-permission-access" + PermissionRequestKindHook PermissionRequestKind = "hook" + PermissionRequestKindMcp PermissionRequestKind = "mcp" + PermissionRequestKindMemory PermissionRequestKind = "memory" + PermissionRequestKindRead PermissionRequestKind = "read" + PermissionRequestKindShell PermissionRequestKind = "shell" + PermissionRequestKindURL PermissionRequestKind = "url" + PermissionRequestKindWrite PermissionRequestKind = "write" +) + +// Whether this is a store or vote memory operation +type PermissionRequestMemoryAction string + +const ( + PermissionRequestMemoryActionStore PermissionRequestMemoryAction = "store" + PermissionRequestMemoryActionVote PermissionRequestMemoryAction = "vote" +) + +// Vote direction (vote only) +type PermissionRequestMemoryDirection string + +const ( + PermissionRequestMemoryDirectionDownvote PermissionRequestMemoryDirection = "downvote" + PermissionRequestMemoryDirectionUpvote PermissionRequestMemoryDirection = "upvote" +) + +// Kind discriminator for PermissionResult. +type PermissionResultKind string + +const ( + PermissionResultKindApproved PermissionResultKind = "approved" + PermissionResultKindApprovedForLocation PermissionResultKind = "approved-for-location" + PermissionResultKindApprovedForSession PermissionResultKind = "approved-for-session" + PermissionResultKindCancelled PermissionResultKind = "cancelled" + PermissionResultKindDeniedByContentExclusionPolicy PermissionResultKind = "denied-by-content-exclusion-policy" + PermissionResultKindDeniedByPermissionRequestHook PermissionResultKind = "denied-by-permission-request-hook" + PermissionResultKindDeniedByRules PermissionResultKind = "denied-by-rules" + PermissionResultKindDeniedInteractivelyByUser PermissionResultKind = "denied-interactively-by-user" + PermissionResultKindDeniedNoApprovalRuleAndCouldNotRequestFromUser PermissionResultKind = "denied-no-approval-rule-and-could-not-request-from-user" +) + +// The type of operation performed on the plan file +type PlanChangedOperation string + +const ( + PlanChangedOperationCreate PlanChangedOperation = "create" + PlanChangedOperationDelete PlanChangedOperation = "delete" + PlanChangedOperationUpdate PlanChangedOperation = "update" +) + +// Whether the session ended normally ("routine") or due to a crash/fatal error ("error") +type ShutdownType string + +const ( + ShutdownTypeError ShutdownType = "error" + ShutdownTypeRoutine ShutdownType = "routine" +) + +// Message role: "system" for system prompts, "developer" for developer-injected instructions +type SystemMessageRole string + +const ( + SystemMessageRoleDeveloper SystemMessageRole = "developer" + SystemMessageRoleSystem SystemMessageRole = "system" +) + +// Whether the agent completed successfully or failed +type SystemNotificationAgentCompletedStatus string + +const ( + SystemNotificationAgentCompletedStatusCompleted SystemNotificationAgentCompletedStatus = "completed" + SystemNotificationAgentCompletedStatusFailed SystemNotificationAgentCompletedStatus = "failed" +) + +// Type discriminator for SystemNotification. +type SystemNotificationType string + +const ( + SystemNotificationTypeAgentCompleted SystemNotificationType = "agent_completed" + SystemNotificationTypeAgentIdle SystemNotificationType = "agent_idle" + SystemNotificationTypeInstructionDiscovered SystemNotificationType = "instruction_discovered" + SystemNotificationTypeNewInboxMessage SystemNotificationType = "new_inbox_message" + SystemNotificationTypeShellCompleted SystemNotificationType = "shell_completed" + SystemNotificationTypeShellDetachedCompleted SystemNotificationType = "shell_detached_completed" +) + +// Theme variant this icon is intended for +type ToolExecutionCompleteContentResourceLinkIconTheme string + +const ( + ToolExecutionCompleteContentResourceLinkIconThemeDark ToolExecutionCompleteContentResourceLinkIconTheme = "dark" + ToolExecutionCompleteContentResourceLinkIconThemeLight ToolExecutionCompleteContentResourceLinkIconTheme = "light" +) + +// Type discriminator for ToolExecutionCompleteContent. +type ToolExecutionCompleteContentType string + +const ( + ToolExecutionCompleteContentTypeAudio ToolExecutionCompleteContentType = "audio" + ToolExecutionCompleteContentTypeImage ToolExecutionCompleteContentType = "image" + ToolExecutionCompleteContentTypeResource ToolExecutionCompleteContentType = "resource" + ToolExecutionCompleteContentTypeResourceLink ToolExecutionCompleteContentType = "resource_link" + ToolExecutionCompleteContentTypeTerminal ToolExecutionCompleteContentType = "terminal" + ToolExecutionCompleteContentTypeText ToolExecutionCompleteContentType = "text" +) + +// The agent mode that was active when this message was sent +type UserMessageAgentMode string + +const ( + UserMessageAgentModeAutopilot UserMessageAgentMode = "autopilot" + UserMessageAgentModeInteractive UserMessageAgentMode = "interactive" + UserMessageAgentModePlan UserMessageAgentMode = "plan" + UserMessageAgentModeShell UserMessageAgentMode = "shell" +) + +// Type of GitHub reference +type UserMessageAttachmentGithubReferenceType string + +const ( + UserMessageAttachmentGithubReferenceTypeDiscussion UserMessageAttachmentGithubReferenceType = "discussion" + UserMessageAttachmentGithubReferenceTypeIssue UserMessageAttachmentGithubReferenceType = "issue" + UserMessageAttachmentGithubReferenceTypePr UserMessageAttachmentGithubReferenceType = "pr" +) + +// Type discriminator for UserMessageAttachment. +type UserMessageAttachmentType string + +const ( + UserMessageAttachmentTypeBlob UserMessageAttachmentType = "blob" + UserMessageAttachmentTypeDirectory UserMessageAttachmentType = "directory" + UserMessageAttachmentTypeFile UserMessageAttachmentType = "file" + UserMessageAttachmentTypeGithubReference UserMessageAttachmentType = "github_reference" + UserMessageAttachmentTypeSelection UserMessageAttachmentType = "selection" +) + +// Kind discriminator for UserToolSessionApproval. +type UserToolSessionApprovalKind string + +const ( + UserToolSessionApprovalKindCommands UserToolSessionApprovalKind = "commands" + UserToolSessionApprovalKindCustomTool UserToolSessionApprovalKind = "custom-tool" + UserToolSessionApprovalKindExtensionManagement UserToolSessionApprovalKind = "extension-management" + UserToolSessionApprovalKindExtensionPermissionAccess UserToolSessionApprovalKind = "extension-permission-access" + UserToolSessionApprovalKindMcp UserToolSessionApprovalKind = "mcp" + UserToolSessionApprovalKindMemory UserToolSessionApprovalKind = "memory" + UserToolSessionApprovalKindRead UserToolSessionApprovalKind = "read" + UserToolSessionApprovalKindWrite UserToolSessionApprovalKind = "write" +) + +// Hosting platform type of the repository (github or ado) +type WorkingDirectoryContextHostType string + +const ( + WorkingDirectoryContextHostTypeAdo WorkingDirectoryContextHostType = "ado" + WorkingDirectoryContextHostTypeGithub WorkingDirectoryContextHostType = "github" +) + +// Whether the file was newly created or updated +type WorkspaceFileChangedOperation string + +const ( + WorkspaceFileChangedOperationCreate WorkspaceFileChangedOperation = "create" + WorkspaceFileChangedOperationUpdate WorkspaceFileChangedOperation = "update" +) + +// Type aliases for convenience. +type ( + Attachment = UserMessageAttachment + AttachmentType = UserMessageAttachmentType + PermissionRequestCommand = PermissionRequestShellCommand + PossibleURL = PermissionRequestShellPossibleURL +) + +// Constant aliases for convenience. +const ( + AttachmentTypeBlob = UserMessageAttachmentTypeBlob + AttachmentTypeDirectory = UserMessageAttachmentTypeDirectory + AttachmentTypeFile = UserMessageAttachmentTypeFile + AttachmentTypeGithubReference = UserMessageAttachmentTypeGithubReference + AttachmentTypeSelection = UserMessageAttachmentTypeSelection +) diff --git a/go/session_event_serialization_test.go b/go/session_event_serialization_test.go index b64a79975..5f8855336 100644 --- a/go/session_event_serialization_test.go +++ b/go/session_event_serialization_test.go @@ -3,8 +3,17 @@ package copilot import ( "encoding/json" "testing" + + "github.com/github/copilot-sdk/go/rpc" ) +var _ rpc.SessionEvent = SessionEvent{} +var _ SessionEvent = rpc.SessionEvent{} +var _ rpc.SessionEventData = (*UserMessageData)(nil) +var _ SessionEventData = (*rpc.UserMessageData)(nil) +var _ rpc.EmbeddedTextResourceContents = EmbeddedTextResourceContents{} +var _ EmbeddedTextResourceContents = rpc.EmbeddedTextResourceContents{} + func TestSessionEventAgentIDRoundTripsKnownEvent(t *testing.T) { var event SessionEvent if err := json.Unmarshal([]byte(`{ diff --git a/go/zsession_events.go b/go/zsession_events.go index fb4b852e2..828095122 100644 --- a/go/zsession_events.go +++ b/go/zsession_events.go @@ -3,3089 +3,458 @@ package copilot -import ( - "encoding/json" - "time" -) - -// SessionEventData is the interface implemented by all per-event data types. -type SessionEventData interface { - sessionEventData() - Type() SessionEventType -} - -// SessionEvent represents a single session event with a typed data payload. -type SessionEvent struct { - // Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. - AgentID *string `json:"agentId,omitempty"` - // Typed event payload. Use a type switch to access per-event fields. - Data SessionEventData `json:"-"` - // When true, the event is transient and not persisted to the session event log on disk - Ephemeral *bool `json:"ephemeral,omitempty"` - // Unique event identifier (UUID v4), generated when the event is emitted - ID string `json:"id"` - // ID of the chronologically preceding event in the session, forming a linked chain. Null for the first event. - ParentID *string `json:"parentId"` - // ISO 8601 timestamp when the event was created - Timestamp time.Time `json:"timestamp"` -} - -// Type returns the event type discriminator derived from Data. -func (e SessionEvent) Type() SessionEventType { - if e.Data == nil { - return "" - } - return e.Data.Type() -} - -// RawSessionEventData holds unparsed JSON data for unrecognized event types. -type RawSessionEventData struct { - EventType SessionEventType - Raw json.RawMessage -} - -func (RawSessionEventData) sessionEventData() {} -func (r RawSessionEventData) Type() SessionEventType { - return r.EventType -} - -// SessionEventType identifies the kind of session event. -type SessionEventType string - -const ( - SessionEventTypeAbort SessionEventType = "abort" - SessionEventTypeAssistantIntent SessionEventType = "assistant.intent" - SessionEventTypeAssistantMessage SessionEventType = "assistant.message" - SessionEventTypeAssistantMessageDelta SessionEventType = "assistant.message_delta" - SessionEventTypeAssistantMessageStart SessionEventType = "assistant.message_start" - SessionEventTypeAssistantReasoning SessionEventType = "assistant.reasoning" - SessionEventTypeAssistantReasoningDelta SessionEventType = "assistant.reasoning_delta" - SessionEventTypeAssistantStreamingDelta SessionEventType = "assistant.streaming_delta" - SessionEventTypeAssistantTurnEnd SessionEventType = "assistant.turn_end" - SessionEventTypeAssistantTurnStart SessionEventType = "assistant.turn_start" - SessionEventTypeAssistantUsage SessionEventType = "assistant.usage" - SessionEventTypeAutoModeSwitchCompleted SessionEventType = "auto_mode_switch.completed" - SessionEventTypeAutoModeSwitchRequested SessionEventType = "auto_mode_switch.requested" - SessionEventTypeCapabilitiesChanged SessionEventType = "capabilities.changed" - SessionEventTypeCommandCompleted SessionEventType = "command.completed" - SessionEventTypeCommandExecute SessionEventType = "command.execute" - SessionEventTypeCommandQueued SessionEventType = "command.queued" - SessionEventTypeCommandsChanged SessionEventType = "commands.changed" - SessionEventTypeElicitationCompleted SessionEventType = "elicitation.completed" - SessionEventTypeElicitationRequested SessionEventType = "elicitation.requested" - SessionEventTypeExitPlanModeCompleted SessionEventType = "exit_plan_mode.completed" - SessionEventTypeExitPlanModeRequested SessionEventType = "exit_plan_mode.requested" - SessionEventTypeExternalToolCompleted SessionEventType = "external_tool.completed" - SessionEventTypeExternalToolRequested SessionEventType = "external_tool.requested" - SessionEventTypeHookEnd SessionEventType = "hook.end" - SessionEventTypeHookStart SessionEventType = "hook.start" - SessionEventTypeMcpOauthCompleted SessionEventType = "mcp.oauth_completed" - SessionEventTypeMcpOauthRequired SessionEventType = "mcp.oauth_required" - SessionEventTypeModelCallFailure SessionEventType = "model.call_failure" - SessionEventTypePendingMessagesModified SessionEventType = "pending_messages.modified" - SessionEventTypePermissionCompleted SessionEventType = "permission.completed" - SessionEventTypePermissionRequested SessionEventType = "permission.requested" - SessionEventTypeSamplingCompleted SessionEventType = "sampling.completed" - SessionEventTypeSamplingRequested SessionEventType = "sampling.requested" - SessionEventTypeSessionBackgroundTasksChanged SessionEventType = "session.background_tasks_changed" - SessionEventTypeSessionCompactionComplete SessionEventType = "session.compaction_complete" - SessionEventTypeSessionCompactionStart SessionEventType = "session.compaction_start" - SessionEventTypeSessionContextChanged SessionEventType = "session.context_changed" - SessionEventTypeSessionCustomAgentsUpdated SessionEventType = "session.custom_agents_updated" - SessionEventTypeSessionError SessionEventType = "session.error" - SessionEventTypeSessionExtensionsLoaded SessionEventType = "session.extensions_loaded" - SessionEventTypeSessionHandoff SessionEventType = "session.handoff" - SessionEventTypeSessionIdle SessionEventType = "session.idle" - SessionEventTypeSessionInfo SessionEventType = "session.info" - SessionEventTypeSessionMcpServersLoaded SessionEventType = "session.mcp_servers_loaded" - SessionEventTypeSessionMcpServerStatusChanged SessionEventType = "session.mcp_server_status_changed" - SessionEventTypeSessionModeChanged SessionEventType = "session.mode_changed" - SessionEventTypeSessionModelChange SessionEventType = "session.model_change" - SessionEventTypeSessionPlanChanged SessionEventType = "session.plan_changed" - SessionEventTypeSessionRemoteSteerableChanged SessionEventType = "session.remote_steerable_changed" - SessionEventTypeSessionResume SessionEventType = "session.resume" - SessionEventTypeSessionScheduleCancelled SessionEventType = "session.schedule_cancelled" - SessionEventTypeSessionScheduleCreated SessionEventType = "session.schedule_created" - SessionEventTypeSessionShutdown SessionEventType = "session.shutdown" - SessionEventTypeSessionSkillsLoaded SessionEventType = "session.skills_loaded" - SessionEventTypeSessionSnapshotRewind SessionEventType = "session.snapshot_rewind" - SessionEventTypeSessionStart SessionEventType = "session.start" - SessionEventTypeSessionTaskComplete SessionEventType = "session.task_complete" - SessionEventTypeSessionTitleChanged SessionEventType = "session.title_changed" - SessionEventTypeSessionToolsUpdated SessionEventType = "session.tools_updated" - SessionEventTypeSessionTruncation SessionEventType = "session.truncation" - SessionEventTypeSessionUsageInfo SessionEventType = "session.usage_info" - SessionEventTypeSessionWarning SessionEventType = "session.warning" - SessionEventTypeSessionWorkspaceFileChanged SessionEventType = "session.workspace_file_changed" - SessionEventTypeSkillInvoked SessionEventType = "skill.invoked" - SessionEventTypeSubagentCompleted SessionEventType = "subagent.completed" - SessionEventTypeSubagentDeselected SessionEventType = "subagent.deselected" - SessionEventTypeSubagentFailed SessionEventType = "subagent.failed" - SessionEventTypeSubagentSelected SessionEventType = "subagent.selected" - SessionEventTypeSubagentStarted SessionEventType = "subagent.started" - SessionEventTypeSystemMessage SessionEventType = "system.message" - SessionEventTypeSystemNotification SessionEventType = "system.notification" - SessionEventTypeToolExecutionComplete SessionEventType = "tool.execution_complete" - SessionEventTypeToolExecutionPartialResult SessionEventType = "tool.execution_partial_result" - SessionEventTypeToolExecutionProgress SessionEventType = "tool.execution_progress" - SessionEventTypeToolExecutionStart SessionEventType = "tool.execution_start" - SessionEventTypeToolUserRequested SessionEventType = "tool.user_requested" - SessionEventTypeUserInputCompleted SessionEventType = "user_input.completed" - SessionEventTypeUserInputRequested SessionEventType = "user_input.requested" - SessionEventTypeUserMessage SessionEventType = "user.message" -) - -// Agent intent description for current activity or plan -type AssistantIntentData struct { - // Short description of what the agent is currently doing or planning to do - Intent string `json:"intent"` -} - -func (*AssistantIntentData) sessionEventData() {} -func (*AssistantIntentData) Type() SessionEventType { return SessionEventTypeAssistantIntent } - -// Agent mode change details including previous and new modes -type SessionModeChangedData struct { - // Agent mode after the change (e.g., "interactive", "plan", "autopilot") - NewMode string `json:"newMode"` - // Agent mode before the change (e.g., "interactive", "plan", "autopilot") - PreviousMode string `json:"previousMode"` -} - -func (*SessionModeChangedData) sessionEventData() {} -func (*SessionModeChangedData) Type() SessionEventType { return SessionEventTypeSessionModeChanged } - -// Assistant reasoning content for timeline display with complete thinking text -type AssistantReasoningData struct { - // The complete extended thinking text from the model - Content string `json:"content"` - // Unique identifier for this reasoning block - ReasoningID string `json:"reasoningId"` -} - -func (*AssistantReasoningData) sessionEventData() {} -func (*AssistantReasoningData) Type() SessionEventType { return SessionEventTypeAssistantReasoning } - -// Assistant response containing text content, optional tool requests, and interaction metadata -type AssistantMessageData struct { - // Raw Anthropic content array with advisor blocks (server_tool_use, advisor_tool_result) for verbatim round-tripping - AnthropicAdvisorBlocks []any `json:"anthropicAdvisorBlocks,omitempty"` - // Anthropic advisor model ID used for this response, for timeline display on replay - AnthropicAdvisorModel *string `json:"anthropicAdvisorModel,omitempty"` - // The assistant's text response content - Content string `json:"content"` - // Encrypted reasoning content from OpenAI models. Session-bound and stripped on resume. - EncryptedContent *string `json:"encryptedContent,omitempty"` - // CAPI interaction ID for correlating this message with upstream telemetry - InteractionID *string `json:"interactionId,omitempty"` - // Unique identifier for this assistant message - MessageID string `json:"messageId"` - // Model that produced this assistant message, if known - Model *string `json:"model,omitempty"` - // Actual output token count from the API response (completion_tokens), used for accurate token accounting - OutputTokens *float64 `json:"outputTokens,omitempty"` - // Tool call ID of the parent tool invocation when this event originates from a sub-agent - // Deprecated: ParentToolCallID is deprecated. - ParentToolCallID *string `json:"parentToolCallId,omitempty"` - // Generation phase for phased-output models (e.g., thinking vs. response phases) - Phase *string `json:"phase,omitempty"` - // Opaque/encrypted extended thinking data from Anthropic models. Session-bound and stripped on resume. - ReasoningOpaque *string `json:"reasoningOpaque,omitempty"` - // Readable reasoning text from the model's extended thinking - ReasoningText *string `json:"reasoningText,omitempty"` - // GitHub request tracing ID (x-github-request-id header) for correlating with server-side logs - RequestID *string `json:"requestId,omitempty"` - // Tool invocations requested by the assistant in this message - ToolRequests []AssistantMessageToolRequest `json:"toolRequests,omitempty"` - // Identifier for the agent loop turn that produced this message, matching the corresponding assistant.turn_start event - TurnID *string `json:"turnId,omitempty"` -} - -func (*AssistantMessageData) sessionEventData() {} -func (*AssistantMessageData) Type() SessionEventType { return SessionEventTypeAssistantMessage } - -// Auto mode switch completion notification -type AutoModeSwitchCompletedData struct { - // Request ID of the resolved request; clients should dismiss any UI for this request - RequestID string `json:"requestId"` - // The user's choice: 'yes', 'yes_always', or 'no' - Response string `json:"response"` -} - -func (*AutoModeSwitchCompletedData) sessionEventData() {} -func (*AutoModeSwitchCompletedData) Type() SessionEventType { - return SessionEventTypeAutoModeSwitchCompleted -} - -// Auto mode switch request notification requiring user approval -type AutoModeSwitchRequestedData struct { - // The rate limit error code that triggered this request - ErrorCode *string `json:"errorCode,omitempty"` - // Unique identifier for this request; used to respond via session.respondToAutoModeSwitch() - RequestID string `json:"requestId"` - // Seconds until the rate limit resets, when known. Lets clients render a humanized reset time alongside the prompt. - RetryAfterSeconds *float64 `json:"retryAfterSeconds,omitempty"` -} - -func (*AutoModeSwitchRequestedData) sessionEventData() {} -func (*AutoModeSwitchRequestedData) Type() SessionEventType { - return SessionEventTypeAutoModeSwitchRequested -} - -// Context window breakdown at the start of LLM-powered conversation compaction -type SessionCompactionStartData struct { - // Token count from non-system messages (user, assistant, tool) at compaction start - ConversationTokens *float64 `json:"conversationTokens,omitempty"` - // Token count from system message(s) at compaction start - SystemTokens *float64 `json:"systemTokens,omitempty"` - // Token count from tool definitions at compaction start - ToolDefinitionsTokens *float64 `json:"toolDefinitionsTokens,omitempty"` -} - -func (*SessionCompactionStartData) sessionEventData() {} -func (*SessionCompactionStartData) Type() SessionEventType { - return SessionEventTypeSessionCompactionStart -} - -// Conversation compaction results including success status, metrics, and optional error details -type SessionCompactionCompleteData struct { - // Checkpoint snapshot number created for recovery - CheckpointNumber *float64 `json:"checkpointNumber,omitempty"` - // File path where the checkpoint was stored - CheckpointPath *string `json:"checkpointPath,omitempty"` - // Token usage breakdown for the compaction LLM call (aligned with assistant.usage format) - CompactionTokensUsed *CompactionCompleteCompactionTokensUsed `json:"compactionTokensUsed,omitempty"` - // Token count from non-system messages (user, assistant, tool) after compaction - ConversationTokens *float64 `json:"conversationTokens,omitempty"` - // Error message if compaction failed - Error *string `json:"error,omitempty"` - // Number of messages removed during compaction - MessagesRemoved *float64 `json:"messagesRemoved,omitempty"` - // Total tokens in conversation after compaction - PostCompactionTokens *float64 `json:"postCompactionTokens,omitempty"` - // Number of messages before compaction - PreCompactionMessagesLength *float64 `json:"preCompactionMessagesLength,omitempty"` - // Total tokens in conversation before compaction - PreCompactionTokens *float64 `json:"preCompactionTokens,omitempty"` - // GitHub request tracing ID (x-github-request-id header) for the compaction LLM call - RequestID *string `json:"requestId,omitempty"` - // Whether compaction completed successfully - Success bool `json:"success"` - // LLM-generated summary of the compacted conversation history - SummaryContent *string `json:"summaryContent,omitempty"` - // Token count from system message(s) after compaction - SystemTokens *float64 `json:"systemTokens,omitempty"` - // Number of tokens removed during compaction - TokensRemoved *float64 `json:"tokensRemoved,omitempty"` - // Token count from tool definitions after compaction - ToolDefinitionsTokens *float64 `json:"toolDefinitionsTokens,omitempty"` -} - -func (*SessionCompactionCompleteData) sessionEventData() {} -func (*SessionCompactionCompleteData) Type() SessionEventType { - return SessionEventTypeSessionCompactionComplete -} - -// Conversation truncation statistics including token counts and removed content metrics -type SessionTruncationData struct { - // Number of messages removed by truncation - MessagesRemovedDuringTruncation float64 `json:"messagesRemovedDuringTruncation"` - // Identifier of the component that performed truncation (e.g., "BasicTruncator") - PerformedBy string `json:"performedBy"` - // Number of conversation messages after truncation - PostTruncationMessagesLength float64 `json:"postTruncationMessagesLength"` - // Total tokens in conversation messages after truncation - PostTruncationTokensInMessages float64 `json:"postTruncationTokensInMessages"` - // Number of conversation messages before truncation - PreTruncationMessagesLength float64 `json:"preTruncationMessagesLength"` - // Total tokens in conversation messages before truncation - PreTruncationTokensInMessages float64 `json:"preTruncationTokensInMessages"` - // Maximum token count for the model's context window - TokenLimit float64 `json:"tokenLimit"` - // Number of tokens removed by truncation - TokensRemovedDuringTruncation float64 `json:"tokensRemovedDuringTruncation"` -} - -func (*SessionTruncationData) sessionEventData() {} -func (*SessionTruncationData) Type() SessionEventType { return SessionEventTypeSessionTruncation } - -// Current context window usage statistics including token and message counts -type SessionUsageInfoData struct { - // Token count from non-system messages (user, assistant, tool) - ConversationTokens *float64 `json:"conversationTokens,omitempty"` - // Current number of tokens in the context window - CurrentTokens float64 `json:"currentTokens"` - // Whether this is the first usage_info event emitted in this session - IsInitial *bool `json:"isInitial,omitempty"` - // Current number of messages in the conversation - MessagesLength float64 `json:"messagesLength"` - // Token count from system message(s) - SystemTokens *float64 `json:"systemTokens,omitempty"` - // Maximum token count for the model's context window - TokenLimit float64 `json:"tokenLimit"` - // Token count from tool definitions - ToolDefinitionsTokens *float64 `json:"toolDefinitionsTokens,omitempty"` -} - -func (*SessionUsageInfoData) sessionEventData() {} -func (*SessionUsageInfoData) Type() SessionEventType { return SessionEventTypeSessionUsageInfo } - -// Custom agent selection details including name and available tools -type SubagentSelectedData struct { - // Human-readable display name of the selected custom agent - AgentDisplayName string `json:"agentDisplayName"` - // Internal name of the selected custom agent - AgentName string `json:"agentName"` - // List of tool names available to this agent, or null for all tools - Tools []string `json:"tools"` -} - -func (*SubagentSelectedData) sessionEventData() {} -func (*SubagentSelectedData) Type() SessionEventType { return SessionEventTypeSubagentSelected } - -// Elicitation request completion with the user's response -type ElicitationCompletedData struct { - // The user action: "accept" (submitted form), "decline" (explicitly refused), or "cancel" (dismissed) - Action *ElicitationCompletedAction `json:"action,omitempty"` - // The submitted form data when action is 'accept'; keys match the requested schema fields - Content map[string]ElicitationCompletedContent `json:"content,omitempty"` - // Request ID of the resolved elicitation request; clients should dismiss any UI for this request - RequestID string `json:"requestId"` -} - -func (*ElicitationCompletedData) sessionEventData() {} -func (*ElicitationCompletedData) Type() SessionEventType { return SessionEventTypeElicitationCompleted } - -// Elicitation request; may be form-based (structured input) or URL-based (browser redirect) -type ElicitationRequestedData struct { - // The source that initiated the request (MCP server name, or absent for agent-initiated) - ElicitationSource *string `json:"elicitationSource,omitempty"` - // Message describing what information is needed from the user - Message string `json:"message"` - // Elicitation mode; "form" for structured input, "url" for browser-based. Defaults to "form" when absent. - Mode *ElicitationRequestedMode `json:"mode,omitempty"` - // JSON Schema describing the form fields to present to the user (form mode only) - RequestedSchema *ElicitationRequestedSchema `json:"requestedSchema,omitempty"` - // Unique identifier for this elicitation request; used to respond via session.respondToElicitation() - RequestID string `json:"requestId"` - // Tool call ID from the LLM completion; used to correlate with CompletionChunk.toolCall.id for remote UIs - ToolCallID *string `json:"toolCallId,omitempty"` - // URL to open in the user's browser (url mode only) - URL *string `json:"url,omitempty"` -} - -func (*ElicitationRequestedData) sessionEventData() {} -func (*ElicitationRequestedData) Type() SessionEventType { return SessionEventTypeElicitationRequested } - -// Empty payload; the event signals that the custom agent was deselected, returning to the default agent -type SubagentDeselectedData struct { -} - -func (*SubagentDeselectedData) sessionEventData() {} -func (*SubagentDeselectedData) Type() SessionEventType { return SessionEventTypeSubagentDeselected } - -// Empty payload; the event signals that the pending message queue has changed -type PendingMessagesModifiedData struct { -} - -func (*PendingMessagesModifiedData) sessionEventData() {} -func (*PendingMessagesModifiedData) Type() SessionEventType { - return SessionEventTypePendingMessagesModified -} - -// Error details for timeline display including message and optional diagnostic information -type SessionErrorData struct { - // Only set on `errorType: "rate_limit"`. When `true`, the runtime will follow this error with an `auto_mode_switch.requested` event (or silently switch if `continueOnAutoMode` is enabled). UI clients can use this flag to suppress duplicate rendering of the rate-limit error when they show their own auto-mode-switch prompt. - EligibleForAutoSwitch *bool `json:"eligibleForAutoSwitch,omitempty"` - // Fine-grained error code from the upstream provider, when available. For `errorType: "rate_limit"`, this is one of the `RateLimitErrorCode` values (e.g., `"user_weekly_rate_limited"`, `"user_global_rate_limited"`, `"rate_limited"`, `"user_model_rate_limited"`, `"integration_rate_limited"`). - ErrorCode *string `json:"errorCode,omitempty"` - // Category of error (e.g., "authentication", "authorization", "quota", "rate_limit", "context_limit", "query") - ErrorType string `json:"errorType"` - // Human-readable error message - Message string `json:"message"` - // GitHub request tracing ID (x-github-request-id header) for correlating with server-side logs - ProviderCallID *string `json:"providerCallId,omitempty"` - // Error stack trace, when available - Stack *string `json:"stack,omitempty"` - // HTTP status code from the upstream request, if applicable - StatusCode *int64 `json:"statusCode,omitempty"` - // Optional URL associated with this error that the user can open in a browser - URL *string `json:"url,omitempty"` -} - -func (*SessionErrorData) sessionEventData() {} -func (*SessionErrorData) Type() SessionEventType { return SessionEventTypeSessionError } - -// External tool completion notification signaling UI dismissal -type ExternalToolCompletedData struct { - // Request ID of the resolved external tool request; clients should dismiss any UI for this request - RequestID string `json:"requestId"` -} - -func (*ExternalToolCompletedData) sessionEventData() {} -func (*ExternalToolCompletedData) Type() SessionEventType { - return SessionEventTypeExternalToolCompleted -} - -// External tool invocation request for client-side tool execution -type ExternalToolRequestedData struct { - // Arguments to pass to the external tool - Arguments any `json:"arguments,omitempty"` - // Unique identifier for this request; used to respond via session.respondToExternalTool() - RequestID string `json:"requestId"` - // Session ID that this external tool request belongs to - SessionID string `json:"sessionId"` - // Tool call ID assigned to this external tool invocation - ToolCallID string `json:"toolCallId"` - // Name of the external tool to invoke - ToolName string `json:"toolName"` - // W3C Trace Context traceparent header for the execute_tool span - Traceparent *string `json:"traceparent,omitempty"` - // W3C Trace Context tracestate header for the execute_tool span - Tracestate *string `json:"tracestate,omitempty"` -} - -func (*ExternalToolRequestedData) sessionEventData() {} -func (*ExternalToolRequestedData) Type() SessionEventType { - return SessionEventTypeExternalToolRequested -} - -// Failed LLM API call metadata for telemetry -type ModelCallFailureData struct { - // Completion ID from the model provider (e.g., chatcmpl-abc123) - APICallID *string `json:"apiCallId,omitempty"` - // Duration of the failed API call in milliseconds - DurationMs *float64 `json:"durationMs,omitempty"` - // Raw provider/runtime error message for restricted telemetry - ErrorMessage *string `json:"errorMessage,omitempty"` - // What initiated this API call (e.g., "sub-agent", "mcp-sampling"); absent for user-initiated calls - Initiator *string `json:"initiator,omitempty"` - // Model identifier used for the failed API call - Model *string `json:"model,omitempty"` - // GitHub request tracing ID (x-github-request-id header) for server-side log correlation - ProviderCallID *string `json:"providerCallId,omitempty"` - // Where the failed model call originated - Source ModelCallFailureSource `json:"source"` - // HTTP status code from the failed request - StatusCode *int64 `json:"statusCode,omitempty"` -} - -func (*ModelCallFailureData) sessionEventData() {} -func (*ModelCallFailureData) Type() SessionEventType { return SessionEventTypeModelCallFailure } - -// Hook invocation completion details including output, success status, and error information -type HookEndData struct { - // Error details when the hook failed - Error *HookEndError `json:"error,omitempty"` - // Identifier matching the corresponding hook.start event - HookInvocationID string `json:"hookInvocationId"` - // Type of hook that was invoked (e.g., "preToolUse", "postToolUse", "sessionStart") - HookType string `json:"hookType"` - // Output data produced by the hook - Output any `json:"output,omitempty"` - // Whether the hook completed successfully - Success bool `json:"success"` -} - -func (*HookEndData) sessionEventData() {} -func (*HookEndData) Type() SessionEventType { return SessionEventTypeHookEnd } - -// Hook invocation start details including type and input data -type HookStartData struct { - // Unique identifier for this hook invocation - HookInvocationID string `json:"hookInvocationId"` - // Type of hook being invoked (e.g., "preToolUse", "postToolUse", "sessionStart") - HookType string `json:"hookType"` - // Input data passed to the hook - Input any `json:"input,omitempty"` -} - -func (*HookStartData) sessionEventData() {} -func (*HookStartData) Type() SessionEventType { return SessionEventTypeHookStart } - -// Informational message for timeline display with categorization -type SessionInfoData struct { - // Category of informational message (e.g., "notification", "timing", "context_window", "mcp", "snapshot", "configuration", "authentication", "model") - InfoType string `json:"infoType"` - // Human-readable informational message for display in the timeline - Message string `json:"message"` - // Optional actionable tip displayed with this message - Tip *string `json:"tip,omitempty"` - // Optional URL associated with this message that the user can open in a browser - URL *string `json:"url,omitempty"` -} - -func (*SessionInfoData) sessionEventData() {} -func (*SessionInfoData) Type() SessionEventType { return SessionEventTypeSessionInfo } - -// LLM API call usage metrics including tokens, costs, quotas, and billing information -type AssistantUsageData struct { - // Completion ID from the model provider (e.g., chatcmpl-abc123) - APICallID *string `json:"apiCallId,omitempty"` - // API endpoint used for this model call, matching CAPI supported_endpoints vocabulary - APIEndpoint *AssistantUsageAPIEndpoint `json:"apiEndpoint,omitempty"` - // Number of tokens read from prompt cache - CacheReadTokens *float64 `json:"cacheReadTokens,omitempty"` - // Number of tokens written to prompt cache - CacheWriteTokens *float64 `json:"cacheWriteTokens,omitempty"` - // Per-request cost and usage data from the CAPI copilot_usage response field - CopilotUsage *AssistantUsageCopilotUsage `json:"copilotUsage,omitempty"` - // Model multiplier cost for billing purposes - Cost *float64 `json:"cost,omitempty"` - // Duration of the API call in milliseconds - Duration *float64 `json:"duration,omitempty"` - // What initiated this API call (e.g., "sub-agent", "mcp-sampling"); absent for user-initiated calls - Initiator *string `json:"initiator,omitempty"` - // Number of input tokens consumed - InputTokens *float64 `json:"inputTokens,omitempty"` - // Average inter-token latency in milliseconds. Only available for streaming requests - InterTokenLatencyMs *float64 `json:"interTokenLatencyMs,omitempty"` - // Model identifier used for this API call - Model string `json:"model"` - // Number of output tokens produced - OutputTokens *float64 `json:"outputTokens,omitempty"` - // Parent tool call ID when this usage originates from a sub-agent - // Deprecated: ParentToolCallID is deprecated. - ParentToolCallID *string `json:"parentToolCallId,omitempty"` - // GitHub request tracing ID (x-github-request-id header) for server-side log correlation - ProviderCallID *string `json:"providerCallId,omitempty"` - // Per-quota resource usage snapshots, keyed by quota identifier - QuotaSnapshots map[string]AssistantUsageQuotaSnapshot `json:"quotaSnapshots,omitempty"` - // Reasoning effort level used for model calls, if applicable (e.g. "low", "medium", "high", "xhigh") - ReasoningEffort *string `json:"reasoningEffort,omitempty"` - // Number of output tokens used for reasoning (e.g., chain-of-thought) - ReasoningTokens *float64 `json:"reasoningTokens,omitempty"` - // Time to first token in milliseconds. Only available for streaming requests - TtftMs *float64 `json:"ttftMs,omitempty"` -} - -func (*AssistantUsageData) sessionEventData() {} -func (*AssistantUsageData) Type() SessionEventType { return SessionEventTypeAssistantUsage } - -// MCP OAuth request completion notification -type McpOauthCompletedData struct { - // Request ID of the resolved OAuth request - RequestID string `json:"requestId"` -} - -func (*McpOauthCompletedData) sessionEventData() {} -func (*McpOauthCompletedData) Type() SessionEventType { return SessionEventTypeMcpOauthCompleted } - -// Model change details including previous and new model identifiers -type SessionModelChangeData struct { - // Reason the change happened, when not user-initiated. Currently `"rate_limit_auto_switch"` for changes triggered by the auto-mode-switch rate-limit recovery path. UI clients can use this to render contextual copy. - Cause *string `json:"cause,omitempty"` - // Newly selected model identifier - NewModel string `json:"newModel"` - // Model that was previously selected, if any - PreviousModel *string `json:"previousModel,omitempty"` - // Reasoning effort level before the model change, if applicable - PreviousReasoningEffort *string `json:"previousReasoningEffort,omitempty"` - // Reasoning effort level after the model change, if applicable - ReasoningEffort *string `json:"reasoningEffort,omitempty"` -} - -func (*SessionModelChangeData) sessionEventData() {} -func (*SessionModelChangeData) Type() SessionEventType { return SessionEventTypeSessionModelChange } - -// Notifies Mission Control that the session's remote steering capability has changed -type SessionRemoteSteerableChangedData struct { - // Whether this session now supports remote steering via Mission Control - RemoteSteerable bool `json:"remoteSteerable"` -} - -func (*SessionRemoteSteerableChangedData) sessionEventData() {} -func (*SessionRemoteSteerableChangedData) Type() SessionEventType { - return SessionEventTypeSessionRemoteSteerableChanged -} - -// OAuth authentication request for an MCP server -type McpOauthRequiredData struct { - // Unique identifier for this OAuth request; used to respond via session.respondToMcpOAuth() - RequestID string `json:"requestId"` - // Display name of the MCP server that requires OAuth - ServerName string `json:"serverName"` - // URL of the MCP server that requires OAuth - ServerURL string `json:"serverUrl"` - // Static OAuth client configuration, if the server specifies one - StaticClientConfig *McpOauthRequiredStaticClientConfig `json:"staticClientConfig,omitempty"` -} - -func (*McpOauthRequiredData) sessionEventData() {} -func (*McpOauthRequiredData) Type() SessionEventType { return SessionEventTypeMcpOauthRequired } - -// Payload indicating the session is idle with no background agents in flight -type SessionIdleData struct { - // True when the preceding agentic loop was cancelled via abort signal - Aborted *bool `json:"aborted,omitempty"` -} - -func (*SessionIdleData) sessionEventData() {} -func (*SessionIdleData) Type() SessionEventType { return SessionEventTypeSessionIdle } - -// Permission request completion notification signaling UI dismissal -type PermissionCompletedData struct { - // Request ID of the resolved permission request; clients should dismiss any UI for this request - RequestID string `json:"requestId"` - // The result of the permission request - Result PermissionResult `json:"result"` - // Optional tool call ID associated with this permission prompt; clients may use it to correlate UI created from tool-scoped prompts - ToolCallID *string `json:"toolCallId,omitempty"` -} - -func (*PermissionCompletedData) sessionEventData() {} -func (*PermissionCompletedData) Type() SessionEventType { return SessionEventTypePermissionCompleted } - -// Permission request notification requiring client approval with request details -type PermissionRequestedData struct { - // Details of the permission being requested - PermissionRequest PermissionRequest `json:"permissionRequest"` - // Derived user-facing permission prompt details for UI consumers - PromptRequest PermissionPromptRequest `json:"promptRequest,omitempty"` - // Unique identifier for this permission request; used to respond via session.respondToPermission() - 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"` -} - -func (*PermissionRequestedData) sessionEventData() {} -func (*PermissionRequestedData) Type() SessionEventType { return SessionEventTypePermissionRequested } - -// Plan approval request with plan content and available user actions -type ExitPlanModeRequestedData struct { - // Available actions the user can take (e.g., approve, edit, reject) - Actions []string `json:"actions"` - // Full content of the plan file - PlanContent string `json:"planContent"` - // The recommended action for the user to take - RecommendedAction string `json:"recommendedAction"` - // Unique identifier for this request; used to respond via session.respondToExitPlanMode() - RequestID string `json:"requestId"` - // Summary of the plan that was created - Summary string `json:"summary"` -} - -func (*ExitPlanModeRequestedData) sessionEventData() {} -func (*ExitPlanModeRequestedData) Type() SessionEventType { - return SessionEventTypeExitPlanModeRequested -} - -// Plan file operation details indicating what changed -type SessionPlanChangedData struct { - // The type of operation performed on the plan file - Operation PlanChangedOperation `json:"operation"` -} - -func (*SessionPlanChangedData) sessionEventData() {} -func (*SessionPlanChangedData) Type() SessionEventType { return SessionEventTypeSessionPlanChanged } - -// Plan mode exit completion with the user's approval decision and optional feedback -type ExitPlanModeCompletedData struct { - // Whether the plan was approved by the user - Approved *bool `json:"approved,omitempty"` - // Whether edits should be auto-approved without confirmation - AutoApproveEdits *bool `json:"autoApproveEdits,omitempty"` - // Free-form feedback from the user if they requested changes to the plan - Feedback *string `json:"feedback,omitempty"` - // Request ID of the resolved exit plan mode request; clients should dismiss any UI for this request - RequestID string `json:"requestId"` - // Which action the user selected (e.g. 'autopilot', 'interactive', 'exit_only') - SelectedAction *string `json:"selectedAction,omitempty"` -} - -func (*ExitPlanModeCompletedData) sessionEventData() {} -func (*ExitPlanModeCompletedData) Type() SessionEventType { - return SessionEventTypeExitPlanModeCompleted -} - -// Queued command completion notification signaling UI dismissal -type CommandCompletedData struct { - // Request ID of the resolved command request; clients should dismiss any UI for this request - RequestID string `json:"requestId"` -} - -func (*CommandCompletedData) sessionEventData() {} -func (*CommandCompletedData) Type() SessionEventType { return SessionEventTypeCommandCompleted } - -// Queued slash command dispatch request for client execution -type CommandQueuedData struct { - // The slash command text to be executed (e.g., /help, /clear) - Command string `json:"command"` - // Unique identifier for this request; used to respond via session.respondToQueuedCommand() - RequestID string `json:"requestId"` -} - -func (*CommandQueuedData) sessionEventData() {} -func (*CommandQueuedData) Type() SessionEventType { return SessionEventTypeCommandQueued } - -// Registered command dispatch request routed to the owning client -type CommandExecuteData struct { - // Raw argument string after the command name - Args string `json:"args"` - // The full command text (e.g., /deploy production) - Command string `json:"command"` - // Command name without leading / - CommandName string `json:"commandName"` - // Unique identifier; used to respond via session.commands.handlePendingCommand() - RequestID string `json:"requestId"` -} - -func (*CommandExecuteData) sessionEventData() {} -func (*CommandExecuteData) Type() SessionEventType { return SessionEventTypeCommandExecute } - -// SDK command registration change notification -type CommandsChangedData struct { - // Current list of registered SDK commands - Commands []CommandsChangedCommand `json:"commands"` -} - -func (*CommandsChangedData) sessionEventData() {} -func (*CommandsChangedData) Type() SessionEventType { return SessionEventTypeCommandsChanged } - -// Sampling request completion notification signaling UI dismissal -type SamplingCompletedData struct { - // Request ID of the resolved sampling request; clients should dismiss any UI for this request - RequestID string `json:"requestId"` -} - -func (*SamplingCompletedData) sessionEventData() {} -func (*SamplingCompletedData) Type() SessionEventType { return SessionEventTypeSamplingCompleted } - -// Sampling request from an MCP server; contains the server name and a requestId for correlation -type SamplingRequestedData struct { - // The JSON-RPC request ID from the MCP protocol - McpRequestID any `json:"mcpRequestId"` - // Unique identifier for this sampling request; used to respond via session.respondToSampling() - RequestID string `json:"requestId"` - // Name of the MCP server that initiated the sampling request - ServerName string `json:"serverName"` -} - -func (*SamplingRequestedData) sessionEventData() {} -func (*SamplingRequestedData) Type() SessionEventType { return SessionEventTypeSamplingRequested } - -// Scheduled prompt cancelled from the schedule manager dialog -type SessionScheduleCancelledData struct { - // Id of the scheduled prompt that was cancelled - ID int64 `json:"id"` -} - -func (*SessionScheduleCancelledData) sessionEventData() {} -func (*SessionScheduleCancelledData) Type() SessionEventType { - return SessionEventTypeSessionScheduleCancelled -} - -// Scheduled prompt registered via /every or /after -type SessionScheduleCreatedData struct { - // Sequential id assigned to the scheduled prompt within the session - ID int64 `json:"id"` - // Interval between ticks in milliseconds - IntervalMs int64 `json:"intervalMs"` - // 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`) - Recurring *bool `json:"recurring,omitempty"` -} - -func (*SessionScheduleCreatedData) sessionEventData() {} -func (*SessionScheduleCreatedData) Type() SessionEventType { - return SessionEventTypeSessionScheduleCreated -} - -// Session capability change notification -type CapabilitiesChangedData struct { - // UI capability changes - UI *CapabilitiesChangedUI `json:"ui,omitempty"` -} - -func (*CapabilitiesChangedData) sessionEventData() {} -func (*CapabilitiesChangedData) Type() SessionEventType { return SessionEventTypeCapabilitiesChanged } - -// Session handoff metadata including source, context, and repository information -type SessionHandoffData struct { - // Additional context information for the handoff - Context *string `json:"context,omitempty"` - // ISO 8601 timestamp when the handoff occurred - HandoffTime time.Time `json:"handoffTime"` - // GitHub host URL for the source session (e.g., https://github.com or https://tenant.ghe.com) - Host *string `json:"host,omitempty"` - // Session ID of the remote session being handed off - RemoteSessionID *string `json:"remoteSessionId,omitempty"` - // Repository context for the handed-off session - Repository *HandoffRepository `json:"repository,omitempty"` - // Origin type of the session being handed off - SourceType HandoffSourceType `json:"sourceType"` - // Summary of the work done in the source session - Summary *string `json:"summary,omitempty"` -} - -func (*SessionHandoffData) sessionEventData() {} -func (*SessionHandoffData) Type() SessionEventType { return SessionEventTypeSessionHandoff } - -// Session initialization metadata including context and configuration -type SessionStartData struct { - // Whether the session was already in use by another client at start time - AlreadyInUse *bool `json:"alreadyInUse,omitempty"` - // Working directory and git context at session start - Context *WorkingDirectoryContext `json:"context,omitempty"` - // Version string of the Copilot application - CopilotVersion string `json:"copilotVersion"` - // When set, identifies a parent session whose context this session continues — e.g., a detached headless rem-agent run launched on the parent's interactive shutdown. Telemetry from this session is reported under the parent's session_id. - DetachedFromSpawningParentSessionID *string `json:"detachedFromSpawningParentSessionId,omitempty"` - // Identifier of the software producing the events (e.g., "copilot-agent") - Producer string `json:"producer"` - // Reasoning effort level used for model calls, if applicable (e.g. "low", "medium", "high", "xhigh") - ReasoningEffort *string `json:"reasoningEffort,omitempty"` - // Whether this session supports remote steering via Mission Control - RemoteSteerable *bool `json:"remoteSteerable,omitempty"` - // Model selected at session creation time, if any - SelectedModel *string `json:"selectedModel,omitempty"` - // Unique identifier for the session - SessionID string `json:"sessionId"` - // ISO 8601 timestamp when the session was created - StartTime time.Time `json:"startTime"` - // Schema version number for the session event format - Version float64 `json:"version"` -} - -func (*SessionStartData) sessionEventData() {} -func (*SessionStartData) Type() SessionEventType { return SessionEventTypeSessionStart } - -// Session resume metadata including current context and event count -type SessionResumeData struct { - // Whether the session was already in use by another client at resume time - AlreadyInUse *bool `json:"alreadyInUse,omitempty"` - // Updated working directory and git context at resume time - Context *WorkingDirectoryContext `json:"context,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. - ContinuePendingWork *bool `json:"continuePendingWork,omitempty"` - // Total number of persisted events in the session at the time of resume - EventCount float64 `json:"eventCount"` - // Reasoning effort level used for model calls, if applicable (e.g. "low", "medium", "high", "xhigh") - ReasoningEffort *string `json:"reasoningEffort,omitempty"` - // Whether this session supports remote steering via Mission Control - RemoteSteerable *bool `json:"remoteSteerable,omitempty"` - // ISO 8601 timestamp when the session was resumed - ResumeTime time.Time `json:"resumeTime"` - // Model currently selected at resume time - SelectedModel *string `json:"selectedModel,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. - SessionWasActive *bool `json:"sessionWasActive,omitempty"` -} - -func (*SessionResumeData) sessionEventData() {} -func (*SessionResumeData) Type() SessionEventType { return SessionEventTypeSessionResume } - -// Session rewind details including target event and count of removed events -type SessionSnapshotRewindData struct { - // Number of events that were removed by the rewind - EventsRemoved float64 `json:"eventsRemoved"` - // Event ID that was rewound to; this event and all after it were removed - UpToEventID string `json:"upToEventId"` -} - -func (*SessionSnapshotRewindData) sessionEventData() {} -func (*SessionSnapshotRewindData) Type() SessionEventType { - return SessionEventTypeSessionSnapshotRewind -} - -// Session termination metrics including usage statistics, code changes, and shutdown reason -type SessionShutdownData struct { - // Aggregate code change metrics for the session - CodeChanges ShutdownCodeChanges `json:"codeChanges"` - // Non-system message token count at shutdown - ConversationTokens *float64 `json:"conversationTokens,omitempty"` - // Model that was selected at the time of shutdown - CurrentModel *string `json:"currentModel,omitempty"` - // Total tokens in context window at shutdown - CurrentTokens *float64 `json:"currentTokens,omitempty"` - // Error description when shutdownType is "error" - ErrorReason *string `json:"errorReason,omitempty"` - // Per-model usage breakdown, keyed by model identifier - ModelMetrics map[string]ShutdownModelMetric `json:"modelMetrics"` - // Unix timestamp (milliseconds) when the session started - SessionStartTime float64 `json:"sessionStartTime"` - // Whether the session ended normally ("routine") or due to a crash/fatal error ("error") - ShutdownType ShutdownType `json:"shutdownType"` - // System message token count at shutdown - SystemTokens *float64 `json:"systemTokens,omitempty"` - // Session-wide per-token-type accumulated token counts - TokenDetails map[string]ShutdownTokenDetail `json:"tokenDetails,omitempty"` - // Tool definitions token count at shutdown - ToolDefinitionsTokens *float64 `json:"toolDefinitionsTokens,omitempty"` - // Cumulative time spent in API calls during the session, in milliseconds - TotalAPIDurationMs float64 `json:"totalApiDurationMs"` - // Session-wide accumulated nano-AI units cost - TotalNanoAiu *float64 `json:"totalNanoAiu,omitempty"` - // Total number of premium API requests used during the session - TotalPremiumRequests float64 `json:"totalPremiumRequests"` -} - -func (*SessionShutdownData) sessionEventData() {} -func (*SessionShutdownData) Type() SessionEventType { return SessionEventTypeSessionShutdown } - -// Session title change payload containing the new display title -type SessionTitleChangedData struct { - // The new display title for the session - Title string `json:"title"` -} - -func (*SessionTitleChangedData) sessionEventData() {} -func (*SessionTitleChangedData) Type() SessionEventType { return SessionEventTypeSessionTitleChanged } - -// SessionBackgroundTasksChangedData holds the payload for session.background_tasks_changed events. -type SessionBackgroundTasksChangedData struct { -} - -func (*SessionBackgroundTasksChangedData) sessionEventData() {} -func (*SessionBackgroundTasksChangedData) Type() SessionEventType { - return SessionEventTypeSessionBackgroundTasksChanged -} - -// SessionCustomAgentsUpdatedData holds the payload for session.custom_agents_updated events. -type SessionCustomAgentsUpdatedData struct { - // Array of loaded custom agent metadata - Agents []CustomAgentsUpdatedAgent `json:"agents"` - // Fatal errors from agent loading - Errors []string `json:"errors"` - // Non-fatal warnings from agent loading - Warnings []string `json:"warnings"` -} - -func (*SessionCustomAgentsUpdatedData) sessionEventData() {} -func (*SessionCustomAgentsUpdatedData) Type() SessionEventType { - return SessionEventTypeSessionCustomAgentsUpdated -} - -// SessionExtensionsLoadedData holds the payload for session.extensions_loaded events. -type SessionExtensionsLoadedData struct { - // Array of discovered extensions and their status - Extensions []ExtensionsLoadedExtension `json:"extensions"` -} - -func (*SessionExtensionsLoadedData) sessionEventData() {} -func (*SessionExtensionsLoadedData) Type() SessionEventType { - return SessionEventTypeSessionExtensionsLoaded -} - -// SessionMcpServerStatusChangedData holds the payload for session.mcp_server_status_changed events. -type SessionMcpServerStatusChangedData struct { - // Name of the MCP server whose status changed - ServerName string `json:"serverName"` - // New connection status: connected, failed, needs-auth, pending, disabled, or not_configured - Status McpServerStatusChangedStatus `json:"status"` -} - -func (*SessionMcpServerStatusChangedData) sessionEventData() {} -func (*SessionMcpServerStatusChangedData) Type() SessionEventType { - return SessionEventTypeSessionMcpServerStatusChanged -} - -// SessionMcpServersLoadedData holds the payload for session.mcp_servers_loaded events. -type SessionMcpServersLoadedData struct { - // Array of MCP server status summaries - Servers []McpServersLoadedServer `json:"servers"` -} - -func (*SessionMcpServersLoadedData) sessionEventData() {} -func (*SessionMcpServersLoadedData) Type() SessionEventType { - return SessionEventTypeSessionMcpServersLoaded -} - -// SessionSkillsLoadedData holds the payload for session.skills_loaded events. -type SessionSkillsLoadedData struct { - // Array of resolved skill metadata - Skills []SkillsLoadedSkill `json:"skills"` -} - -func (*SessionSkillsLoadedData) sessionEventData() {} -func (*SessionSkillsLoadedData) Type() SessionEventType { return SessionEventTypeSessionSkillsLoaded } - -// SessionToolsUpdatedData holds the payload for session.tools_updated events. -type SessionToolsUpdatedData struct { - Model string `json:"model"` -} - -func (*SessionToolsUpdatedData) sessionEventData() {} -func (*SessionToolsUpdatedData) Type() SessionEventType { return SessionEventTypeSessionToolsUpdated } - -// Skill invocation details including content, allowed tools, and plugin metadata -type SkillInvokedData struct { - // Tool names that should be auto-approved when this skill is active - AllowedTools []string `json:"allowedTools,omitempty"` - // Full content of the skill file, injected into the conversation for the model - Content string `json:"content"` - // Description of the skill from its SKILL.md frontmatter - Description *string `json:"description,omitempty"` - // Name of the invoked skill - Name string `json:"name"` - // File path to the SKILL.md definition - Path string `json:"path"` - // Name of the plugin this skill originated from, when applicable - PluginName *string `json:"pluginName,omitempty"` - // Version of the plugin this skill originated from, when applicable - PluginVersion *string `json:"pluginVersion,omitempty"` -} - -func (*SkillInvokedData) sessionEventData() {} -func (*SkillInvokedData) Type() SessionEventType { return SessionEventTypeSkillInvoked } - -// Streaming assistant message delta for incremental response updates -type AssistantMessageDeltaData struct { - // Incremental text chunk to append to the message content - DeltaContent string `json:"deltaContent"` - // Message ID this delta belongs to, matching the corresponding assistant.message event - MessageID string `json:"messageId"` - // 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"` -} - -func (*AssistantMessageDeltaData) sessionEventData() {} -func (*AssistantMessageDeltaData) Type() SessionEventType { - return SessionEventTypeAssistantMessageDelta -} - -// Streaming assistant message start metadata -type AssistantMessageStartData struct { - // Message ID this start event belongs to, matching subsequent deltas and assistant.message - MessageID string `json:"messageId"` - // Generation phase this message belongs to for phased-output models - Phase *string `json:"phase,omitempty"` -} - -func (*AssistantMessageStartData) sessionEventData() {} -func (*AssistantMessageStartData) Type() SessionEventType { - return SessionEventTypeAssistantMessageStart -} - -// Streaming reasoning delta for incremental extended thinking updates -type AssistantReasoningDeltaData struct { - // Incremental text chunk to append to the reasoning content - DeltaContent string `json:"deltaContent"` - // Reasoning block ID this delta belongs to, matching the corresponding assistant.reasoning event - ReasoningID string `json:"reasoningId"` -} - -func (*AssistantReasoningDeltaData) sessionEventData() {} -func (*AssistantReasoningDeltaData) Type() SessionEventType { - return SessionEventTypeAssistantReasoningDelta -} - -// Streaming response progress with cumulative byte count -type AssistantStreamingDeltaData struct { - // Cumulative total bytes received from the streaming response so far - TotalResponseSizeBytes float64 `json:"totalResponseSizeBytes"` -} - -func (*AssistantStreamingDeltaData) sessionEventData() {} -func (*AssistantStreamingDeltaData) Type() SessionEventType { - return SessionEventTypeAssistantStreamingDelta -} - -// Streaming tool execution output for incremental result display -type ToolExecutionPartialResultData struct { - // Incremental output chunk from the running tool - PartialOutput string `json:"partialOutput"` - // Tool call ID this partial result belongs to - ToolCallID string `json:"toolCallId"` -} - -func (*ToolExecutionPartialResultData) sessionEventData() {} -func (*ToolExecutionPartialResultData) Type() SessionEventType { - return SessionEventTypeToolExecutionPartialResult -} - -// Sub-agent completion details for successful execution -type SubagentCompletedData struct { - // Human-readable display name of the sub-agent - AgentDisplayName string `json:"agentDisplayName"` - // Internal name of the sub-agent - AgentName string `json:"agentName"` - // Wall-clock duration of the sub-agent execution in milliseconds - DurationMs *float64 `json:"durationMs,omitempty"` - // Model used by the sub-agent - Model *string `json:"model,omitempty"` - // Tool call ID of the parent tool invocation that spawned this sub-agent - ToolCallID string `json:"toolCallId"` - // Total tokens (input + output) consumed by the sub-agent - TotalTokens *float64 `json:"totalTokens,omitempty"` - // Total number of tool calls made by the sub-agent - TotalToolCalls *float64 `json:"totalToolCalls,omitempty"` -} - -func (*SubagentCompletedData) sessionEventData() {} -func (*SubagentCompletedData) Type() SessionEventType { return SessionEventTypeSubagentCompleted } - -// Sub-agent failure details including error message and agent information -type SubagentFailedData struct { - // Human-readable display name of the sub-agent - AgentDisplayName string `json:"agentDisplayName"` - // Internal name of the sub-agent - AgentName string `json:"agentName"` - // Wall-clock duration of the sub-agent execution in milliseconds - DurationMs *float64 `json:"durationMs,omitempty"` - // Error message describing why the sub-agent failed - Error string `json:"error"` - // Model used by the sub-agent (if any model calls succeeded before failure) - Model *string `json:"model,omitempty"` - // Tool call ID of the parent tool invocation that spawned this sub-agent - ToolCallID string `json:"toolCallId"` - // Total tokens (input + output) consumed before the sub-agent failed - TotalTokens *float64 `json:"totalTokens,omitempty"` - // Total number of tool calls made before the sub-agent failed - TotalToolCalls *float64 `json:"totalToolCalls,omitempty"` -} - -func (*SubagentFailedData) sessionEventData() {} -func (*SubagentFailedData) Type() SessionEventType { return SessionEventTypeSubagentFailed } - -// Sub-agent startup details including parent tool call and agent information -type SubagentStartedData struct { - // Description of what the sub-agent does - AgentDescription string `json:"agentDescription"` - // Human-readable display name of the sub-agent - AgentDisplayName string `json:"agentDisplayName"` - // Internal name of the sub-agent - AgentName string `json:"agentName"` - // Model the sub-agent will run with, when known at start. Surfaced in the timeline for auto-selected sub-agents (e.g. rubber-duck). - Model *string `json:"model,omitempty"` - // Tool call ID of the parent tool invocation that spawned this sub-agent - ToolCallID string `json:"toolCallId"` -} - -func (*SubagentStartedData) sessionEventData() {} -func (*SubagentStartedData) Type() SessionEventType { return SessionEventTypeSubagentStarted } - -// System-generated notification for runtime events like background task completion -type SystemNotificationData struct { - // The notification text, typically wrapped in XML tags - Content string `json:"content"` - // Structured metadata identifying what triggered this notification - Kind SystemNotification `json:"kind"` -} - -func (*SystemNotificationData) sessionEventData() {} -func (*SystemNotificationData) Type() SessionEventType { return SessionEventTypeSystemNotification } - -// System/developer instruction content with role and optional template metadata -type SystemMessageData struct { - // The system or developer prompt text sent as model input - Content string `json:"content"` - // Metadata about the prompt template and its construction - Metadata *SystemMessageMetadata `json:"metadata,omitempty"` - // Optional name identifier for the message source - Name *string `json:"name,omitempty"` - // Message role: "system" for system prompts, "developer" for developer-injected instructions - Role SystemMessageRole `json:"role"` -} - -func (*SystemMessageData) sessionEventData() {} -func (*SystemMessageData) Type() SessionEventType { return SessionEventTypeSystemMessage } - -// Task completion notification with summary from the agent -type SessionTaskCompleteData struct { - // Whether the tool call succeeded. False when validation failed (e.g., invalid arguments) - Success *bool `json:"success,omitempty"` - // Summary of the completed task, provided by the agent - Summary *string `json:"summary,omitempty"` -} - -func (*SessionTaskCompleteData) sessionEventData() {} -func (*SessionTaskCompleteData) Type() SessionEventType { return SessionEventTypeSessionTaskComplete } - -// Tool execution completion results including success status, detailed output, and error information -type ToolExecutionCompleteData struct { - // Error details when the tool execution failed - Error *ToolExecutionCompleteError `json:"error,omitempty"` - // CAPI interaction ID for correlating this tool execution with upstream telemetry - InteractionID *string `json:"interactionId,omitempty"` - // Whether this tool call was explicitly requested by the user rather than the assistant - IsUserRequested *bool `json:"isUserRequested,omitempty"` - // Model identifier that generated this tool call - Model *string `json:"model,omitempty"` - // Tool call ID of the parent tool invocation when this event originates from a sub-agent - // Deprecated: ParentToolCallID is deprecated. - ParentToolCallID *string `json:"parentToolCallId,omitempty"` - // Tool execution result on success - Result *ToolExecutionCompleteResult `json:"result,omitempty"` - // Whether the tool execution completed successfully - Success bool `json:"success"` - // Unique identifier for the completed tool call - ToolCallID string `json:"toolCallId"` - // Tool-specific telemetry data (e.g., CodeQL check counts, grep match counts) - ToolTelemetry map[string]any `json:"toolTelemetry,omitempty"` - // Identifier for the agent loop turn this tool was invoked in, matching the corresponding assistant.turn_start event - TurnID *string `json:"turnId,omitempty"` -} - -func (*ToolExecutionCompleteData) sessionEventData() {} -func (*ToolExecutionCompleteData) Type() SessionEventType { - return SessionEventTypeToolExecutionComplete -} - -// Tool execution progress notification with status message -type ToolExecutionProgressData struct { - // Human-readable progress status message (e.g., from an MCP server) - ProgressMessage string `json:"progressMessage"` - // Tool call ID this progress notification belongs to - ToolCallID string `json:"toolCallId"` -} - -func (*ToolExecutionProgressData) sessionEventData() {} -func (*ToolExecutionProgressData) Type() SessionEventType { - return SessionEventTypeToolExecutionProgress -} - -// Tool execution startup details including MCP server information when applicable -type ToolExecutionStartData struct { - // Arguments passed to the tool - Arguments any `json:"arguments,omitempty"` - // Name of the MCP server hosting this tool, when the tool is an MCP tool - McpServerName *string `json:"mcpServerName,omitempty"` - // Original tool name on the MCP server, when the tool is an MCP tool - McpToolName *string `json:"mcpToolName,omitempty"` - // Tool call ID of the parent tool invocation when this event originates from a sub-agent - // Deprecated: ParentToolCallID is deprecated. - ParentToolCallID *string `json:"parentToolCallId,omitempty"` - // Unique identifier for this tool call - ToolCallID string `json:"toolCallId"` - // Name of the tool being executed - ToolName string `json:"toolName"` - // Identifier for the agent loop turn this tool was invoked in, matching the corresponding assistant.turn_start event - TurnID *string `json:"turnId,omitempty"` -} - -func (*ToolExecutionStartData) sessionEventData() {} -func (*ToolExecutionStartData) Type() SessionEventType { return SessionEventTypeToolExecutionStart } - -// Turn abort information including the reason for termination -type AbortData struct { - // Finite reason code describing why the current turn was aborted - Reason AbortReason `json:"reason"` -} - -func (*AbortData) sessionEventData() {} -func (*AbortData) Type() SessionEventType { return SessionEventTypeAbort } - -// Turn completion metadata including the turn identifier -type AssistantTurnEndData struct { - // Identifier of the turn that has ended, matching the corresponding assistant.turn_start event - TurnID string `json:"turnId"` -} - -func (*AssistantTurnEndData) sessionEventData() {} -func (*AssistantTurnEndData) Type() SessionEventType { return SessionEventTypeAssistantTurnEnd } - -// Turn initialization metadata including identifier and interaction tracking -type AssistantTurnStartData struct { - // CAPI interaction ID for correlating this turn with upstream telemetry - InteractionID *string `json:"interactionId,omitempty"` - // Identifier for this turn within the agentic loop, typically a stringified turn number - TurnID string `json:"turnId"` -} - -func (*AssistantTurnStartData) sessionEventData() {} -func (*AssistantTurnStartData) Type() SessionEventType { return SessionEventTypeAssistantTurnStart } - -// User input request completion with the user's response -type UserInputCompletedData struct { - // The user's answer to the input request - Answer *string `json:"answer,omitempty"` - // Request ID of the resolved user input request; clients should dismiss any UI for this request - RequestID string `json:"requestId"` - // Whether the answer was typed as free-form text rather than selected from choices - WasFreeform *bool `json:"wasFreeform,omitempty"` -} - -func (*UserInputCompletedData) sessionEventData() {} -func (*UserInputCompletedData) Type() SessionEventType { return SessionEventTypeUserInputCompleted } - -// User input request notification with question and optional predefined choices -type UserInputRequestedData struct { - // Whether the user can provide a free-form text response in addition to predefined choices - AllowFreeform *bool `json:"allowFreeform,omitempty"` - // Predefined choices for the user to select from, if applicable - Choices []string `json:"choices,omitempty"` - // The question or prompt to present to the user - Question string `json:"question"` - // Unique identifier for this input request; used to respond via session.respondToUserInput() - RequestID string `json:"requestId"` - // The LLM-assigned tool call ID that triggered this request; used by remote UIs to correlate responses - ToolCallID *string `json:"toolCallId,omitempty"` -} - -func (*UserInputRequestedData) sessionEventData() {} -func (*UserInputRequestedData) Type() SessionEventType { return SessionEventTypeUserInputRequested } - -// User-initiated tool invocation request with tool name and arguments -type ToolUserRequestedData struct { - // Arguments for the tool invocation - Arguments any `json:"arguments,omitempty"` - // Unique identifier for this tool call - ToolCallID string `json:"toolCallId"` - // Name of the tool the user wants to invoke - ToolName string `json:"toolName"` -} - -func (*ToolUserRequestedData) sessionEventData() {} -func (*ToolUserRequestedData) Type() SessionEventType { return SessionEventTypeToolUserRequested } - -// UserMessageData holds the payload for user.message events. -type UserMessageData struct { - // The agent mode that was active when this message was sent - AgentMode *UserMessageAgentMode `json:"agentMode,omitempty"` - // Files, selections, or GitHub references attached to the message - Attachments []UserMessageAttachment `json:"attachments,omitempty"` - // The user's message text as displayed in the timeline - Content string `json:"content"` - // CAPI interaction ID for correlating this user message with its turn - InteractionID *string `json:"interactionId,omitempty"` - // True when this user message was auto-injected by autopilot's continuation loop rather than typed by the user; used to distinguish autopilot-driven turns in telemetry. - IsAutopilotContinuation *bool `json:"isAutopilotContinuation,omitempty"` - // Path-backed native document attachments that stayed on the tagged_files path flow because native upload would exceed the request size limit - NativeDocumentPathFallbackPaths []string `json:"nativeDocumentPathFallbackPaths,omitempty"` - // 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) - Source *string `json:"source,omitempty"` - // Normalized document MIME types that were sent natively instead of through tagged_files XML - SupportedNativeDocumentMIMETypes []string `json:"supportedNativeDocumentMimeTypes,omitempty"` - // Transformed version of the message sent to the model, with XML wrapping, timestamps, and other augmentations for prompt caching - TransformedContent *string `json:"transformedContent,omitempty"` -} - -func (*UserMessageData) sessionEventData() {} -func (*UserMessageData) Type() SessionEventType { return SessionEventTypeUserMessage } - -// Warning message for timeline display with categorization -type SessionWarningData struct { - // Human-readable warning message for display in the timeline - Message string `json:"message"` - // Optional URL associated with this warning that the user can open in a browser - URL *string `json:"url,omitempty"` - // Category of warning (e.g., "subscription", "policy", "mcp") - WarningType string `json:"warningType"` -} - -func (*SessionWarningData) sessionEventData() {} -func (*SessionWarningData) Type() SessionEventType { return SessionEventTypeSessionWarning } - -// Working directory and git context at session start -type SessionContextChangedData struct { - // Base commit of current git branch at session start time - BaseCommit *string `json:"baseCommit,omitempty"` - // Current git branch name - Branch *string `json:"branch,omitempty"` - // Current working directory path - Cwd string `json:"cwd"` - // Root directory of the git repository, resolved via git rev-parse - GitRoot *string `json:"gitRoot,omitempty"` - // Head commit of current git branch at session start time - HeadCommit *string `json:"headCommit,omitempty"` - // Hosting platform type of the repository (github or ado) - HostType *WorkingDirectoryContextHostType `json:"hostType,omitempty"` - // Repository identifier derived from the git remote URL ("owner/name" for GitHub, "org/project/repo" for Azure DevOps) - Repository *string `json:"repository,omitempty"` - // Raw host string from the git remote URL (e.g. "github.com", "mycompany.ghe.com", "dev.azure.com") - RepositoryHost *string `json:"repositoryHost,omitempty"` -} - -func (*SessionContextChangedData) sessionEventData() {} -func (*SessionContextChangedData) Type() SessionEventType { - return SessionEventTypeSessionContextChanged -} - -// Workspace file change details including path and operation type -type SessionWorkspaceFileChangedData struct { - // Whether the file was newly created or updated - Operation WorkspaceFileChangedOperation `json:"operation"` - // Relative path within the session workspace files directory - Path string `json:"path"` -} - -func (*SessionWorkspaceFileChangedData) sessionEventData() {} -func (*SessionWorkspaceFileChangedData) Type() SessionEventType { - return SessionEventTypeSessionWorkspaceFileChanged -} - -// A tool invocation request from the assistant -type AssistantMessageToolRequest struct { - // Arguments to pass to the tool, format depends on the tool - Arguments any `json:"arguments,omitempty"` - // Resolved intention summary describing what this specific call does - IntentionSummary *string `json:"intentionSummary,omitempty"` - // Name of the MCP server hosting this tool, when the tool is an MCP tool - McpServerName *string `json:"mcpServerName,omitempty"` - // Original tool name on the MCP server, when the tool is an MCP tool - McpToolName *string `json:"mcpToolName,omitempty"` - // Name of the tool being invoked - Name string `json:"name"` - // Unique identifier for this tool call - ToolCallID string `json:"toolCallId"` - // Human-readable display title for the tool - ToolTitle *string `json:"toolTitle,omitempty"` - // Tool call type: "function" for standard tool calls, "custom" for grammar-based tool calls. Defaults to "function" when absent. - Type *AssistantMessageToolRequestType `json:"type,omitempty"` -} - -// Per-request cost and usage data from the CAPI copilot_usage response field -type AssistantUsageCopilotUsage struct { - // Itemized token usage breakdown - TokenDetails []AssistantUsageCopilotUsageTokenDetail `json:"tokenDetails"` - // Total cost in nano-AI units for this request - TotalNanoAiu float64 `json:"totalNanoAiu"` -} - -// Token usage detail for a single billing category -type AssistantUsageCopilotUsageTokenDetail struct { - // Number of tokens in this billing batch - BatchSize float64 `json:"batchSize"` - // Cost per batch of tokens - CostPerBatch float64 `json:"costPerBatch"` - // Total token count for this entry - TokenCount float64 `json:"tokenCount"` - // Token category (e.g., "input", "output") - TokenType string `json:"tokenType"` -} - -type AssistantUsageQuotaSnapshot struct { - // Total requests allowed by the entitlement - EntitlementRequests float64 `json:"entitlementRequests"` - // Whether the user has an unlimited usage entitlement - IsUnlimitedEntitlement bool `json:"isUnlimitedEntitlement"` - // Number of requests over the entitlement limit - Overage float64 `json:"overage"` - // Whether overage is allowed when quota is exhausted - OverageAllowedWithExhaustedQuota bool `json:"overageAllowedWithExhaustedQuota"` - // Percentage of quota remaining (0.0 to 1.0) - RemainingPercentage float64 `json:"remainingPercentage"` - // Date when the quota resets - ResetDate *time.Time `json:"resetDate,omitempty"` - // Whether usage is still permitted after quota exhaustion - UsageAllowedWithExhaustedQuota bool `json:"usageAllowedWithExhaustedQuota"` - // Number of requests already consumed - UsedRequests float64 `json:"usedRequests"` -} - -// UI capability changes -type CapabilitiesChangedUI struct { - // Whether elicitation is now supported - Elicitation *bool `json:"elicitation,omitempty"` -} - -type CommandsChangedCommand struct { - Description *string `json:"description,omitempty"` - Name string `json:"name"` -} - -// Token usage breakdown for the compaction LLM call (aligned with assistant.usage format) -type CompactionCompleteCompactionTokensUsed struct { - // Cached input tokens reused in the compaction LLM call - CacheReadTokens *float64 `json:"cacheReadTokens,omitempty"` - // Tokens written to prompt cache in the compaction LLM call - CacheWriteTokens *float64 `json:"cacheWriteTokens,omitempty"` - // Per-request cost and usage data from the CAPI copilot_usage response field - CopilotUsage *CompactionCompleteCompactionTokensUsedCopilotUsage `json:"copilotUsage,omitempty"` - // Duration of the compaction LLM call in milliseconds - Duration *float64 `json:"duration,omitempty"` - // Input tokens consumed by the compaction LLM call - InputTokens *float64 `json:"inputTokens,omitempty"` - // Model identifier used for the compaction LLM call - Model *string `json:"model,omitempty"` - // Output tokens produced by the compaction LLM call - OutputTokens *float64 `json:"outputTokens,omitempty"` -} - -// Per-request cost and usage data from the CAPI copilot_usage response field -type CompactionCompleteCompactionTokensUsedCopilotUsage struct { - // Itemized token usage breakdown - TokenDetails []CompactionCompleteCompactionTokensUsedCopilotUsageTokenDetail `json:"tokenDetails"` - // Total cost in nano-AI units for this request - TotalNanoAiu float64 `json:"totalNanoAiu"` -} - -// Token usage detail for a single billing category -type CompactionCompleteCompactionTokensUsedCopilotUsageTokenDetail struct { - // Number of tokens in this billing batch - BatchSize float64 `json:"batchSize"` - // Cost per batch of tokens - CostPerBatch float64 `json:"costPerBatch"` - // Total token count for this entry - TokenCount float64 `json:"tokenCount"` - // Token category (e.g., "input", "output") - TokenType string `json:"tokenType"` -} - -type CustomAgentsUpdatedAgent struct { - // Description of what the agent does - Description string `json:"description"` - // Human-readable display name - DisplayName string `json:"displayName"` - // Unique identifier for the agent - ID string `json:"id"` - // Model override for this agent, if set - Model *string `json:"model,omitempty"` - // Internal name of the agent - Name string `json:"name"` - // Source location: user, project, inherited, remote, or plugin - Source string `json:"source"` - // List of tool names available to this agent, or null when all tools are available - Tools []string `json:"tools"` - // Whether the agent can be selected by the user - UserInvocable bool `json:"userInvocable"` -} - -type ElicitationCompletedContent interface { - elicitationCompletedContent() -} - -type ElicitationCompletedBooleanContent bool - -func (ElicitationCompletedBooleanContent) elicitationCompletedContent() {} - -type ElicitationCompletedNumberContent float64 - -func (ElicitationCompletedNumberContent) elicitationCompletedContent() {} - -type ElicitationCompletedStringArrayContent []string - -func (ElicitationCompletedStringArrayContent) elicitationCompletedContent() {} - -type ElicitationCompletedStringContent string - -func (ElicitationCompletedStringContent) elicitationCompletedContent() {} - -// JSON Schema describing the form fields to present to the user (form mode only) -type ElicitationRequestedSchema struct { - // Form field definitions, keyed by field name - Properties map[string]any `json:"properties"` - // List of required field names - Required []string `json:"required,omitempty"` - // Schema type indicator (always 'object') - Type ElicitationRequestedSchemaType `json:"type"` -} - -type ExtensionsLoadedExtension struct { - // Source-qualified extension ID (e.g., 'project:my-ext', 'user:auth-helper') - ID string `json:"id"` - // Extension name (directory name) - Name string `json:"name"` - // Discovery source - Source ExtensionsLoadedExtensionSource `json:"source"` - // Current status: running, disabled, failed, or starting - Status ExtensionsLoadedExtensionStatus `json:"status"` -} - -// Repository context for the handed-off session -type HandoffRepository struct { - // Git branch name, if applicable - Branch *string `json:"branch,omitempty"` - // Repository name - Name string `json:"name"` - // Repository owner (user or organization) - Owner string `json:"owner"` -} - -// Error details when the hook failed -type HookEndError struct { - // Human-readable error message - Message string `json:"message"` - // Error stack trace, when available - Stack *string `json:"stack,omitempty"` -} - -// Static OAuth client configuration, if the server specifies one -type McpOauthRequiredStaticClientConfig struct { - // OAuth client ID for the server - ClientID string `json:"clientId"` - // Optional non-default OAuth grant type. When set to 'client_credentials', the OAuth flow runs headlessly using the client_id + keychain-stored secret (no browser, no callback server). - GrantType *McpOauthRequiredStaticClientConfigGrantType `json:"grantType,omitempty"` - // Whether this is a public OAuth client - PublicClient *bool `json:"publicClient,omitempty"` -} - -type McpServersLoadedServer struct { - // Error message if the server failed to connect - Error *string `json:"error,omitempty"` - // Server name (config key) - Name string `json:"name"` - // Configuration source: user, workspace, plugin, or builtin - Source *string `json:"source,omitempty"` - // Connection status: connected, failed, needs-auth, pending, disabled, or not_configured - Status McpServersLoadedServerStatus `json:"status"` -} - -// Derived user-facing permission prompt details for UI consumers -type PermissionPromptRequest interface { - permissionPromptRequest() - Kind() PermissionPromptRequestKind -} - -type RawPermissionPromptRequest struct { - Discriminator PermissionPromptRequestKind - Raw json.RawMessage -} - -func (RawPermissionPromptRequest) permissionPromptRequest() {} -func (r RawPermissionPromptRequest) Kind() PermissionPromptRequestKind { - return r.Discriminator -} - -// Shell command permission prompt -type PermissionPromptRequestCommands struct { - // Whether the UI can offer session-wide approval for this command pattern - CanOfferSessionApproval bool `json:"canOfferSessionApproval"` - // Command identifiers covered by this approval prompt - CommandIdentifiers []string `json:"commandIdentifiers"` - // The complete shell command text to be executed - FullCommandText string `json:"fullCommandText"` - // Human-readable description of what the command intends to do - Intention string `json:"intention"` - // Tool call ID that triggered this permission request - ToolCallID *string `json:"toolCallId,omitempty"` - // Optional warning message about risks of running this command - Warning *string `json:"warning,omitempty"` -} - -func (PermissionPromptRequestCommands) permissionPromptRequest() {} -func (PermissionPromptRequestCommands) Kind() PermissionPromptRequestKind { - return PermissionPromptRequestKindCommands -} - -// Custom tool invocation permission prompt -type PermissionPromptRequestCustomTool struct { - // Arguments to pass to the custom tool - Args any `json:"args,omitempty"` - // Tool call ID that triggered this permission request - ToolCallID *string `json:"toolCallId,omitempty"` - // Description of what the custom tool does - ToolDescription string `json:"toolDescription"` - // Name of the custom tool - ToolName string `json:"toolName"` -} - -func (PermissionPromptRequestCustomTool) permissionPromptRequest() {} -func (PermissionPromptRequestCustomTool) Kind() PermissionPromptRequestKind { - return PermissionPromptRequestKindCustomTool -} - -// Extension management permission prompt -type PermissionPromptRequestExtensionManagement struct { - // Name of the extension being managed - ExtensionName *string `json:"extensionName,omitempty"` - // The extension management operation (scaffold, reload) - Operation string `json:"operation"` - // Tool call ID that triggered this permission request - ToolCallID *string `json:"toolCallId,omitempty"` -} - -func (PermissionPromptRequestExtensionManagement) permissionPromptRequest() {} -func (PermissionPromptRequestExtensionManagement) Kind() PermissionPromptRequestKind { - return PermissionPromptRequestKindExtensionManagement -} - -// Extension permission access prompt -type PermissionPromptRequestExtensionPermissionAccess struct { - // Capabilities the extension is requesting - Capabilities []string `json:"capabilities"` - // Name of the extension requesting permission access - ExtensionName string `json:"extensionName"` - // Tool call ID that triggered this permission request - ToolCallID *string `json:"toolCallId,omitempty"` -} - -func (PermissionPromptRequestExtensionPermissionAccess) permissionPromptRequest() {} -func (PermissionPromptRequestExtensionPermissionAccess) Kind() PermissionPromptRequestKind { - return PermissionPromptRequestKindExtensionPermissionAccess -} - -// Hook confirmation permission prompt -type PermissionPromptRequestHook struct { - // Optional message from the hook explaining why confirmation is needed - HookMessage *string `json:"hookMessage,omitempty"` - // Arguments of the tool call being gated - ToolArgs any `json:"toolArgs,omitempty"` - // Tool call ID that triggered this permission request - ToolCallID *string `json:"toolCallId,omitempty"` - // Name of the tool the hook is gating - ToolName string `json:"toolName"` -} - -func (PermissionPromptRequestHook) permissionPromptRequest() {} -func (PermissionPromptRequestHook) Kind() PermissionPromptRequestKind { - return PermissionPromptRequestKindHook -} - -// MCP tool invocation permission prompt -type PermissionPromptRequestMcp struct { - // Arguments to pass to the MCP tool - Args *any `json:"args,omitempty"` - // Name of the MCP server providing the tool - ServerName string `json:"serverName"` - // Tool call ID that triggered this permission request - ToolCallID *string `json:"toolCallId,omitempty"` - // Internal name of the MCP tool - ToolName string `json:"toolName"` - // Human-readable title of the MCP tool - ToolTitle string `json:"toolTitle"` -} - -func (PermissionPromptRequestMcp) permissionPromptRequest() {} -func (PermissionPromptRequestMcp) Kind() PermissionPromptRequestKind { - return PermissionPromptRequestKindMcp -} - -// Memory operation permission prompt -type PermissionPromptRequestMemory struct { - // Whether this is a store or vote memory operation - Action *PermissionPromptRequestMemoryAction `json:"action,omitempty"` - // Source references for the stored fact (store only) - Citations *string `json:"citations,omitempty"` - // Vote direction (vote only) - Direction *PermissionPromptRequestMemoryDirection `json:"direction,omitempty"` - // The fact being stored or voted on - Fact string `json:"fact"` - // Reason for the vote (vote only) - Reason *string `json:"reason,omitempty"` - // Topic or subject of the memory (store only) - Subject *string `json:"subject,omitempty"` - // Tool call ID that triggered this permission request - ToolCallID *string `json:"toolCallId,omitempty"` -} - -func (PermissionPromptRequestMemory) permissionPromptRequest() {} -func (PermissionPromptRequestMemory) Kind() PermissionPromptRequestKind { - return PermissionPromptRequestKindMemory -} - -// Path access permission prompt -type PermissionPromptRequestPath struct { - // Underlying permission kind that needs path approval - AccessKind PermissionPromptRequestPathAccessKind `json:"accessKind"` - // File paths that require explicit approval - Paths []string `json:"paths"` - // Tool call ID that triggered this permission request - ToolCallID *string `json:"toolCallId,omitempty"` -} - -func (PermissionPromptRequestPath) permissionPromptRequest() {} -func (PermissionPromptRequestPath) Kind() PermissionPromptRequestKind { - return PermissionPromptRequestKindPath -} - -// File read permission prompt -type PermissionPromptRequestRead struct { - // Human-readable description of why the file is being read - Intention string `json:"intention"` - // Path of the file or directory being read - Path string `json:"path"` - // Tool call ID that triggered this permission request - ToolCallID *string `json:"toolCallId,omitempty"` -} - -func (PermissionPromptRequestRead) permissionPromptRequest() {} -func (PermissionPromptRequestRead) Kind() PermissionPromptRequestKind { - return PermissionPromptRequestKindRead -} - -// URL access permission prompt -type PermissionPromptRequestURL struct { - // Human-readable description of why the URL is being accessed - Intention string `json:"intention"` - // Tool call ID that triggered this permission request - ToolCallID *string `json:"toolCallId,omitempty"` - // URL to be fetched - URL string `json:"url"` -} - -func (PermissionPromptRequestURL) permissionPromptRequest() {} -func (PermissionPromptRequestURL) Kind() PermissionPromptRequestKind { - return PermissionPromptRequestKindURL -} - -// File write permission prompt -type PermissionPromptRequestWrite struct { - // Whether the UI can offer session-wide approval for file write operations - CanOfferSessionApproval bool `json:"canOfferSessionApproval"` - // Unified diff showing the proposed changes - Diff string `json:"diff"` - // Path of the file being written to - FileName string `json:"fileName"` - // Human-readable description of the intended file change - Intention string `json:"intention"` - // Complete new file contents for newly created files - NewFileContents *string `json:"newFileContents,omitempty"` - // Tool call ID that triggered this permission request - ToolCallID *string `json:"toolCallId,omitempty"` -} - -func (PermissionPromptRequestWrite) permissionPromptRequest() {} -func (PermissionPromptRequestWrite) Kind() PermissionPromptRequestKind { - return PermissionPromptRequestKindWrite -} - -// Details of the permission being requested -type PermissionRequest interface { - permissionRequest() - Kind() PermissionRequestKind -} - -type RawPermissionRequest struct { - Discriminator PermissionRequestKind - Raw json.RawMessage -} - -func (RawPermissionRequest) permissionRequest() {} -func (r RawPermissionRequest) Kind() PermissionRequestKind { - return r.Discriminator -} - -// Custom tool invocation permission request -type PermissionRequestCustomTool struct { - // Arguments to pass to the custom tool - Args any `json:"args,omitempty"` - // Tool call ID that triggered this permission request - ToolCallID *string `json:"toolCallId,omitempty"` - // Description of what the custom tool does - ToolDescription string `json:"toolDescription"` - // Name of the custom tool - ToolName string `json:"toolName"` -} - -func (PermissionRequestCustomTool) permissionRequest() {} -func (PermissionRequestCustomTool) Kind() PermissionRequestKind { - return PermissionRequestKindCustomTool -} - -// Extension management permission request -type PermissionRequestExtensionManagement struct { - // Name of the extension being managed - ExtensionName *string `json:"extensionName,omitempty"` - // The extension management operation (scaffold, reload) - Operation string `json:"operation"` - // Tool call ID that triggered this permission request - ToolCallID *string `json:"toolCallId,omitempty"` -} - -func (PermissionRequestExtensionManagement) permissionRequest() {} -func (PermissionRequestExtensionManagement) Kind() PermissionRequestKind { - return PermissionRequestKindExtensionManagement -} - -// Extension permission access request -type PermissionRequestExtensionPermissionAccess struct { - // Capabilities the extension is requesting - Capabilities []string `json:"capabilities"` - // Name of the extension requesting permission access - ExtensionName string `json:"extensionName"` - // Tool call ID that triggered this permission request - ToolCallID *string `json:"toolCallId,omitempty"` -} - -func (PermissionRequestExtensionPermissionAccess) permissionRequest() {} -func (PermissionRequestExtensionPermissionAccess) Kind() PermissionRequestKind { - return PermissionRequestKindExtensionPermissionAccess -} - -// Hook confirmation permission request -type PermissionRequestHook struct { - // Optional message from the hook explaining why confirmation is needed - HookMessage *string `json:"hookMessage,omitempty"` - // Arguments of the tool call being gated - ToolArgs any `json:"toolArgs,omitempty"` - // Tool call ID that triggered this permission request - ToolCallID *string `json:"toolCallId,omitempty"` - // Name of the tool the hook is gating - ToolName string `json:"toolName"` -} - -func (PermissionRequestHook) permissionRequest() {} -func (PermissionRequestHook) Kind() PermissionRequestKind { - return PermissionRequestKindHook -} - -// MCP tool invocation permission request -type PermissionRequestMcp struct { - // Arguments to pass to the MCP tool - Args any `json:"args,omitempty"` - // Whether this MCP tool is read-only (no side effects) - ReadOnly bool `json:"readOnly"` - // Name of the MCP server providing the tool - ServerName string `json:"serverName"` - // Tool call ID that triggered this permission request - ToolCallID *string `json:"toolCallId,omitempty"` - // Internal name of the MCP tool - ToolName string `json:"toolName"` - // Human-readable title of the MCP tool - ToolTitle string `json:"toolTitle"` -} - -func (PermissionRequestMcp) permissionRequest() {} -func (PermissionRequestMcp) Kind() PermissionRequestKind { - return PermissionRequestKindMcp -} - -// Memory operation permission request -type PermissionRequestMemory struct { - // Whether this is a store or vote memory operation - Action *PermissionRequestMemoryAction `json:"action,omitempty"` - // Source references for the stored fact (store only) - Citations *string `json:"citations,omitempty"` - // Vote direction (vote only) - Direction *PermissionRequestMemoryDirection `json:"direction,omitempty"` - // The fact being stored or voted on - Fact string `json:"fact"` - // Reason for the vote (vote only) - Reason *string `json:"reason,omitempty"` - // Topic or subject of the memory (store only) - Subject *string `json:"subject,omitempty"` - // Tool call ID that triggered this permission request - ToolCallID *string `json:"toolCallId,omitempty"` -} - -func (PermissionRequestMemory) permissionRequest() {} -func (PermissionRequestMemory) Kind() PermissionRequestKind { - return PermissionRequestKindMemory -} - -// File or directory read permission request -type PermissionRequestRead struct { - // Human-readable description of why the file is being read - Intention string `json:"intention"` - // Path of the file or directory being read - Path string `json:"path"` - // Tool call ID that triggered this permission request - ToolCallID *string `json:"toolCallId,omitempty"` -} - -func (PermissionRequestRead) permissionRequest() {} -func (PermissionRequestRead) Kind() PermissionRequestKind { - return PermissionRequestKindRead -} - -// Shell command permission request -type PermissionRequestShell struct { - // Whether the UI can offer session-wide approval for this command pattern - CanOfferSessionApproval bool `json:"canOfferSessionApproval"` - // Parsed command identifiers found in the command text - Commands []PermissionRequestShellCommand `json:"commands"` - // 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"` - // File paths that may be read or written by the command - PossiblePaths []string `json:"possiblePaths"` - // URLs that may be accessed by the command - PossibleUrls []PermissionRequestShellPossibleURL `json:"possibleUrls"` - // Tool call ID that triggered this permission request - ToolCallID *string `json:"toolCallId,omitempty"` - // Optional warning message about risks of running this command - Warning *string `json:"warning,omitempty"` -} - -func (PermissionRequestShell) permissionRequest() {} -func (PermissionRequestShell) Kind() PermissionRequestKind { - return PermissionRequestKindShell -} - -// URL access permission request -type PermissionRequestURL struct { - // Human-readable description of why the URL is being accessed - Intention string `json:"intention"` - // Tool call ID that triggered this permission request - ToolCallID *string `json:"toolCallId,omitempty"` - // URL to be fetched - URL string `json:"url"` -} - -func (PermissionRequestURL) permissionRequest() {} -func (PermissionRequestURL) Kind() PermissionRequestKind { - return PermissionRequestKindURL -} - -// File write permission request -type PermissionRequestWrite struct { - // Whether the UI can offer session-wide approval for file write operations - CanOfferSessionApproval bool `json:"canOfferSessionApproval"` - // Unified diff showing the proposed changes - Diff string `json:"diff"` - // Path of the file being written to - FileName string `json:"fileName"` - // Human-readable description of the intended file change - Intention string `json:"intention"` - // Complete new file contents for newly created files - NewFileContents *string `json:"newFileContents,omitempty"` - // Tool call ID that triggered this permission request - ToolCallID *string `json:"toolCallId,omitempty"` -} - -func (PermissionRequestWrite) permissionRequest() {} -func (PermissionRequestWrite) Kind() PermissionRequestKind { - return PermissionRequestKindWrite -} - -type PermissionRequestShellCommand struct { - // Command identifier (e.g., executable name) - Identifier string `json:"identifier"` - // Whether this command is read-only (no side effects) - ReadOnly bool `json:"readOnly"` -} - -type PermissionRequestShellPossibleURL struct { - // URL that may be accessed by the command - URL string `json:"url"` -} - -// The result of the permission request -type PermissionResult interface { - permissionResult() - Kind() PermissionResultKind -} +import "github.com/github/copilot-sdk/go/rpc" -type RawPermissionResult struct { - Discriminator PermissionResultKind - Raw json.RawMessage -} - -func (RawPermissionResult) permissionResult() {} -func (r RawPermissionResult) Kind() PermissionResultKind { - return r.Discriminator -} - -type PermissionApproved struct { -} - -func (PermissionApproved) permissionResult() {} -func (PermissionApproved) Kind() PermissionResultKind { - return PermissionResultKindApproved -} - -type PermissionApprovedForLocation struct { - // The approval to persist for this location - Approval UserToolSessionApproval `json:"approval"` - // The location key (git root or cwd) to persist the approval to - LocationKey string `json:"locationKey"` -} - -func (PermissionApprovedForLocation) permissionResult() {} -func (PermissionApprovedForLocation) Kind() PermissionResultKind { - return PermissionResultKindApprovedForLocation -} - -type PermissionApprovedForSession struct { - // The approval to add as a session-scoped rule - Approval UserToolSessionApproval `json:"approval"` -} - -func (PermissionApprovedForSession) permissionResult() {} -func (PermissionApprovedForSession) Kind() PermissionResultKind { - return PermissionResultKindApprovedForSession -} - -type PermissionCancelled struct { - // Optional explanation of why the request was cancelled - Reason *string `json:"reason,omitempty"` -} - -func (PermissionCancelled) permissionResult() {} -func (PermissionCancelled) Kind() PermissionResultKind { - return PermissionResultKindCancelled -} - -type PermissionDeniedByContentExclusionPolicy struct { - // Human-readable explanation of why the path was excluded - Message string `json:"message"` - // File path that triggered the exclusion - Path string `json:"path"` -} - -func (PermissionDeniedByContentExclusionPolicy) permissionResult() {} -func (PermissionDeniedByContentExclusionPolicy) Kind() PermissionResultKind { - return PermissionResultKindDeniedByContentExclusionPolicy -} - -type PermissionDeniedByPermissionRequestHook struct { - // Whether to interrupt the current agent turn - Interrupt *bool `json:"interrupt,omitempty"` - // Optional message from the hook explaining the denial - Message *string `json:"message,omitempty"` -} - -func (PermissionDeniedByPermissionRequestHook) permissionResult() {} -func (PermissionDeniedByPermissionRequestHook) Kind() PermissionResultKind { - return PermissionResultKindDeniedByPermissionRequestHook -} - -type PermissionDeniedByRules struct { - // Rules that denied the request - Rules []PermissionRule `json:"rules"` -} - -func (PermissionDeniedByRules) permissionResult() {} -func (PermissionDeniedByRules) Kind() PermissionResultKind { - return PermissionResultKindDeniedByRules -} - -type PermissionDeniedInteractivelyByUser struct { - // Optional feedback from the user explaining the denial - Feedback *string `json:"feedback,omitempty"` - // Whether to force-reject the current agent turn - ForceReject *bool `json:"forceReject,omitempty"` -} - -func (PermissionDeniedInteractivelyByUser) permissionResult() {} -func (PermissionDeniedInteractivelyByUser) Kind() PermissionResultKind { - return PermissionResultKindDeniedInteractivelyByUser -} - -type PermissionDeniedNoApprovalRuleAndCouldNotRequestFromUser struct { -} - -func (PermissionDeniedNoApprovalRuleAndCouldNotRequestFromUser) permissionResult() {} -func (PermissionDeniedNoApprovalRuleAndCouldNotRequestFromUser) Kind() PermissionResultKind { - return PermissionResultKindDeniedNoApprovalRuleAndCouldNotRequestFromUser -} - -type PermissionRule struct { - // Optional rule argument matched against the request - Argument *string `json:"argument"` - // The rule kind, such as Shell or GitHubMCP - Kind string `json:"kind"` -} - -// Aggregate code change metrics for the session -type ShutdownCodeChanges struct { - // List of file paths that were modified during the session - FilesModified []string `json:"filesModified"` - // Total number of lines added during the session - LinesAdded float64 `json:"linesAdded"` - // Total number of lines removed during the session - LinesRemoved float64 `json:"linesRemoved"` -} - -type ShutdownModelMetric struct { - // Request count and cost metrics - Requests ShutdownModelMetricRequests `json:"requests"` - // Token count details per type - TokenDetails map[string]ShutdownModelMetricTokenDetail `json:"tokenDetails,omitempty"` - // Accumulated nano-AI units cost for this model - TotalNanoAiu *float64 `json:"totalNanoAiu,omitempty"` - // Token usage breakdown - Usage ShutdownModelMetricUsage `json:"usage"` -} - -// Request count and cost metrics -type ShutdownModelMetricRequests struct { - // Cumulative cost multiplier for requests to this model - Cost float64 `json:"cost"` - // Total number of API requests made to this model - Count float64 `json:"count"` -} - -type ShutdownModelMetricTokenDetail struct { - // Accumulated token count for this token type - TokenCount float64 `json:"tokenCount"` -} - -// Token usage breakdown -type ShutdownModelMetricUsage struct { - // Total tokens read from prompt cache across all requests - CacheReadTokens float64 `json:"cacheReadTokens"` - // Total tokens written to prompt cache across all requests - CacheWriteTokens float64 `json:"cacheWriteTokens"` - // Total input tokens consumed across all requests to this model - InputTokens float64 `json:"inputTokens"` - // Total output tokens produced across all requests to this model - OutputTokens float64 `json:"outputTokens"` - // Total reasoning tokens produced across all requests to this model - ReasoningTokens *float64 `json:"reasoningTokens,omitempty"` -} - -type ShutdownTokenDetail struct { - // Accumulated token count for this token type - TokenCount float64 `json:"tokenCount"` -} - -type SkillsLoadedSkill struct { - // Description of what the skill does - Description string `json:"description"` - // Whether the skill is currently enabled - Enabled bool `json:"enabled"` - // Unique identifier for the skill - Name string `json:"name"` - // Absolute path to the skill file, if available - Path *string `json:"path,omitempty"` - // Source location type of the skill (e.g., project, personal, plugin) - Source string `json:"source"` - // Whether the skill can be invoked by the user as a slash command - UserInvocable bool `json:"userInvocable"` -} - -// Metadata about the prompt template and its construction -type SystemMessageMetadata struct { - // Version identifier of the prompt template used - PromptVersion *string `json:"promptVersion,omitempty"` - // Template variables used when constructing the prompt - Variables map[string]any `json:"variables,omitempty"` -} - -// Structured metadata identifying what triggered this notification -type SystemNotification interface { - systemNotification() - Type() SystemNotificationType -} - -type RawSystemNotification struct { - Discriminator SystemNotificationType - Raw json.RawMessage -} - -func (RawSystemNotification) systemNotification() {} -func (r RawSystemNotification) Type() SystemNotificationType { - return r.Discriminator -} - -type SystemNotificationAgentCompleted struct { - // Unique identifier of the background agent - AgentID string `json:"agentId"` - // Type of the agent (e.g., explore, task, general-purpose) - AgentType string `json:"agentType"` - // Human-readable description of the agent task - Description *string `json:"description,omitempty"` - // The full prompt given to the background agent - Prompt *string `json:"prompt,omitempty"` - // Whether the agent completed successfully or failed - Status SystemNotificationAgentCompletedStatus `json:"status"` -} - -func (SystemNotificationAgentCompleted) systemNotification() {} -func (SystemNotificationAgentCompleted) Type() SystemNotificationType { - return SystemNotificationTypeAgentCompleted -} - -type SystemNotificationAgentIdle struct { - // Unique identifier of the background agent - AgentID string `json:"agentId"` - // Type of the agent (e.g., explore, task, general-purpose) - AgentType string `json:"agentType"` - // Human-readable description of the agent task - Description *string `json:"description,omitempty"` -} - -func (SystemNotificationAgentIdle) systemNotification() {} -func (SystemNotificationAgentIdle) Type() SystemNotificationType { - return SystemNotificationTypeAgentIdle -} - -type SystemNotificationInstructionDiscovered struct { - // Human-readable label for the timeline (e.g., 'AGENTS.md from packages/billing/') - Description *string `json:"description,omitempty"` - // Relative path to the discovered instruction file - SourcePath string `json:"sourcePath"` - // Path of the file access that triggered discovery - TriggerFile string `json:"triggerFile"` - // Tool command that triggered discovery (currently always 'view') - TriggerTool string `json:"triggerTool"` -} - -func (SystemNotificationInstructionDiscovered) systemNotification() {} -func (SystemNotificationInstructionDiscovered) Type() SystemNotificationType { - return SystemNotificationTypeInstructionDiscovered -} - -type SystemNotificationNewInboxMessage struct { - // Unique identifier of the inbox entry - EntryID string `json:"entryId"` - // Human-readable name of the sender - SenderName string `json:"senderName"` - // Category of the sender (e.g., sidekick-agent, plugin, hook) - SenderType string `json:"senderType"` - // Short summary shown before the agent decides whether to read the inbox - Summary string `json:"summary"` -} - -func (SystemNotificationNewInboxMessage) systemNotification() {} -func (SystemNotificationNewInboxMessage) Type() SystemNotificationType { - return SystemNotificationTypeNewInboxMessage -} - -type SystemNotificationShellCompleted struct { - // Human-readable description of the command - Description *string `json:"description,omitempty"` - // Exit code of the shell command, if available - ExitCode *float64 `json:"exitCode,omitempty"` - // Unique identifier of the shell session - ShellID string `json:"shellId"` -} - -func (SystemNotificationShellCompleted) systemNotification() {} -func (SystemNotificationShellCompleted) Type() SystemNotificationType { - return SystemNotificationTypeShellCompleted -} - -type SystemNotificationShellDetachedCompleted struct { - // Human-readable description of the command - Description *string `json:"description,omitempty"` - // Unique identifier of the detached shell session - ShellID string `json:"shellId"` -} - -func (SystemNotificationShellDetachedCompleted) systemNotification() {} -func (SystemNotificationShellDetachedCompleted) Type() SystemNotificationType { - return SystemNotificationTypeShellDetachedCompleted -} - -// A content block within a tool result, which may be text, terminal output, image, audio, or a resource -type ToolExecutionCompleteContent interface { - toolExecutionCompleteContent() - Type() ToolExecutionCompleteContentType -} - -type RawToolExecutionCompleteContent struct { - Discriminator ToolExecutionCompleteContentType - Raw json.RawMessage -} - -func (RawToolExecutionCompleteContent) toolExecutionCompleteContent() {} -func (r RawToolExecutionCompleteContent) Type() ToolExecutionCompleteContentType { - return r.Discriminator -} - -// Audio content block with base64-encoded data -type ToolExecutionCompleteContentAudio struct { - // Base64-encoded audio data - Data string `json:"data"` - // MIME type of the audio (e.g., audio/wav, audio/mpeg) - MIMEType string `json:"mimeType"` -} - -func (ToolExecutionCompleteContentAudio) toolExecutionCompleteContent() {} -func (ToolExecutionCompleteContentAudio) Type() ToolExecutionCompleteContentType { - return ToolExecutionCompleteContentTypeAudio -} - -// Image content block with base64-encoded data -type ToolExecutionCompleteContentImage struct { - // Base64-encoded image data - Data string `json:"data"` - // MIME type of the image (e.g., image/png, image/jpeg) - MIMEType string `json:"mimeType"` -} - -func (ToolExecutionCompleteContentImage) toolExecutionCompleteContent() {} -func (ToolExecutionCompleteContentImage) Type() ToolExecutionCompleteContentType { - return ToolExecutionCompleteContentTypeImage -} - -// Embedded resource content block with inline text or binary data -type ToolExecutionCompleteContentResource struct { - // The embedded resource contents, either text or base64-encoded binary - Resource ToolExecutionCompleteContentResourceDetails `json:"resource"` -} - -func (ToolExecutionCompleteContentResource) toolExecutionCompleteContent() {} -func (ToolExecutionCompleteContentResource) Type() ToolExecutionCompleteContentType { - return ToolExecutionCompleteContentTypeResource -} - -// Resource link content block referencing an external resource -type ToolExecutionCompleteContentResourceLink struct { - // Human-readable description of the resource - Description *string `json:"description,omitempty"` - // Icons associated with this resource - Icons []ToolExecutionCompleteContentResourceLinkIcon `json:"icons,omitempty"` - // MIME type of the resource content - MIMEType *string `json:"mimeType,omitempty"` - // Resource name identifier - Name string `json:"name"` - // Size of the resource in bytes - Size *float64 `json:"size,omitempty"` - // Human-readable display title for the resource - Title *string `json:"title,omitempty"` - // URI identifying the resource - URI string `json:"uri"` -} - -func (ToolExecutionCompleteContentResourceLink) toolExecutionCompleteContent() {} -func (ToolExecutionCompleteContentResourceLink) Type() ToolExecutionCompleteContentType { - return ToolExecutionCompleteContentTypeResourceLink -} - -// Terminal/shell output content block with optional exit code and working directory -type ToolExecutionCompleteContentTerminal struct { - // Working directory where the command was executed - Cwd *string `json:"cwd,omitempty"` - // Process exit code, if the command has completed - ExitCode *float64 `json:"exitCode,omitempty"` - // Terminal/shell output text - Text string `json:"text"` -} - -func (ToolExecutionCompleteContentTerminal) toolExecutionCompleteContent() {} -func (ToolExecutionCompleteContentTerminal) Type() ToolExecutionCompleteContentType { - return ToolExecutionCompleteContentTypeTerminal -} - -// Plain text content block -type ToolExecutionCompleteContentText struct { - // The text content - Text string `json:"text"` -} - -func (ToolExecutionCompleteContentText) toolExecutionCompleteContent() {} -func (ToolExecutionCompleteContentText) Type() ToolExecutionCompleteContentType { - return ToolExecutionCompleteContentTypeText -} - -// The embedded resource contents, either text or base64-encoded binary -type ToolExecutionCompleteContentResourceDetails interface { - toolExecutionCompleteContentResourceDetails() -} - -type RawToolExecutionCompleteContentResourceDetails struct { - Raw json.RawMessage -} - -func (RawToolExecutionCompleteContentResourceDetails) toolExecutionCompleteContentResourceDetails() {} - -type EmbeddedBlobResourceContents struct { - // Base64-encoded binary content of the resource - Blob string `json:"blob"` - // MIME type of the blob content - MIMEType *string `json:"mimeType,omitempty"` - // URI identifying the resource - URI string `json:"uri"` -} - -func (EmbeddedBlobResourceContents) toolExecutionCompleteContentResourceDetails() {} - -type EmbeddedTextResourceContents struct { - // MIME type of the text content - MIMEType *string `json:"mimeType,omitempty"` - // Text content of the resource - Text string `json:"text"` - // URI identifying the resource - URI string `json:"uri"` -} - -func (EmbeddedTextResourceContents) toolExecutionCompleteContentResourceDetails() {} - -// Icon image for a resource -type ToolExecutionCompleteContentResourceLinkIcon struct { - // MIME type of the icon image - MIMEType *string `json:"mimeType,omitempty"` - // Available icon sizes (e.g., ['16x16', '32x32']) - Sizes []string `json:"sizes,omitempty"` - // URL or path to the icon image - Src string `json:"src"` - // Theme variant this icon is intended for - Theme *ToolExecutionCompleteContentResourceLinkIconTheme `json:"theme,omitempty"` -} - -// Error details when the tool execution failed -type ToolExecutionCompleteError struct { - // Machine-readable error code - Code *string `json:"code,omitempty"` - // Human-readable error message - Message string `json:"message"` -} - -// Tool execution result on success -type ToolExecutionCompleteResult struct { - // Concise tool result text sent to the LLM for chat completion, potentially truncated for token efficiency - Content string `json:"content"` - // Structured content blocks (text, images, audio, resources) returned by the tool in their native format - Contents []ToolExecutionCompleteContent `json:"contents,omitempty"` - // Full detailed tool result for UI/timeline display, preserving complete content such as diffs. Falls back to content when absent. - DetailedContent *string `json:"detailedContent,omitempty"` -} - -// A user message attachment — a file, directory, code selection, blob, or GitHub reference -type UserMessageAttachment interface { - userMessageAttachment() - Type() UserMessageAttachmentType -} - -type RawUserMessageAttachment struct { - Discriminator UserMessageAttachmentType - Raw json.RawMessage -} - -func (RawUserMessageAttachment) userMessageAttachment() {} -func (r RawUserMessageAttachment) Type() UserMessageAttachmentType { - return r.Discriminator -} - -// Blob attachment with inline base64-encoded data -type UserMessageAttachmentBlob struct { - // Base64-encoded content - Data string `json:"data"` - // User-facing display name for the attachment - DisplayName *string `json:"displayName,omitempty"` - // MIME type of the inline data - MIMEType string `json:"mimeType"` -} - -func (UserMessageAttachmentBlob) userMessageAttachment() {} -func (UserMessageAttachmentBlob) Type() UserMessageAttachmentType { - return UserMessageAttachmentTypeBlob -} - -// Directory attachment -type UserMessageAttachmentDirectory struct { - // User-facing display name for the attachment - DisplayName string `json:"displayName"` - // Absolute directory path - Path string `json:"path"` -} - -func (UserMessageAttachmentDirectory) userMessageAttachment() {} -func (UserMessageAttachmentDirectory) Type() UserMessageAttachmentType { - return UserMessageAttachmentTypeDirectory -} - -// File attachment -type UserMessageAttachmentFile struct { - // User-facing display name for the attachment - DisplayName string `json:"displayName"` - // Optional line range to scope the attachment to a specific section of the file - LineRange *UserMessageAttachmentFileLineRange `json:"lineRange,omitempty"` - // Absolute file path - Path string `json:"path"` -} - -func (UserMessageAttachmentFile) userMessageAttachment() {} -func (UserMessageAttachmentFile) Type() UserMessageAttachmentType { - return UserMessageAttachmentTypeFile -} - -// GitHub issue, pull request, or discussion reference -type UserMessageAttachmentGithubReference struct { - // Issue, pull request, or discussion number - Number float64 `json:"number"` - // Type of GitHub reference - ReferenceType UserMessageAttachmentGithubReferenceType `json:"referenceType"` - // Current state of the referenced item (e.g., open, closed, merged) - State string `json:"state"` - // Title of the referenced item - Title string `json:"title"` - // URL to the referenced item on GitHub - URL string `json:"url"` -} - -func (UserMessageAttachmentGithubReference) userMessageAttachment() {} -func (UserMessageAttachmentGithubReference) Type() UserMessageAttachmentType { - return UserMessageAttachmentTypeGithubReference -} - -// Code selection attachment from an editor -type UserMessageAttachmentSelection struct { - // User-facing display name for the selection - DisplayName string `json:"displayName"` - // Absolute path to the file containing the selection - FilePath string `json:"filePath"` - // Position range of the selection within the file - Selection UserMessageAttachmentSelectionDetails `json:"selection"` - // The selected text content - Text string `json:"text"` -} - -func (UserMessageAttachmentSelection) userMessageAttachment() {} -func (UserMessageAttachmentSelection) Type() UserMessageAttachmentType { - return UserMessageAttachmentTypeSelection -} - -// Optional line range to scope the attachment to a specific section of the file -type UserMessageAttachmentFileLineRange struct { - // End line number (1-based, inclusive) - End float64 `json:"end"` - // Start line number (1-based) - Start float64 `json:"start"` -} - -// Position range of the selection within the file -type UserMessageAttachmentSelectionDetails struct { - // End position of the selection - End UserMessageAttachmentSelectionDetailsEnd `json:"end"` - // Start position of the selection - Start UserMessageAttachmentSelectionDetailsStart `json:"start"` -} - -// End position of the selection -type UserMessageAttachmentSelectionDetailsEnd struct { - // End character offset within the line (0-based) - Character float64 `json:"character"` - // End line number (0-based) - Line float64 `json:"line"` -} - -// Start position of the selection -type UserMessageAttachmentSelectionDetailsStart struct { - // Start character offset within the line (0-based) - Character float64 `json:"character"` - // Start line number (0-based) - Line float64 `json:"line"` -} - -// The approval to add as a session-scoped rule -type UserToolSessionApproval interface { - userToolSessionApproval() - Kind() UserToolSessionApprovalKind -} - -type RawUserToolSessionApproval struct { - Discriminator UserToolSessionApprovalKind - Raw json.RawMessage -} - -func (RawUserToolSessionApproval) userToolSessionApproval() {} -func (r RawUserToolSessionApproval) Kind() UserToolSessionApprovalKind { - return r.Discriminator -} - -type UserToolSessionApprovalCommands struct { - // Command identifiers approved by the user - CommandIdentifiers []string `json:"commandIdentifiers"` -} - -func (UserToolSessionApprovalCommands) userToolSessionApproval() {} -func (UserToolSessionApprovalCommands) Kind() UserToolSessionApprovalKind { - return UserToolSessionApprovalKindCommands -} - -type UserToolSessionApprovalCustomTool struct { - // Custom tool name - ToolName string `json:"toolName"` -} - -func (UserToolSessionApprovalCustomTool) userToolSessionApproval() {} -func (UserToolSessionApprovalCustomTool) Kind() UserToolSessionApprovalKind { - return UserToolSessionApprovalKindCustomTool -} - -type UserToolSessionApprovalExtensionManagement struct { - // Optional operation identifier - Operation *string `json:"operation,omitempty"` -} - -func (UserToolSessionApprovalExtensionManagement) userToolSessionApproval() {} -func (UserToolSessionApprovalExtensionManagement) Kind() UserToolSessionApprovalKind { - return UserToolSessionApprovalKindExtensionManagement -} - -type UserToolSessionApprovalExtensionPermissionAccess struct { - // Extension name - ExtensionName string `json:"extensionName"` -} - -func (UserToolSessionApprovalExtensionPermissionAccess) userToolSessionApproval() {} -func (UserToolSessionApprovalExtensionPermissionAccess) Kind() UserToolSessionApprovalKind { - return UserToolSessionApprovalKindExtensionPermissionAccess -} - -type UserToolSessionApprovalMcp struct { - // MCP server name - ServerName string `json:"serverName"` - // Optional MCP tool name, or null for all tools on the server - ToolName *string `json:"toolName"` -} - -func (UserToolSessionApprovalMcp) userToolSessionApproval() {} -func (UserToolSessionApprovalMcp) Kind() UserToolSessionApprovalKind { - return UserToolSessionApprovalKindMcp -} - -type UserToolSessionApprovalMemory struct { -} - -func (UserToolSessionApprovalMemory) userToolSessionApproval() {} -func (UserToolSessionApprovalMemory) Kind() UserToolSessionApprovalKind { - return UserToolSessionApprovalKindMemory -} - -type UserToolSessionApprovalRead struct { -} - -func (UserToolSessionApprovalRead) userToolSessionApproval() {} -func (UserToolSessionApprovalRead) Kind() UserToolSessionApprovalKind { - return UserToolSessionApprovalKindRead -} - -type UserToolSessionApprovalWrite struct { -} - -func (UserToolSessionApprovalWrite) userToolSessionApproval() {} -func (UserToolSessionApprovalWrite) Kind() UserToolSessionApprovalKind { - return UserToolSessionApprovalKindWrite -} - -// Working directory and git context at session start -type WorkingDirectoryContext struct { - // Base commit of current git branch at session start time - BaseCommit *string `json:"baseCommit,omitempty"` - // Current git branch name - Branch *string `json:"branch,omitempty"` - // Current working directory path - Cwd string `json:"cwd"` - // Root directory of the git repository, resolved via git rev-parse - GitRoot *string `json:"gitRoot,omitempty"` - // Head commit of current git branch at session start time - HeadCommit *string `json:"headCommit,omitempty"` - // Hosting platform type of the repository (github or ado) - HostType *WorkingDirectoryContextHostType `json:"hostType,omitempty"` - // Repository identifier derived from the git remote URL ("owner/name" for GitHub, "org/project/repo" for Azure DevOps) - Repository *string `json:"repository,omitempty"` - // Raw host string from the git remote URL (e.g. "github.com", "mycompany.ghe.com", "dev.azure.com") - RepositoryHost *string `json:"repositoryHost,omitempty"` -} - -// Finite reason code describing why the current turn was aborted -type AbortReason string - -const ( - AbortReasonRemoteCommand AbortReason = "remote_command" - AbortReasonUserAbort AbortReason = "user_abort" - AbortReasonUserInitiated AbortReason = "user_initiated" -) - -// Tool call type: "function" for standard tool calls, "custom" for grammar-based tool calls. Defaults to "function" when absent. -type AssistantMessageToolRequestType string - -const ( - AssistantMessageToolRequestTypeCustom AssistantMessageToolRequestType = "custom" - AssistantMessageToolRequestTypeFunction AssistantMessageToolRequestType = "function" -) - -// API endpoint used for this model call, matching CAPI supported_endpoints vocabulary -type AssistantUsageAPIEndpoint string - -const ( - AssistantUsageAPIEndpointChatCompletions AssistantUsageAPIEndpoint = "/chat/completions" - AssistantUsageAPIEndpointResponses AssistantUsageAPIEndpoint = "/responses" - AssistantUsageAPIEndpointV1Messages AssistantUsageAPIEndpoint = "/v1/messages" - AssistantUsageAPIEndpointWsResponses AssistantUsageAPIEndpoint = "ws:/responses" -) - -// The user action: "accept" (submitted form), "decline" (explicitly refused), or "cancel" (dismissed) -type ElicitationCompletedAction string - -const ( - ElicitationCompletedActionAccept ElicitationCompletedAction = "accept" - ElicitationCompletedActionCancel ElicitationCompletedAction = "cancel" - ElicitationCompletedActionDecline ElicitationCompletedAction = "decline" -) - -// Elicitation mode; "form" for structured input, "url" for browser-based. Defaults to "form" when absent. -type ElicitationRequestedMode string - -const ( - ElicitationRequestedModeForm ElicitationRequestedMode = "form" - ElicitationRequestedModeURL ElicitationRequestedMode = "url" -) - -// Schema type indicator (always 'object') -type ElicitationRequestedSchemaType string - -const ( - ElicitationRequestedSchemaTypeObject ElicitationRequestedSchemaType = "object" -) - -// Discovery source -type ExtensionsLoadedExtensionSource string - -const ( - ExtensionsLoadedExtensionSourceProject ExtensionsLoadedExtensionSource = "project" - ExtensionsLoadedExtensionSourceUser ExtensionsLoadedExtensionSource = "user" -) - -// Current status: running, disabled, failed, or starting -type ExtensionsLoadedExtensionStatus string - -const ( - ExtensionsLoadedExtensionStatusDisabled ExtensionsLoadedExtensionStatus = "disabled" - ExtensionsLoadedExtensionStatusFailed ExtensionsLoadedExtensionStatus = "failed" - ExtensionsLoadedExtensionStatusRunning ExtensionsLoadedExtensionStatus = "running" - ExtensionsLoadedExtensionStatusStarting ExtensionsLoadedExtensionStatus = "starting" -) - -// Origin type of the session being handed off -type HandoffSourceType string - -const ( - HandoffSourceTypeLocal HandoffSourceType = "local" - HandoffSourceTypeRemote HandoffSourceType = "remote" -) - -// Optional non-default OAuth grant type. When set to 'client_credentials', the OAuth flow runs headlessly using the client_id + keychain-stored secret (no browser, no callback server). -type McpOauthRequiredStaticClientConfigGrantType string - -const ( - McpOauthRequiredStaticClientConfigGrantTypeClientCredentials McpOauthRequiredStaticClientConfigGrantType = "client_credentials" -) - -// Connection status: connected, failed, needs-auth, pending, disabled, or not_configured -type McpServersLoadedServerStatus string - -const ( - McpServersLoadedServerStatusConnected McpServersLoadedServerStatus = "connected" - McpServersLoadedServerStatusDisabled McpServersLoadedServerStatus = "disabled" - McpServersLoadedServerStatusFailed McpServersLoadedServerStatus = "failed" - McpServersLoadedServerStatusNeedsAuth McpServersLoadedServerStatus = "needs-auth" - McpServersLoadedServerStatusNotConfigured McpServersLoadedServerStatus = "not_configured" - McpServersLoadedServerStatusPending McpServersLoadedServerStatus = "pending" -) - -// New connection status: connected, failed, needs-auth, pending, disabled, or not_configured -type McpServerStatusChangedStatus string - -const ( - McpServerStatusChangedStatusConnected McpServerStatusChangedStatus = "connected" - McpServerStatusChangedStatusDisabled McpServerStatusChangedStatus = "disabled" - McpServerStatusChangedStatusFailed McpServerStatusChangedStatus = "failed" - McpServerStatusChangedStatusNeedsAuth McpServerStatusChangedStatus = "needs-auth" - McpServerStatusChangedStatusNotConfigured McpServerStatusChangedStatus = "not_configured" - McpServerStatusChangedStatusPending McpServerStatusChangedStatus = "pending" -) - -// Where the failed model call originated -type ModelCallFailureSource string - -const ( - ModelCallFailureSourceMcpSampling ModelCallFailureSource = "mcp_sampling" - ModelCallFailureSourceSubagent ModelCallFailureSource = "subagent" - ModelCallFailureSourceTopLevel ModelCallFailureSource = "top_level" -) - -// Kind discriminator for PermissionPromptRequest. -type PermissionPromptRequestKind string - -const ( - PermissionPromptRequestKindCommands PermissionPromptRequestKind = "commands" - PermissionPromptRequestKindCustomTool PermissionPromptRequestKind = "custom-tool" - PermissionPromptRequestKindExtensionManagement PermissionPromptRequestKind = "extension-management" - PermissionPromptRequestKindExtensionPermissionAccess PermissionPromptRequestKind = "extension-permission-access" - PermissionPromptRequestKindHook PermissionPromptRequestKind = "hook" - PermissionPromptRequestKindMcp PermissionPromptRequestKind = "mcp" - PermissionPromptRequestKindMemory PermissionPromptRequestKind = "memory" - PermissionPromptRequestKindPath PermissionPromptRequestKind = "path" - PermissionPromptRequestKindRead PermissionPromptRequestKind = "read" - PermissionPromptRequestKindURL PermissionPromptRequestKind = "url" - PermissionPromptRequestKindWrite PermissionPromptRequestKind = "write" -) - -// Whether this is a store or vote memory operation -type PermissionPromptRequestMemoryAction string - -const ( - PermissionPromptRequestMemoryActionStore PermissionPromptRequestMemoryAction = "store" - PermissionPromptRequestMemoryActionVote PermissionPromptRequestMemoryAction = "vote" -) - -// Vote direction (vote only) -type PermissionPromptRequestMemoryDirection string - -const ( - PermissionPromptRequestMemoryDirectionDownvote PermissionPromptRequestMemoryDirection = "downvote" - PermissionPromptRequestMemoryDirectionUpvote PermissionPromptRequestMemoryDirection = "upvote" -) - -// Underlying permission kind that needs path approval -type PermissionPromptRequestPathAccessKind string - -const ( - PermissionPromptRequestPathAccessKindRead PermissionPromptRequestPathAccessKind = "read" - PermissionPromptRequestPathAccessKindShell PermissionPromptRequestPathAccessKind = "shell" - PermissionPromptRequestPathAccessKindWrite PermissionPromptRequestPathAccessKind = "write" -) - -// Kind discriminator for PermissionRequest. -type PermissionRequestKind string - -const ( - PermissionRequestKindCustomTool PermissionRequestKind = "custom-tool" - PermissionRequestKindExtensionManagement PermissionRequestKind = "extension-management" - PermissionRequestKindExtensionPermissionAccess PermissionRequestKind = "extension-permission-access" - PermissionRequestKindHook PermissionRequestKind = "hook" - PermissionRequestKindMcp PermissionRequestKind = "mcp" - PermissionRequestKindMemory PermissionRequestKind = "memory" - PermissionRequestKindRead PermissionRequestKind = "read" - PermissionRequestKindShell PermissionRequestKind = "shell" - PermissionRequestKindURL PermissionRequestKind = "url" - PermissionRequestKindWrite PermissionRequestKind = "write" -) - -// Whether this is a store or vote memory operation -type PermissionRequestMemoryAction string - -const ( - PermissionRequestMemoryActionStore PermissionRequestMemoryAction = "store" - PermissionRequestMemoryActionVote PermissionRequestMemoryAction = "vote" -) - -// Vote direction (vote only) -type PermissionRequestMemoryDirection string - -const ( - PermissionRequestMemoryDirectionDownvote PermissionRequestMemoryDirection = "downvote" - PermissionRequestMemoryDirectionUpvote PermissionRequestMemoryDirection = "upvote" -) - -// Kind discriminator for PermissionResult. -type PermissionResultKind string - -const ( - PermissionResultKindApproved PermissionResultKind = "approved" - PermissionResultKindApprovedForLocation PermissionResultKind = "approved-for-location" - PermissionResultKindApprovedForSession PermissionResultKind = "approved-for-session" - PermissionResultKindCancelled PermissionResultKind = "cancelled" - PermissionResultKindDeniedByContentExclusionPolicy PermissionResultKind = "denied-by-content-exclusion-policy" - PermissionResultKindDeniedByPermissionRequestHook PermissionResultKind = "denied-by-permission-request-hook" - PermissionResultKindDeniedByRules PermissionResultKind = "denied-by-rules" - PermissionResultKindDeniedInteractivelyByUser PermissionResultKind = "denied-interactively-by-user" - PermissionResultKindDeniedNoApprovalRuleAndCouldNotRequestFromUser PermissionResultKind = "denied-no-approval-rule-and-could-not-request-from-user" -) - -// The type of operation performed on the plan file -type PlanChangedOperation string - -const ( - PlanChangedOperationCreate PlanChangedOperation = "create" - PlanChangedOperationDelete PlanChangedOperation = "delete" - PlanChangedOperationUpdate PlanChangedOperation = "update" -) - -// Whether the session ended normally ("routine") or due to a crash/fatal error ("error") -type ShutdownType string - -const ( - ShutdownTypeError ShutdownType = "error" - ShutdownTypeRoutine ShutdownType = "routine" -) - -// Message role: "system" for system prompts, "developer" for developer-injected instructions -type SystemMessageRole string - -const ( - SystemMessageRoleDeveloper SystemMessageRole = "developer" - SystemMessageRoleSystem SystemMessageRole = "system" -) - -// Whether the agent completed successfully or failed -type SystemNotificationAgentCompletedStatus string - -const ( - SystemNotificationAgentCompletedStatusCompleted SystemNotificationAgentCompletedStatus = "completed" - SystemNotificationAgentCompletedStatusFailed SystemNotificationAgentCompletedStatus = "failed" -) - -// Type discriminator for SystemNotification. -type SystemNotificationType string - -const ( - SystemNotificationTypeAgentCompleted SystemNotificationType = "agent_completed" - SystemNotificationTypeAgentIdle SystemNotificationType = "agent_idle" - SystemNotificationTypeInstructionDiscovered SystemNotificationType = "instruction_discovered" - SystemNotificationTypeNewInboxMessage SystemNotificationType = "new_inbox_message" - SystemNotificationTypeShellCompleted SystemNotificationType = "shell_completed" - SystemNotificationTypeShellDetachedCompleted SystemNotificationType = "shell_detached_completed" -) - -// Theme variant this icon is intended for -type ToolExecutionCompleteContentResourceLinkIconTheme string - -const ( - ToolExecutionCompleteContentResourceLinkIconThemeDark ToolExecutionCompleteContentResourceLinkIconTheme = "dark" - ToolExecutionCompleteContentResourceLinkIconThemeLight ToolExecutionCompleteContentResourceLinkIconTheme = "light" -) - -// Type discriminator for ToolExecutionCompleteContent. -type ToolExecutionCompleteContentType string - -const ( - ToolExecutionCompleteContentTypeAudio ToolExecutionCompleteContentType = "audio" - ToolExecutionCompleteContentTypeImage ToolExecutionCompleteContentType = "image" - ToolExecutionCompleteContentTypeResource ToolExecutionCompleteContentType = "resource" - ToolExecutionCompleteContentTypeResourceLink ToolExecutionCompleteContentType = "resource_link" - ToolExecutionCompleteContentTypeTerminal ToolExecutionCompleteContentType = "terminal" - ToolExecutionCompleteContentTypeText ToolExecutionCompleteContentType = "text" -) - -// The agent mode that was active when this message was sent -type UserMessageAgentMode string - -const ( - UserMessageAgentModeAutopilot UserMessageAgentMode = "autopilot" - UserMessageAgentModeInteractive UserMessageAgentMode = "interactive" - UserMessageAgentModePlan UserMessageAgentMode = "plan" - UserMessageAgentModeShell UserMessageAgentMode = "shell" -) - -// Type of GitHub reference -type UserMessageAttachmentGithubReferenceType string - -const ( - UserMessageAttachmentGithubReferenceTypeDiscussion UserMessageAttachmentGithubReferenceType = "discussion" - UserMessageAttachmentGithubReferenceTypeIssue UserMessageAttachmentGithubReferenceType = "issue" - UserMessageAttachmentGithubReferenceTypePr UserMessageAttachmentGithubReferenceType = "pr" -) - -// Type discriminator for UserMessageAttachment. -type UserMessageAttachmentType string - -const ( - UserMessageAttachmentTypeBlob UserMessageAttachmentType = "blob" - UserMessageAttachmentTypeDirectory UserMessageAttachmentType = "directory" - UserMessageAttachmentTypeFile UserMessageAttachmentType = "file" - UserMessageAttachmentTypeGithubReference UserMessageAttachmentType = "github_reference" - UserMessageAttachmentTypeSelection UserMessageAttachmentType = "selection" -) - -// Kind discriminator for UserToolSessionApproval. -type UserToolSessionApprovalKind string - -const ( - UserToolSessionApprovalKindCommands UserToolSessionApprovalKind = "commands" - UserToolSessionApprovalKindCustomTool UserToolSessionApprovalKind = "custom-tool" - UserToolSessionApprovalKindExtensionManagement UserToolSessionApprovalKind = "extension-management" - UserToolSessionApprovalKindExtensionPermissionAccess UserToolSessionApprovalKind = "extension-permission-access" - UserToolSessionApprovalKindMcp UserToolSessionApprovalKind = "mcp" - UserToolSessionApprovalKindMemory UserToolSessionApprovalKind = "memory" - UserToolSessionApprovalKindRead UserToolSessionApprovalKind = "read" - UserToolSessionApprovalKindWrite UserToolSessionApprovalKind = "write" -) - -// Hosting platform type of the repository (github or ado) -type WorkingDirectoryContextHostType string - -const ( - WorkingDirectoryContextHostTypeAdo WorkingDirectoryContextHostType = "ado" - WorkingDirectoryContextHostTypeGithub WorkingDirectoryContextHostType = "github" -) - -// Whether the file was newly created or updated -type WorkspaceFileChangedOperation string - -const ( - WorkspaceFileChangedOperationCreate WorkspaceFileChangedOperation = "create" - WorkspaceFileChangedOperationUpdate WorkspaceFileChangedOperation = "update" -) - -// Type aliases for convenience. +// Session-event types are generated in the rpc package and aliased here for source compatibility. type ( - Attachment = UserMessageAttachment - AttachmentType = UserMessageAttachmentType - PermissionRequestCommand = PermissionRequestShellCommand - PossibleURL = PermissionRequestShellPossibleURL -) - -// Constant aliases for convenience. -const ( - AttachmentTypeBlob = UserMessageAttachmentTypeBlob - AttachmentTypeDirectory = UserMessageAttachmentTypeDirectory - AttachmentTypeFile = UserMessageAttachmentTypeFile - AttachmentTypeGithubReference = UserMessageAttachmentTypeGithubReference - AttachmentTypeSelection = UserMessageAttachmentTypeSelection + AbortData = rpc.AbortData + AbortReason = rpc.AbortReason + AssistantIntentData = rpc.AssistantIntentData + AssistantMessageData = rpc.AssistantMessageData + AssistantMessageDeltaData = rpc.AssistantMessageDeltaData + AssistantMessageStartData = rpc.AssistantMessageStartData + AssistantMessageToolRequest = rpc.AssistantMessageToolRequest + AssistantMessageToolRequestType = rpc.AssistantMessageToolRequestType + AssistantReasoningData = rpc.AssistantReasoningData + AssistantReasoningDeltaData = rpc.AssistantReasoningDeltaData + AssistantStreamingDeltaData = rpc.AssistantStreamingDeltaData + AssistantTurnEndData = rpc.AssistantTurnEndData + AssistantTurnStartData = rpc.AssistantTurnStartData + AssistantUsageAPIEndpoint = rpc.AssistantUsageAPIEndpoint + AssistantUsageCopilotUsage = rpc.AssistantUsageCopilotUsage + AssistantUsageCopilotUsageTokenDetail = rpc.AssistantUsageCopilotUsageTokenDetail + AssistantUsageData = rpc.AssistantUsageData + AssistantUsageQuotaSnapshot = rpc.AssistantUsageQuotaSnapshot + Attachment = rpc.Attachment + AttachmentType = rpc.AttachmentType + AutoModeSwitchCompletedData = rpc.AutoModeSwitchCompletedData + AutoModeSwitchRequestedData = rpc.AutoModeSwitchRequestedData + CapabilitiesChangedData = rpc.CapabilitiesChangedData + CapabilitiesChangedUI = rpc.CapabilitiesChangedUI + CommandCompletedData = rpc.CommandCompletedData + CommandExecuteData = rpc.CommandExecuteData + CommandQueuedData = rpc.CommandQueuedData + CommandsChangedCommand = rpc.CommandsChangedCommand + CommandsChangedData = rpc.CommandsChangedData + CompactionCompleteCompactionTokensUsed = rpc.CompactionCompleteCompactionTokensUsed + CompactionCompleteCompactionTokensUsedCopilotUsage = rpc.CompactionCompleteCompactionTokensUsedCopilotUsage + CompactionCompleteCompactionTokensUsedCopilotUsageTokenDetail = rpc.CompactionCompleteCompactionTokensUsedCopilotUsageTokenDetail + CustomAgentsUpdatedAgent = rpc.CustomAgentsUpdatedAgent + ElicitationCompletedAction = rpc.ElicitationCompletedAction + ElicitationCompletedBooleanContent = rpc.ElicitationCompletedBooleanContent + ElicitationCompletedContent = rpc.ElicitationCompletedContent + ElicitationCompletedData = rpc.ElicitationCompletedData + ElicitationCompletedNumberContent = rpc.ElicitationCompletedNumberContent + ElicitationCompletedStringArrayContent = rpc.ElicitationCompletedStringArrayContent + ElicitationCompletedStringContent = rpc.ElicitationCompletedStringContent + ElicitationRequestedData = rpc.ElicitationRequestedData + ElicitationRequestedMode = rpc.ElicitationRequestedMode + ElicitationRequestedSchema = rpc.ElicitationRequestedSchema + ElicitationRequestedSchemaType = rpc.ElicitationRequestedSchemaType + EmbeddedBlobResourceContents = rpc.EmbeddedBlobResourceContents + EmbeddedTextResourceContents = rpc.EmbeddedTextResourceContents + ExitPlanModeCompletedData = rpc.ExitPlanModeCompletedData + ExitPlanModeRequestedData = rpc.ExitPlanModeRequestedData + ExtensionsLoadedExtension = rpc.ExtensionsLoadedExtension + ExtensionsLoadedExtensionSource = rpc.ExtensionsLoadedExtensionSource + ExtensionsLoadedExtensionStatus = rpc.ExtensionsLoadedExtensionStatus + ExternalToolCompletedData = rpc.ExternalToolCompletedData + ExternalToolRequestedData = rpc.ExternalToolRequestedData + HandoffRepository = rpc.HandoffRepository + HandoffSourceType = rpc.HandoffSourceType + HookEndData = rpc.HookEndData + HookEndError = rpc.HookEndError + HookStartData = rpc.HookStartData + McpOauthCompletedData = rpc.McpOauthCompletedData + McpOauthRequiredData = rpc.McpOauthRequiredData + McpOauthRequiredStaticClientConfig = rpc.McpOauthRequiredStaticClientConfig + McpOauthRequiredStaticClientConfigGrantType = rpc.McpOauthRequiredStaticClientConfigGrantType + McpServersLoadedServer = rpc.McpServersLoadedServer + McpServersLoadedServerStatus = rpc.McpServersLoadedServerStatus + McpServerStatusChangedStatus = rpc.McpServerStatusChangedStatus + ModelCallFailureData = rpc.ModelCallFailureData + ModelCallFailureSource = rpc.ModelCallFailureSource + PendingMessagesModifiedData = rpc.PendingMessagesModifiedData + PermissionApproved = rpc.PermissionApproved + PermissionApprovedForLocation = rpc.PermissionApprovedForLocation + PermissionApprovedForSession = rpc.PermissionApprovedForSession + PermissionCancelled = rpc.PermissionCancelled + PermissionCompletedData = rpc.PermissionCompletedData + PermissionDeniedByContentExclusionPolicy = rpc.PermissionDeniedByContentExclusionPolicy + PermissionDeniedByPermissionRequestHook = rpc.PermissionDeniedByPermissionRequestHook + PermissionDeniedByRules = rpc.PermissionDeniedByRules + PermissionDeniedInteractivelyByUser = rpc.PermissionDeniedInteractivelyByUser + PermissionDeniedNoApprovalRuleAndCouldNotRequestFromUser = rpc.PermissionDeniedNoApprovalRuleAndCouldNotRequestFromUser + PermissionPromptRequest = rpc.PermissionPromptRequest + PermissionPromptRequestCommands = rpc.PermissionPromptRequestCommands + PermissionPromptRequestCustomTool = rpc.PermissionPromptRequestCustomTool + PermissionPromptRequestExtensionManagement = rpc.PermissionPromptRequestExtensionManagement + PermissionPromptRequestExtensionPermissionAccess = rpc.PermissionPromptRequestExtensionPermissionAccess + PermissionPromptRequestHook = rpc.PermissionPromptRequestHook + PermissionPromptRequestKind = rpc.PermissionPromptRequestKind + PermissionPromptRequestMcp = rpc.PermissionPromptRequestMcp + PermissionPromptRequestMemory = rpc.PermissionPromptRequestMemory + PermissionPromptRequestMemoryAction = rpc.PermissionPromptRequestMemoryAction + PermissionPromptRequestMemoryDirection = rpc.PermissionPromptRequestMemoryDirection + PermissionPromptRequestPath = rpc.PermissionPromptRequestPath + PermissionPromptRequestPathAccessKind = rpc.PermissionPromptRequestPathAccessKind + PermissionPromptRequestRead = rpc.PermissionPromptRequestRead + PermissionPromptRequestURL = rpc.PermissionPromptRequestURL + PermissionPromptRequestWrite = rpc.PermissionPromptRequestWrite + PermissionRequest = rpc.PermissionRequest + PermissionRequestCommand = rpc.PermissionRequestCommand + PermissionRequestCustomTool = rpc.PermissionRequestCustomTool + PermissionRequestedData = rpc.PermissionRequestedData + PermissionRequestExtensionManagement = rpc.PermissionRequestExtensionManagement + PermissionRequestExtensionPermissionAccess = rpc.PermissionRequestExtensionPermissionAccess + PermissionRequestHook = rpc.PermissionRequestHook + PermissionRequestKind = rpc.PermissionRequestKind + PermissionRequestMcp = rpc.PermissionRequestMcp + PermissionRequestMemory = rpc.PermissionRequestMemory + PermissionRequestMemoryAction = rpc.PermissionRequestMemoryAction + PermissionRequestMemoryDirection = rpc.PermissionRequestMemoryDirection + PermissionRequestRead = rpc.PermissionRequestRead + PermissionRequestShell = rpc.PermissionRequestShell + PermissionRequestShellCommand = rpc.PermissionRequestShellCommand + PermissionRequestShellPossibleURL = rpc.PermissionRequestShellPossibleURL + PermissionRequestURL = rpc.PermissionRequestURL + PermissionRequestWrite = rpc.PermissionRequestWrite + PermissionResult = rpc.PermissionResult + PermissionResultKind = rpc.PermissionResultKind + PermissionRule = rpc.PermissionRule + PlanChangedOperation = rpc.PlanChangedOperation + PossibleURL = rpc.PossibleURL + RawPermissionPromptRequest = rpc.RawPermissionPromptRequest + RawPermissionRequest = rpc.RawPermissionRequest + RawPermissionResult = rpc.RawPermissionResult + RawSessionEventData = rpc.RawSessionEventData + RawSystemNotification = rpc.RawSystemNotification + RawToolExecutionCompleteContent = rpc.RawToolExecutionCompleteContent + RawUserMessageAttachment = rpc.RawUserMessageAttachment + RawUserToolSessionApproval = rpc.RawUserToolSessionApproval + SamplingCompletedData = rpc.SamplingCompletedData + SamplingRequestedData = rpc.SamplingRequestedData + SessionBackgroundTasksChangedData = rpc.SessionBackgroundTasksChangedData + SessionCompactionCompleteData = rpc.SessionCompactionCompleteData + SessionCompactionStartData = rpc.SessionCompactionStartData + SessionContextChangedData = rpc.SessionContextChangedData + SessionCustomAgentsUpdatedData = rpc.SessionCustomAgentsUpdatedData + SessionErrorData = rpc.SessionErrorData + SessionEvent = rpc.SessionEvent + SessionEventData = rpc.SessionEventData + SessionEventType = rpc.SessionEventType + SessionExtensionsLoadedData = rpc.SessionExtensionsLoadedData + SessionHandoffData = rpc.SessionHandoffData + SessionIdleData = rpc.SessionIdleData + SessionInfoData = rpc.SessionInfoData + SessionMcpServersLoadedData = rpc.SessionMcpServersLoadedData + SessionMcpServerStatusChangedData = rpc.SessionMcpServerStatusChangedData + SessionModeChangedData = rpc.SessionModeChangedData + SessionModelChangeData = rpc.SessionModelChangeData + SessionPlanChangedData = rpc.SessionPlanChangedData + SessionRemoteSteerableChangedData = rpc.SessionRemoteSteerableChangedData + SessionResumeData = rpc.SessionResumeData + SessionScheduleCancelledData = rpc.SessionScheduleCancelledData + SessionScheduleCreatedData = rpc.SessionScheduleCreatedData + SessionShutdownData = rpc.SessionShutdownData + SessionSkillsLoadedData = rpc.SessionSkillsLoadedData + SessionSnapshotRewindData = rpc.SessionSnapshotRewindData + SessionStartData = rpc.SessionStartData + SessionTaskCompleteData = rpc.SessionTaskCompleteData + SessionTitleChangedData = rpc.SessionTitleChangedData + SessionToolsUpdatedData = rpc.SessionToolsUpdatedData + SessionTruncationData = rpc.SessionTruncationData + SessionUsageInfoData = rpc.SessionUsageInfoData + SessionWarningData = rpc.SessionWarningData + SessionWorkspaceFileChangedData = rpc.SessionWorkspaceFileChangedData + ShutdownCodeChanges = rpc.ShutdownCodeChanges + ShutdownModelMetric = rpc.ShutdownModelMetric + ShutdownModelMetricRequests = rpc.ShutdownModelMetricRequests + ShutdownModelMetricTokenDetail = rpc.ShutdownModelMetricTokenDetail + ShutdownModelMetricUsage = rpc.ShutdownModelMetricUsage + ShutdownTokenDetail = rpc.ShutdownTokenDetail + ShutdownType = rpc.ShutdownType + SkillInvokedData = rpc.SkillInvokedData + SkillsLoadedSkill = rpc.SkillsLoadedSkill + SubagentCompletedData = rpc.SubagentCompletedData + SubagentDeselectedData = rpc.SubagentDeselectedData + SubagentFailedData = rpc.SubagentFailedData + SubagentSelectedData = rpc.SubagentSelectedData + SubagentStartedData = rpc.SubagentStartedData + SystemMessageData = rpc.SystemMessageData + SystemMessageMetadata = rpc.SystemMessageMetadata + SystemMessageRole = rpc.SystemMessageRole + SystemNotification = rpc.SystemNotification + SystemNotificationAgentCompleted = rpc.SystemNotificationAgentCompleted + SystemNotificationAgentCompletedStatus = rpc.SystemNotificationAgentCompletedStatus + SystemNotificationAgentIdle = rpc.SystemNotificationAgentIdle + SystemNotificationData = rpc.SystemNotificationData + SystemNotificationInstructionDiscovered = rpc.SystemNotificationInstructionDiscovered + SystemNotificationNewInboxMessage = rpc.SystemNotificationNewInboxMessage + SystemNotificationShellCompleted = rpc.SystemNotificationShellCompleted + SystemNotificationShellDetachedCompleted = rpc.SystemNotificationShellDetachedCompleted + SystemNotificationType = rpc.SystemNotificationType + ToolExecutionCompleteContent = rpc.ToolExecutionCompleteContent + ToolExecutionCompleteContentAudio = rpc.ToolExecutionCompleteContentAudio + ToolExecutionCompleteContentImage = rpc.ToolExecutionCompleteContentImage + ToolExecutionCompleteContentResource = rpc.ToolExecutionCompleteContentResource + ToolExecutionCompleteContentResourceDetails = rpc.ToolExecutionCompleteContentResourceDetails + ToolExecutionCompleteContentResourceLink = rpc.ToolExecutionCompleteContentResourceLink + ToolExecutionCompleteContentResourceLinkIcon = rpc.ToolExecutionCompleteContentResourceLinkIcon + ToolExecutionCompleteContentResourceLinkIconTheme = rpc.ToolExecutionCompleteContentResourceLinkIconTheme + ToolExecutionCompleteContentTerminal = rpc.ToolExecutionCompleteContentTerminal + ToolExecutionCompleteContentText = rpc.ToolExecutionCompleteContentText + ToolExecutionCompleteContentType = rpc.ToolExecutionCompleteContentType + ToolExecutionCompleteData = rpc.ToolExecutionCompleteData + ToolExecutionCompleteError = rpc.ToolExecutionCompleteError + ToolExecutionCompleteResult = rpc.ToolExecutionCompleteResult + ToolExecutionPartialResultData = rpc.ToolExecutionPartialResultData + ToolExecutionProgressData = rpc.ToolExecutionProgressData + ToolExecutionStartData = rpc.ToolExecutionStartData + ToolUserRequestedData = rpc.ToolUserRequestedData + UserInputCompletedData = rpc.UserInputCompletedData + UserInputRequestedData = rpc.UserInputRequestedData + UserMessageAgentMode = rpc.UserMessageAgentMode + UserMessageAttachment = rpc.UserMessageAttachment + UserMessageAttachmentBlob = rpc.UserMessageAttachmentBlob + UserMessageAttachmentDirectory = rpc.UserMessageAttachmentDirectory + UserMessageAttachmentFile = rpc.UserMessageAttachmentFile + UserMessageAttachmentFileLineRange = rpc.UserMessageAttachmentFileLineRange + UserMessageAttachmentGithubReference = rpc.UserMessageAttachmentGithubReference + UserMessageAttachmentGithubReferenceType = rpc.UserMessageAttachmentGithubReferenceType + UserMessageAttachmentSelection = rpc.UserMessageAttachmentSelection + UserMessageAttachmentSelectionDetails = rpc.UserMessageAttachmentSelectionDetails + UserMessageAttachmentSelectionDetailsEnd = rpc.UserMessageAttachmentSelectionDetailsEnd + UserMessageAttachmentSelectionDetailsStart = rpc.UserMessageAttachmentSelectionDetailsStart + UserMessageAttachmentType = rpc.UserMessageAttachmentType + UserMessageData = rpc.UserMessageData + UserToolSessionApproval = rpc.UserToolSessionApproval + UserToolSessionApprovalCommands = rpc.UserToolSessionApprovalCommands + UserToolSessionApprovalCustomTool = rpc.UserToolSessionApprovalCustomTool + UserToolSessionApprovalExtensionManagement = rpc.UserToolSessionApprovalExtensionManagement + UserToolSessionApprovalExtensionPermissionAccess = rpc.UserToolSessionApprovalExtensionPermissionAccess + UserToolSessionApprovalKind = rpc.UserToolSessionApprovalKind + UserToolSessionApprovalMcp = rpc.UserToolSessionApprovalMcp + UserToolSessionApprovalMemory = rpc.UserToolSessionApprovalMemory + UserToolSessionApprovalRead = rpc.UserToolSessionApprovalRead + UserToolSessionApprovalWrite = rpc.UserToolSessionApprovalWrite + WorkingDirectoryContext = rpc.WorkingDirectoryContext + WorkingDirectoryContextHostType = rpc.WorkingDirectoryContextHostType + WorkspaceFileChangedOperation = rpc.WorkspaceFileChangedOperation +) + +// Session-event constants are generated in the rpc package and re-exported here for source compatibility. +const ( + AbortReasonRemoteCommand = rpc.AbortReasonRemoteCommand + AbortReasonUserAbort = rpc.AbortReasonUserAbort + AbortReasonUserInitiated = rpc.AbortReasonUserInitiated + AssistantMessageToolRequestTypeCustom = rpc.AssistantMessageToolRequestTypeCustom + AssistantMessageToolRequestTypeFunction = rpc.AssistantMessageToolRequestTypeFunction + AssistantUsageAPIEndpointChatCompletions = rpc.AssistantUsageAPIEndpointChatCompletions + AssistantUsageAPIEndpointResponses = rpc.AssistantUsageAPIEndpointResponses + AssistantUsageAPIEndpointV1Messages = rpc.AssistantUsageAPIEndpointV1Messages + AssistantUsageAPIEndpointWsResponses = rpc.AssistantUsageAPIEndpointWsResponses + AttachmentTypeBlob = rpc.AttachmentTypeBlob + AttachmentTypeDirectory = rpc.AttachmentTypeDirectory + AttachmentTypeFile = rpc.AttachmentTypeFile + AttachmentTypeGithubReference = rpc.AttachmentTypeGithubReference + AttachmentTypeSelection = rpc.AttachmentTypeSelection + ElicitationCompletedActionAccept = rpc.ElicitationCompletedActionAccept + ElicitationCompletedActionCancel = rpc.ElicitationCompletedActionCancel + ElicitationCompletedActionDecline = rpc.ElicitationCompletedActionDecline + ElicitationRequestedModeForm = rpc.ElicitationRequestedModeForm + ElicitationRequestedModeURL = rpc.ElicitationRequestedModeURL + ElicitationRequestedSchemaTypeObject = rpc.ElicitationRequestedSchemaTypeObject + ExtensionsLoadedExtensionSourceProject = rpc.ExtensionsLoadedExtensionSourceProject + ExtensionsLoadedExtensionSourceUser = rpc.ExtensionsLoadedExtensionSourceUser + ExtensionsLoadedExtensionStatusDisabled = rpc.ExtensionsLoadedExtensionStatusDisabled + ExtensionsLoadedExtensionStatusFailed = rpc.ExtensionsLoadedExtensionStatusFailed + ExtensionsLoadedExtensionStatusRunning = rpc.ExtensionsLoadedExtensionStatusRunning + ExtensionsLoadedExtensionStatusStarting = rpc.ExtensionsLoadedExtensionStatusStarting + HandoffSourceTypeLocal = rpc.HandoffSourceTypeLocal + HandoffSourceTypeRemote = rpc.HandoffSourceTypeRemote + McpOauthRequiredStaticClientConfigGrantTypeClientCredentials = rpc.McpOauthRequiredStaticClientConfigGrantTypeClientCredentials + McpServersLoadedServerStatusConnected = rpc.McpServersLoadedServerStatusConnected + McpServersLoadedServerStatusDisabled = rpc.McpServersLoadedServerStatusDisabled + McpServersLoadedServerStatusFailed = rpc.McpServersLoadedServerStatusFailed + McpServersLoadedServerStatusNeedsAuth = rpc.McpServersLoadedServerStatusNeedsAuth + McpServersLoadedServerStatusNotConfigured = rpc.McpServersLoadedServerStatusNotConfigured + McpServersLoadedServerStatusPending = rpc.McpServersLoadedServerStatusPending + McpServerStatusChangedStatusConnected = rpc.McpServerStatusChangedStatusConnected + McpServerStatusChangedStatusDisabled = rpc.McpServerStatusChangedStatusDisabled + McpServerStatusChangedStatusFailed = rpc.McpServerStatusChangedStatusFailed + McpServerStatusChangedStatusNeedsAuth = rpc.McpServerStatusChangedStatusNeedsAuth + McpServerStatusChangedStatusNotConfigured = rpc.McpServerStatusChangedStatusNotConfigured + McpServerStatusChangedStatusPending = rpc.McpServerStatusChangedStatusPending + ModelCallFailureSourceMcpSampling = rpc.ModelCallFailureSourceMcpSampling + ModelCallFailureSourceSubagent = rpc.ModelCallFailureSourceSubagent + ModelCallFailureSourceTopLevel = rpc.ModelCallFailureSourceTopLevel + PermissionPromptRequestKindCommands = rpc.PermissionPromptRequestKindCommands + PermissionPromptRequestKindCustomTool = rpc.PermissionPromptRequestKindCustomTool + PermissionPromptRequestKindExtensionManagement = rpc.PermissionPromptRequestKindExtensionManagement + PermissionPromptRequestKindExtensionPermissionAccess = rpc.PermissionPromptRequestKindExtensionPermissionAccess + PermissionPromptRequestKindHook = rpc.PermissionPromptRequestKindHook + PermissionPromptRequestKindMcp = rpc.PermissionPromptRequestKindMcp + PermissionPromptRequestKindMemory = rpc.PermissionPromptRequestKindMemory + PermissionPromptRequestKindPath = rpc.PermissionPromptRequestKindPath + PermissionPromptRequestKindRead = rpc.PermissionPromptRequestKindRead + PermissionPromptRequestKindURL = rpc.PermissionPromptRequestKindURL + PermissionPromptRequestKindWrite = rpc.PermissionPromptRequestKindWrite + PermissionPromptRequestMemoryActionStore = rpc.PermissionPromptRequestMemoryActionStore + PermissionPromptRequestMemoryActionVote = rpc.PermissionPromptRequestMemoryActionVote + PermissionPromptRequestMemoryDirectionDownvote = rpc.PermissionPromptRequestMemoryDirectionDownvote + PermissionPromptRequestMemoryDirectionUpvote = rpc.PermissionPromptRequestMemoryDirectionUpvote + PermissionPromptRequestPathAccessKindRead = rpc.PermissionPromptRequestPathAccessKindRead + PermissionPromptRequestPathAccessKindShell = rpc.PermissionPromptRequestPathAccessKindShell + PermissionPromptRequestPathAccessKindWrite = rpc.PermissionPromptRequestPathAccessKindWrite + PermissionRequestKindCustomTool = rpc.PermissionRequestKindCustomTool + PermissionRequestKindExtensionManagement = rpc.PermissionRequestKindExtensionManagement + PermissionRequestKindExtensionPermissionAccess = rpc.PermissionRequestKindExtensionPermissionAccess + PermissionRequestKindHook = rpc.PermissionRequestKindHook + PermissionRequestKindMcp = rpc.PermissionRequestKindMcp + PermissionRequestKindMemory = rpc.PermissionRequestKindMemory + PermissionRequestKindRead = rpc.PermissionRequestKindRead + PermissionRequestKindShell = rpc.PermissionRequestKindShell + PermissionRequestKindURL = rpc.PermissionRequestKindURL + PermissionRequestKindWrite = rpc.PermissionRequestKindWrite + PermissionRequestMemoryActionStore = rpc.PermissionRequestMemoryActionStore + PermissionRequestMemoryActionVote = rpc.PermissionRequestMemoryActionVote + PermissionRequestMemoryDirectionDownvote = rpc.PermissionRequestMemoryDirectionDownvote + PermissionRequestMemoryDirectionUpvote = rpc.PermissionRequestMemoryDirectionUpvote + PermissionResultKindApproved = rpc.PermissionResultKindApproved + PermissionResultKindApprovedForLocation = rpc.PermissionResultKindApprovedForLocation + PermissionResultKindApprovedForSession = rpc.PermissionResultKindApprovedForSession + PermissionResultKindCancelled = rpc.PermissionResultKindCancelled + PermissionResultKindDeniedByContentExclusionPolicy = rpc.PermissionResultKindDeniedByContentExclusionPolicy + PermissionResultKindDeniedByPermissionRequestHook = rpc.PermissionResultKindDeniedByPermissionRequestHook + PermissionResultKindDeniedByRules = rpc.PermissionResultKindDeniedByRules + PermissionResultKindDeniedInteractivelyByUser = rpc.PermissionResultKindDeniedInteractivelyByUser + PermissionResultKindDeniedNoApprovalRuleAndCouldNotRequestFromUser = rpc.PermissionResultKindDeniedNoApprovalRuleAndCouldNotRequestFromUser + PlanChangedOperationCreate = rpc.PlanChangedOperationCreate + PlanChangedOperationDelete = rpc.PlanChangedOperationDelete + PlanChangedOperationUpdate = rpc.PlanChangedOperationUpdate + SessionEventTypeAbort = rpc.SessionEventTypeAbort + SessionEventTypeAssistantIntent = rpc.SessionEventTypeAssistantIntent + SessionEventTypeAssistantMessage = rpc.SessionEventTypeAssistantMessage + SessionEventTypeAssistantMessageDelta = rpc.SessionEventTypeAssistantMessageDelta + SessionEventTypeAssistantMessageStart = rpc.SessionEventTypeAssistantMessageStart + SessionEventTypeAssistantReasoning = rpc.SessionEventTypeAssistantReasoning + SessionEventTypeAssistantReasoningDelta = rpc.SessionEventTypeAssistantReasoningDelta + SessionEventTypeAssistantStreamingDelta = rpc.SessionEventTypeAssistantStreamingDelta + SessionEventTypeAssistantTurnEnd = rpc.SessionEventTypeAssistantTurnEnd + SessionEventTypeAssistantTurnStart = rpc.SessionEventTypeAssistantTurnStart + SessionEventTypeAssistantUsage = rpc.SessionEventTypeAssistantUsage + SessionEventTypeAutoModeSwitchCompleted = rpc.SessionEventTypeAutoModeSwitchCompleted + SessionEventTypeAutoModeSwitchRequested = rpc.SessionEventTypeAutoModeSwitchRequested + SessionEventTypeCapabilitiesChanged = rpc.SessionEventTypeCapabilitiesChanged + SessionEventTypeCommandCompleted = rpc.SessionEventTypeCommandCompleted + SessionEventTypeCommandExecute = rpc.SessionEventTypeCommandExecute + SessionEventTypeCommandQueued = rpc.SessionEventTypeCommandQueued + SessionEventTypeCommandsChanged = rpc.SessionEventTypeCommandsChanged + SessionEventTypeElicitationCompleted = rpc.SessionEventTypeElicitationCompleted + SessionEventTypeElicitationRequested = rpc.SessionEventTypeElicitationRequested + SessionEventTypeExitPlanModeCompleted = rpc.SessionEventTypeExitPlanModeCompleted + SessionEventTypeExitPlanModeRequested = rpc.SessionEventTypeExitPlanModeRequested + SessionEventTypeExternalToolCompleted = rpc.SessionEventTypeExternalToolCompleted + SessionEventTypeExternalToolRequested = rpc.SessionEventTypeExternalToolRequested + SessionEventTypeHookEnd = rpc.SessionEventTypeHookEnd + SessionEventTypeHookStart = rpc.SessionEventTypeHookStart + SessionEventTypeMcpOauthCompleted = rpc.SessionEventTypeMcpOauthCompleted + SessionEventTypeMcpOauthRequired = rpc.SessionEventTypeMcpOauthRequired + SessionEventTypeModelCallFailure = rpc.SessionEventTypeModelCallFailure + SessionEventTypePendingMessagesModified = rpc.SessionEventTypePendingMessagesModified + SessionEventTypePermissionCompleted = rpc.SessionEventTypePermissionCompleted + SessionEventTypePermissionRequested = rpc.SessionEventTypePermissionRequested + SessionEventTypeSamplingCompleted = rpc.SessionEventTypeSamplingCompleted + SessionEventTypeSamplingRequested = rpc.SessionEventTypeSamplingRequested + SessionEventTypeSessionBackgroundTasksChanged = rpc.SessionEventTypeSessionBackgroundTasksChanged + SessionEventTypeSessionCompactionComplete = rpc.SessionEventTypeSessionCompactionComplete + SessionEventTypeSessionCompactionStart = rpc.SessionEventTypeSessionCompactionStart + SessionEventTypeSessionContextChanged = rpc.SessionEventTypeSessionContextChanged + SessionEventTypeSessionCustomAgentsUpdated = rpc.SessionEventTypeSessionCustomAgentsUpdated + SessionEventTypeSessionError = rpc.SessionEventTypeSessionError + SessionEventTypeSessionExtensionsLoaded = rpc.SessionEventTypeSessionExtensionsLoaded + SessionEventTypeSessionHandoff = rpc.SessionEventTypeSessionHandoff + SessionEventTypeSessionIdle = rpc.SessionEventTypeSessionIdle + SessionEventTypeSessionInfo = rpc.SessionEventTypeSessionInfo + SessionEventTypeSessionMcpServersLoaded = rpc.SessionEventTypeSessionMcpServersLoaded + SessionEventTypeSessionMcpServerStatusChanged = rpc.SessionEventTypeSessionMcpServerStatusChanged + SessionEventTypeSessionModeChanged = rpc.SessionEventTypeSessionModeChanged + SessionEventTypeSessionModelChange = rpc.SessionEventTypeSessionModelChange + SessionEventTypeSessionPlanChanged = rpc.SessionEventTypeSessionPlanChanged + SessionEventTypeSessionRemoteSteerableChanged = rpc.SessionEventTypeSessionRemoteSteerableChanged + SessionEventTypeSessionResume = rpc.SessionEventTypeSessionResume + SessionEventTypeSessionScheduleCancelled = rpc.SessionEventTypeSessionScheduleCancelled + SessionEventTypeSessionScheduleCreated = rpc.SessionEventTypeSessionScheduleCreated + SessionEventTypeSessionShutdown = rpc.SessionEventTypeSessionShutdown + SessionEventTypeSessionSkillsLoaded = rpc.SessionEventTypeSessionSkillsLoaded + SessionEventTypeSessionSnapshotRewind = rpc.SessionEventTypeSessionSnapshotRewind + SessionEventTypeSessionStart = rpc.SessionEventTypeSessionStart + SessionEventTypeSessionTaskComplete = rpc.SessionEventTypeSessionTaskComplete + SessionEventTypeSessionTitleChanged = rpc.SessionEventTypeSessionTitleChanged + SessionEventTypeSessionToolsUpdated = rpc.SessionEventTypeSessionToolsUpdated + SessionEventTypeSessionTruncation = rpc.SessionEventTypeSessionTruncation + SessionEventTypeSessionUsageInfo = rpc.SessionEventTypeSessionUsageInfo + SessionEventTypeSessionWarning = rpc.SessionEventTypeSessionWarning + SessionEventTypeSessionWorkspaceFileChanged = rpc.SessionEventTypeSessionWorkspaceFileChanged + SessionEventTypeSkillInvoked = rpc.SessionEventTypeSkillInvoked + SessionEventTypeSubagentCompleted = rpc.SessionEventTypeSubagentCompleted + SessionEventTypeSubagentDeselected = rpc.SessionEventTypeSubagentDeselected + SessionEventTypeSubagentFailed = rpc.SessionEventTypeSubagentFailed + SessionEventTypeSubagentSelected = rpc.SessionEventTypeSubagentSelected + SessionEventTypeSubagentStarted = rpc.SessionEventTypeSubagentStarted + SessionEventTypeSystemMessage = rpc.SessionEventTypeSystemMessage + SessionEventTypeSystemNotification = rpc.SessionEventTypeSystemNotification + SessionEventTypeToolExecutionComplete = rpc.SessionEventTypeToolExecutionComplete + SessionEventTypeToolExecutionPartialResult = rpc.SessionEventTypeToolExecutionPartialResult + SessionEventTypeToolExecutionProgress = rpc.SessionEventTypeToolExecutionProgress + SessionEventTypeToolExecutionStart = rpc.SessionEventTypeToolExecutionStart + SessionEventTypeToolUserRequested = rpc.SessionEventTypeToolUserRequested + SessionEventTypeUserInputCompleted = rpc.SessionEventTypeUserInputCompleted + SessionEventTypeUserInputRequested = rpc.SessionEventTypeUserInputRequested + SessionEventTypeUserMessage = rpc.SessionEventTypeUserMessage + ShutdownTypeError = rpc.ShutdownTypeError + ShutdownTypeRoutine = rpc.ShutdownTypeRoutine + SystemMessageRoleDeveloper = rpc.SystemMessageRoleDeveloper + SystemMessageRoleSystem = rpc.SystemMessageRoleSystem + SystemNotificationAgentCompletedStatusCompleted = rpc.SystemNotificationAgentCompletedStatusCompleted + SystemNotificationAgentCompletedStatusFailed = rpc.SystemNotificationAgentCompletedStatusFailed + SystemNotificationTypeAgentCompleted = rpc.SystemNotificationTypeAgentCompleted + SystemNotificationTypeAgentIdle = rpc.SystemNotificationTypeAgentIdle + SystemNotificationTypeInstructionDiscovered = rpc.SystemNotificationTypeInstructionDiscovered + SystemNotificationTypeNewInboxMessage = rpc.SystemNotificationTypeNewInboxMessage + SystemNotificationTypeShellCompleted = rpc.SystemNotificationTypeShellCompleted + SystemNotificationTypeShellDetachedCompleted = rpc.SystemNotificationTypeShellDetachedCompleted + ToolExecutionCompleteContentResourceLinkIconThemeDark = rpc.ToolExecutionCompleteContentResourceLinkIconThemeDark + ToolExecutionCompleteContentResourceLinkIconThemeLight = rpc.ToolExecutionCompleteContentResourceLinkIconThemeLight + ToolExecutionCompleteContentTypeAudio = rpc.ToolExecutionCompleteContentTypeAudio + ToolExecutionCompleteContentTypeImage = rpc.ToolExecutionCompleteContentTypeImage + ToolExecutionCompleteContentTypeResource = rpc.ToolExecutionCompleteContentTypeResource + ToolExecutionCompleteContentTypeResourceLink = rpc.ToolExecutionCompleteContentTypeResourceLink + ToolExecutionCompleteContentTypeTerminal = rpc.ToolExecutionCompleteContentTypeTerminal + ToolExecutionCompleteContentTypeText = rpc.ToolExecutionCompleteContentTypeText + UserMessageAgentModeAutopilot = rpc.UserMessageAgentModeAutopilot + UserMessageAgentModeInteractive = rpc.UserMessageAgentModeInteractive + UserMessageAgentModePlan = rpc.UserMessageAgentModePlan + UserMessageAgentModeShell = rpc.UserMessageAgentModeShell + UserMessageAttachmentGithubReferenceTypeDiscussion = rpc.UserMessageAttachmentGithubReferenceTypeDiscussion + UserMessageAttachmentGithubReferenceTypeIssue = rpc.UserMessageAttachmentGithubReferenceTypeIssue + UserMessageAttachmentGithubReferenceTypePr = rpc.UserMessageAttachmentGithubReferenceTypePr + UserMessageAttachmentTypeBlob = rpc.UserMessageAttachmentTypeBlob + UserMessageAttachmentTypeDirectory = rpc.UserMessageAttachmentTypeDirectory + UserMessageAttachmentTypeFile = rpc.UserMessageAttachmentTypeFile + UserMessageAttachmentTypeGithubReference = rpc.UserMessageAttachmentTypeGithubReference + UserMessageAttachmentTypeSelection = rpc.UserMessageAttachmentTypeSelection + UserToolSessionApprovalKindCommands = rpc.UserToolSessionApprovalKindCommands + UserToolSessionApprovalKindCustomTool = rpc.UserToolSessionApprovalKindCustomTool + UserToolSessionApprovalKindExtensionManagement = rpc.UserToolSessionApprovalKindExtensionManagement + UserToolSessionApprovalKindExtensionPermissionAccess = rpc.UserToolSessionApprovalKindExtensionPermissionAccess + UserToolSessionApprovalKindMcp = rpc.UserToolSessionApprovalKindMcp + UserToolSessionApprovalKindMemory = rpc.UserToolSessionApprovalKindMemory + UserToolSessionApprovalKindRead = rpc.UserToolSessionApprovalKindRead + UserToolSessionApprovalKindWrite = rpc.UserToolSessionApprovalKindWrite + WorkingDirectoryContextHostTypeAdo = rpc.WorkingDirectoryContextHostTypeAdo + WorkingDirectoryContextHostTypeGithub = rpc.WorkingDirectoryContextHostTypeGithub + WorkspaceFileChangedOperationCreate = rpc.WorkspaceFileChangedOperationCreate + WorkspaceFileChangedOperationUpdate = rpc.WorkspaceFileChangedOperationUpdate ) diff --git a/nodejs/src/generated/rpc.ts b/nodejs/src/generated/rpc.ts index 9160fff57..97cd9df0e 100644 --- a/nodejs/src/generated/rpc.ts +++ b/nodejs/src/generated/rpc.ts @@ -5,6 +5,8 @@ import type { MessageConnection } from "vscode-jsonrpc/node.js"; +import type { EmbeddedBlobResourceContents, EmbeddedTextResourceContents } from "./session-events.js"; + /** * Authentication type * @@ -609,36 +611,6 @@ export interface DiscoveredMcpServer { enabled: boolean; } -export interface EmbeddedBlobResourceContents { - /** - * URI identifying the resource - */ - uri: string; - /** - * MIME type of the blob content - */ - mimeType?: string; - /** - * Base64-encoded binary content of the resource - */ - blob: string; -} - -export interface EmbeddedTextResourceContents { - /** - * URI identifying the resource - */ - uri: string; - /** - * MIME type of the text content - */ - mimeType?: string; - /** - * Text content of the resource - */ - text: string; -} - export interface Extension { /** * Source-qualified ID (e.g., 'project:my-ext', 'user:auth-helper') diff --git a/nodejs/test/shared-codegen.test.ts b/nodejs/test/shared-codegen.test.ts new file mode 100644 index 000000000..88f4d3cc3 --- /dev/null +++ b/nodejs/test/shared-codegen.test.ts @@ -0,0 +1,258 @@ +import type { JSONSchema7 } from "json-schema"; +import { describe, expect, it } from "vitest"; + +import { + collectReachableDefinitionNames, + findSharedSchemaDefinitions, + inlineExternalSchemaDefinitions, + rewriteSharedDefinitionReferences, +} from "../../scripts/codegen/utils.ts"; + +describe("shared schema definition codegen utilities", () => { + it("rewrites reachable identical shared definitions without enum-only assumptions", () => { + const sessionSchema: JSONSchema7 = { + definitions: { + SessionEvent: { + anyOf: [ + { + type: "object", + required: ["type", "data"], + properties: { + type: { const: "session.start" }, + data: { + type: "object", + required: ["payload", "reasoningSummary"], + properties: { + payload: { $ref: "#/definitions/SharedPayload" }, + reasoningSummary: { + $ref: "#/definitions/ReasoningSummary", + }, + }, + }, + }, + }, + ], + }, + ReasoningSummary: { + type: "string", + enum: ["concise", "detailed"], + }, + SharedPayload: { + type: "object", + required: ["leaf"], + properties: { + leaf: { $ref: "#/definitions/SharedLeaf" }, + }, + }, + SharedLeaf: { + type: "object", + required: ["value"], + properties: { + value: { type: "string" }, + }, + }, + BrokenParent: { + type: "object", + properties: { + leaf: { $ref: "#/definitions/BrokenLeaf" }, + }, + }, + BrokenLeaf: { + type: "string", + enum: ["session"], + }, + UnusedShared: { + type: "object", + properties: { + value: { type: "string" }, + }, + }, + }, + }; + const apiSchema = { + definitions: { + ReasoningSummary: { + type: "string", + enum: ["concise", "detailed"], + }, + SharedPayload: { + type: "object", + required: ["leaf"], + properties: { + leaf: { $ref: "#/$defs/SharedLeaf" }, + }, + }, + SharedLeaf: { + type: "object", + required: ["value"], + properties: { + value: { type: "string" }, + }, + }, + BrokenParent: { + type: "object", + properties: { + leaf: { $ref: "#/definitions/BrokenLeaf" }, + }, + }, + BrokenLeaf: { + type: "string", + enum: ["api"], + }, + UnusedShared: { + type: "object", + properties: { + value: { type: "string" }, + }, + }, + }, + $defs: { + SharedLeaf: { + type: "object", + required: ["value"], + properties: { + value: { type: "string" }, + }, + }, + }, + server: { + test: { + rpcMethod: "test.shared", + params: { + type: "object", + properties: { + broken: { $ref: "#/definitions/BrokenParent" }, + payload: { $ref: "#/definitions/SharedPayload" }, + reasoningSummary: { $ref: "#/definitions/ReasoningSummary" }, + unused: { $ref: "#/definitions/UnusedShared" }, + }, + }, + result: { type: "null" }, + }, + }, + }; + + const shared = findSharedSchemaDefinitions( + apiSchema as Record, + sessionSchema as unknown as Record + ); + expect([...shared].sort()).toEqual([ + "ReasoningSummary", + "SharedLeaf", + "SharedPayload", + "UnusedShared", + ]); + + const reachable = collectReachableDefinitionNames( + sessionSchema as unknown as Record + ); + for (const name of [...shared]) { + if (!reachable.has(name)) shared.delete(name); + } + + const rewritten = rewriteSharedDefinitionReferences( + apiSchema, + shared, + "session-events.schema.json" + ) as typeof apiSchema; + + expect(rewritten.definitions).not.toHaveProperty("ReasoningSummary"); + expect(rewritten.definitions).not.toHaveProperty("SharedPayload"); + expect(rewritten.definitions).not.toHaveProperty("SharedLeaf"); + expect(rewritten.definitions).toHaveProperty("BrokenParent"); + expect(rewritten.definitions).toHaveProperty("UnusedShared"); + expect(rewritten.server.test.params.properties.reasoningSummary.$ref).toBe( + "session-events.schema.json#/definitions/ReasoningSummary" + ); + expect(rewritten.server.test.params.properties.payload.$ref).toBe( + "session-events.schema.json#/definitions/SharedPayload" + ); + expect(rewritten.server.test.params.properties.broken.$ref).toBe( + "#/definitions/BrokenParent" + ); + expect(rewritten.server.test.params.properties.unused.$ref).toBe( + "#/definitions/UnusedShared" + ); + }); + + it("inlines direct external refs with transitive definitions", () => { + const sessionSchema: JSONSchema7 = { + definitions: { + SessionEvent: { + anyOf: [{ $ref: "#/definitions/SessionStartEvent" }], + }, + SessionStartEvent: { + type: "object", + required: ["type", "data"], + properties: { + type: { const: "session.start" }, + data: { $ref: "#/definitions/SessionStartData" }, + }, + }, + SessionStartData: { + type: "object", + required: ["reasoningSummary", "shutdownType"], + properties: { + reasoningSummary: { $ref: "#/definitions/ReasoningSummary" }, + shutdownType: { $ref: "#/definitions/ShutdownType" }, + }, + }, + ReasoningSummary: { + type: "string", + enum: ["concise", "detailed"], + }, + ShutdownType: { + type: "string", + enum: ["session"], + }, + }, + }; + const apiSchema = { + definitions: { + EventsReadResult: { + type: "object", + required: ["events"], + properties: { + events: { + type: "array", + items: { + $ref: "session-events.schema.json#/definitions/SessionEvent", + }, + }, + }, + }, + ShutdownType: { + type: "string", + enum: ["api"], + }, + }, + }; + + const { schema: inlined, inlinedDefinitionNames } = inlineExternalSchemaDefinitions( + apiSchema, + sessionSchema as unknown as Record, + "session-events.schema.json", + { conflictingDefinitionNamePrefix: "SessionEvents" } + ); + + expect([...inlinedDefinitionNames].sort()).toEqual([ + "ReasoningSummary", + "SessionEvent", + "SessionEventsShutdownType", + "SessionStartData", + "SessionStartEvent", + ]); + const inlinedDefinitions = inlined.definitions as Record; + expect(inlinedDefinitions.EventsReadResult.properties.events.items.$ref).toBe( + "#/definitions/SessionEvent" + ); + expect(inlinedDefinitions.SessionStartData.properties.reasoningSummary.$ref).toBe( + "#/definitions/ReasoningSummary" + ); + expect(inlinedDefinitions.SessionStartData.properties.shutdownType.$ref).toBe( + "#/definitions/SessionEventsShutdownType" + ); + expect(inlinedDefinitions.ShutdownType.enum).toEqual(["api"]); + expect(inlinedDefinitions.SessionEventsShutdownType.enum).toEqual(["session"]); + }); +}); diff --git a/python/copilot/generated/rpc.py b/python/copilot/generated/rpc.py index 41049b38d..738e92934 100644 --- a/python/copilot/generated/rpc.py +++ b/python/copilot/generated/rpc.py @@ -5,6 +5,8 @@ from typing import TYPE_CHECKING +from .session_events import EmbeddedBlobResourceContents, EmbeddedTextResourceContents + if TYPE_CHECKING: from .._jsonrpc import JsonRpcClient @@ -433,60 +435,6 @@ class DiscoveredMCPServerType(Enum): SSE = "sse" STDIO = "stdio" -@dataclass -class EmbeddedBlobResourceContents: - blob: str - """Base64-encoded binary content of the resource""" - - uri: str - """URI identifying the resource""" - - mime_type: str | None = None - """MIME type of the blob content""" - - @staticmethod - def from_dict(obj: Any) -> 'EmbeddedBlobResourceContents': - assert isinstance(obj, dict) - blob = from_str(obj.get("blob")) - uri = from_str(obj.get("uri")) - mime_type = from_union([from_str, from_none], obj.get("mimeType")) - return EmbeddedBlobResourceContents(blob, uri, mime_type) - - def to_dict(self) -> dict: - result: dict = {} - result["blob"] = from_str(self.blob) - result["uri"] = from_str(self.uri) - if self.mime_type is not None: - result["mimeType"] = from_union([from_str, from_none], self.mime_type) - return result - -@dataclass -class EmbeddedTextResourceContents: - text: str - """Text content of the resource""" - - uri: str - """URI identifying the resource""" - - mime_type: str | None = None - """MIME type of the text content""" - - @staticmethod - def from_dict(obj: Any) -> 'EmbeddedTextResourceContents': - assert isinstance(obj, dict) - text = from_str(obj.get("text")) - uri = from_str(obj.get("uri")) - mime_type = from_union([from_str, from_none], obj.get("mimeType")) - return EmbeddedTextResourceContents(text, uri, mime_type) - - def to_dict(self) -> dict: - result: dict = {} - result["text"] = from_str(self.text) - result["uri"] = from_str(self.uri) - if self.mime_type is not None: - result["mimeType"] = from_union([from_str, from_none], self.mime_type) - return result - class ExtensionSource(Enum): """Discovery source: project (.github/extensions/) or user (~/.copilot/extensions/)""" @@ -541,44 +489,6 @@ class ExternalToolTextResultForLlmContentResourceLinkIconTheme(Enum): DARK = "dark" LIGHT = "light" -@dataclass -class ExternalToolTextResultForLlmContentResourceDetails: - """The embedded resource contents, either text or base64-encoded binary""" - - uri: str - """URI identifying the resource""" - - mime_type: str | None = None - """MIME type of the text content - - MIME type of the blob content - """ - text: str | None = None - """Text content of the resource""" - - blob: str | None = None - """Base64-encoded binary content of the resource""" - - @staticmethod - def from_dict(obj: Any) -> 'ExternalToolTextResultForLlmContentResourceDetails': - assert isinstance(obj, dict) - uri = from_str(obj.get("uri")) - mime_type = from_union([from_str, from_none], obj.get("mimeType")) - text = from_union([from_str, from_none], obj.get("text")) - blob = from_union([from_str, from_none], obj.get("blob")) - return ExternalToolTextResultForLlmContentResourceDetails(uri, mime_type, text, blob) - - def to_dict(self) -> dict: - result: dict = {} - result["uri"] = from_str(self.uri) - if self.mime_type is not None: - result["mimeType"] = from_union([from_str, from_none], self.mime_type) - if self.text is not None: - result["text"] = from_union([from_str, from_none], self.text) - if self.blob is not None: - result["blob"] = from_union([from_str, from_none], self.blob) - return result - class ExternalToolTextResultForLlmContentType(Enum): AUDIO = "audio" IMAGE = "image" @@ -3074,6 +2984,8 @@ def to_dict(self) -> dict: result["theme"] = from_union([lambda x: to_enum(ExternalToolTextResultForLlmContentResourceLinkIconTheme, x), from_none], self.theme) return result +ExternalToolTextResultForLlmContentResourceDetails = EmbeddedTextResourceContents | EmbeddedBlobResourceContents + @dataclass class ExternalToolTextResultForLlmContentAudio: """Audio content block with base64-encoded data""" @@ -3143,13 +3055,13 @@ class ExternalToolTextResultForLlmContentResource: @staticmethod def from_dict(obj: Any) -> 'ExternalToolTextResultForLlmContentResource': assert isinstance(obj, dict) - resource = ExternalToolTextResultForLlmContentResourceDetails.from_dict(obj.get("resource")) + resource = (lambda x: from_union([EmbeddedTextResourceContents.from_dict, EmbeddedBlobResourceContents.from_dict], x))(obj.get("resource")) type = ExternalToolTextResultForLlmContentResourceType(obj.get("type")) return ExternalToolTextResultForLlmContentResource(resource, type) def to_dict(self) -> dict: result: dict = {} - result["resource"] = to_class(ExternalToolTextResultForLlmContentResourceDetails, self.resource) + result["resource"] = from_union([lambda x: to_class(EmbeddedTextResourceContents, x), lambda x: to_class(EmbeddedBlobResourceContents, x)], self.resource) result["type"] = to_enum(ExternalToolTextResultForLlmContentResourceType, self.type) return result @@ -5112,7 +5024,7 @@ def from_dict(obj: Any) -> 'ExternalToolTextResultForLlmContent': size = from_union([from_float, from_none], obj.get("size")) title = from_union([from_str, from_none], obj.get("title")) uri = from_union([from_str, from_none], obj.get("uri")) - resource = from_union([ExternalToolTextResultForLlmContentResourceDetails.from_dict, from_none], obj.get("resource")) + resource = from_union([(lambda x: from_union([EmbeddedTextResourceContents.from_dict, EmbeddedBlobResourceContents.from_dict], x)), from_none], obj.get("resource")) return ExternalToolTextResultForLlmContent(type, text, cwd, exit_code, data, mime_type, description, icons, name, size, title, uri, resource) def to_dict(self) -> dict: @@ -5141,7 +5053,7 @@ def to_dict(self) -> dict: if self.uri is not None: result["uri"] = from_union([from_str, from_none], self.uri) if self.resource is not None: - result["resource"] = from_union([lambda x: to_class(ExternalToolTextResultForLlmContentResourceDetails, x), from_none], self.resource) + result["resource"] = from_union([lambda x: from_union([lambda x: to_class(EmbeddedTextResourceContents, x), lambda x: to_class(EmbeddedBlobResourceContents, x)], x), from_none], self.resource) return result @dataclass @@ -6389,8 +6301,6 @@ class RPC: discovered_mcp_server: DiscoveredMCPServer discovered_mcp_server_source: MCPServerSource discovered_mcp_server_type: DiscoveredMCPServerType - embedded_blob_resource_contents: EmbeddedBlobResourceContents - embedded_text_resource_contents: EmbeddedTextResourceContents extension: Extension extension_list: ExtensionList extensions_disable_request: ExtensionsDisableRequest @@ -6647,8 +6557,6 @@ def from_dict(obj: Any) -> 'RPC': discovered_mcp_server = DiscoveredMCPServer.from_dict(obj.get("DiscoveredMcpServer")) discovered_mcp_server_source = MCPServerSource(obj.get("DiscoveredMcpServerSource")) discovered_mcp_server_type = DiscoveredMCPServerType(obj.get("DiscoveredMcpServerType")) - embedded_blob_resource_contents = EmbeddedBlobResourceContents.from_dict(obj.get("EmbeddedBlobResourceContents")) - embedded_text_resource_contents = EmbeddedTextResourceContents.from_dict(obj.get("EmbeddedTextResourceContents")) extension = Extension.from_dict(obj.get("Extension")) extension_list = ExtensionList.from_dict(obj.get("ExtensionList")) extensions_disable_request = ExtensionsDisableRequest.from_dict(obj.get("ExtensionsDisableRequest")) @@ -6661,7 +6569,7 @@ def from_dict(obj: Any) -> 'RPC': external_tool_text_result_for_llm_content_audio = ExternalToolTextResultForLlmContentAudio.from_dict(obj.get("ExternalToolTextResultForLlmContentAudio")) external_tool_text_result_for_llm_content_image = ExternalToolTextResultForLlmContentImage.from_dict(obj.get("ExternalToolTextResultForLlmContentImage")) external_tool_text_result_for_llm_content_resource = ExternalToolTextResultForLlmContentResource.from_dict(obj.get("ExternalToolTextResultForLlmContentResource")) - external_tool_text_result_for_llm_content_resource_details = ExternalToolTextResultForLlmContentResourceDetails.from_dict(obj.get("ExternalToolTextResultForLlmContentResourceDetails")) + external_tool_text_result_for_llm_content_resource_details = (lambda x: from_union([EmbeddedTextResourceContents.from_dict, EmbeddedBlobResourceContents.from_dict], x))(obj.get("ExternalToolTextResultForLlmContentResourceDetails")) external_tool_text_result_for_llm_content_resource_link = ExternalToolTextResultForLlmContentResourceLink.from_dict(obj.get("ExternalToolTextResultForLlmContentResourceLink")) external_tool_text_result_for_llm_content_resource_link_icon = ExternalToolTextResultForLlmContentResourceLinkIcon.from_dict(obj.get("ExternalToolTextResultForLlmContentResourceLinkIcon")) external_tool_text_result_for_llm_content_resource_link_icon_theme = ExternalToolTextResultForLlmContentResourceLinkIconTheme(obj.get("ExternalToolTextResultForLlmContentResourceLinkIconTheme")) @@ -6878,7 +6786,7 @@ def from_dict(obj: Any) -> 'RPC': workspaces_list_files_result = WorkspacesListFilesResult.from_dict(obj.get("WorkspacesListFilesResult")) workspaces_read_file_request = WorkspacesReadFileRequest.from_dict(obj.get("WorkspacesReadFileRequest")) workspaces_read_file_result = WorkspacesReadFileResult.from_dict(obj.get("WorkspacesReadFileResult")) - return RPC(account_get_quota_request, account_get_quota_result, account_quota_snapshot, agent_get_current_result, agent_info, agent_list, agent_reload_result, agent_select_request, agent_select_result, auth_info_type, command_list, commands_handle_pending_command_request, commands_handle_pending_command_result, commands_invoke_request, commands_list_request, commands_respond_to_queued_command_request, commands_respond_to_queued_command_result, connect_request, connect_result, current_model, discovered_mcp_server, discovered_mcp_server_source, discovered_mcp_server_type, embedded_blob_resource_contents, embedded_text_resource_contents, extension, extension_list, extensions_disable_request, extensions_enable_request, extension_source, extension_status, external_tool_result, external_tool_text_result_for_llm, external_tool_text_result_for_llm_content, external_tool_text_result_for_llm_content_audio, external_tool_text_result_for_llm_content_image, external_tool_text_result_for_llm_content_resource, external_tool_text_result_for_llm_content_resource_details, external_tool_text_result_for_llm_content_resource_link, external_tool_text_result_for_llm_content_resource_link_icon, external_tool_text_result_for_llm_content_resource_link_icon_theme, external_tool_text_result_for_llm_content_terminal, external_tool_text_result_for_llm_content_text, filter_mapping, filter_mapping_string, filter_mapping_value, fleet_start_request, fleet_start_result, handle_pending_tool_call_request, handle_pending_tool_call_result, history_compact_context_window, history_compact_result, history_truncate_request, history_truncate_result, instructions_get_sources_result, instructions_sources, instructions_sources_location, instructions_sources_type, log_request, log_result, mcp_config_add_request, mcp_config_disable_request, mcp_config_enable_request, mcp_config_list, mcp_config_remove_request, mcp_config_update_request, mcp_disable_request, mcp_discover_request, mcp_discover_result, mcp_enable_request, mcp_oauth_login_request, mcp_oauth_login_result, mcp_server, mcp_server_config, mcp_server_config_http, mcp_server_config_http_oauth_grant_type, mcp_server_config_http_type, mcp_server_config_local, mcp_server_config_local_type, mcp_server_list, mcp_server_source, mcp_server_status, model, model_billing, model_billing_token_prices, model_capabilities, model_capabilities_limits, model_capabilities_limits_vision, model_capabilities_override, model_capabilities_override_limits, model_capabilities_override_limits_vision, model_capabilities_override_supports, model_capabilities_supports, model_list, model_picker_category, model_picker_price_category, model_policy, models_list_request, model_switch_to_request, model_switch_to_result, mode_set_request, name_get_result, name_set_request, permission_decision, permission_decision_approve_for_location, permission_decision_approve_for_location_approval, permission_decision_approve_for_location_approval_commands, permission_decision_approve_for_location_approval_custom_tool, permission_decision_approve_for_location_approval_extension_management, permission_decision_approve_for_location_approval_extension_permission_access, permission_decision_approve_for_location_approval_mcp, permission_decision_approve_for_location_approval_mcp_sampling, permission_decision_approve_for_location_approval_memory, permission_decision_approve_for_location_approval_read, permission_decision_approve_for_location_approval_write, permission_decision_approve_for_session, permission_decision_approve_for_session_approval, permission_decision_approve_for_session_approval_commands, permission_decision_approve_for_session_approval_custom_tool, permission_decision_approve_for_session_approval_extension_management, permission_decision_approve_for_session_approval_extension_permission_access, permission_decision_approve_for_session_approval_mcp, permission_decision_approve_for_session_approval_mcp_sampling, permission_decision_approve_for_session_approval_memory, permission_decision_approve_for_session_approval_read, permission_decision_approve_for_session_approval_write, permission_decision_approve_once, permission_decision_approve_permanently, permission_decision_reject, permission_decision_request, permission_decision_user_not_available, permission_request_result, permissions_reset_session_approvals_request, permissions_reset_session_approvals_result, permissions_set_approve_all_request, permissions_set_approve_all_result, ping_request, ping_result, plan_read_result, plan_update_request, plugin, plugin_list, queued_command_handled, queued_command_not_handled, queued_command_result, remote_enable_request, remote_enable_result, remote_session_mode, server_skill, server_skill_list, session_auth_status, session_fs_append_file_request, session_fs_error, session_fs_error_code, session_fs_exists_request, session_fs_exists_result, session_fs_mkdir_request, session_fs_readdir_request, session_fs_readdir_result, session_fs_readdir_with_types_entry, session_fs_readdir_with_types_entry_type, session_fs_readdir_with_types_request, session_fs_readdir_with_types_result, session_fs_read_file_request, session_fs_read_file_result, session_fs_rename_request, session_fs_rm_request, session_fs_set_provider_conventions, session_fs_set_provider_request, session_fs_set_provider_result, session_fs_stat_request, session_fs_stat_result, session_fs_write_file_request, session_log_level, session_mode, sessions_fork_request, sessions_fork_result, shell_exec_request, shell_exec_result, shell_kill_request, shell_kill_result, shell_kill_signal, skill, skill_list, skills_config_set_disabled_skills_request, skills_disable_request, skills_discover_request, skills_enable_request, skills_load_diagnostics, slash_command_agent_prompt_mode, slash_command_agent_prompt_result, slash_command_completed_result, slash_command_info, slash_command_input, slash_command_input_completion, slash_command_invocation_result, slash_command_kind, slash_command_text_result, task_agent_info, task_agent_info_execution_mode, task_agent_info_status, task_info, task_list, tasks_cancel_request, tasks_cancel_result, task_shell_info, task_shell_info_attachment_mode, task_shell_info_execution_mode, task_shell_info_status, tasks_promote_to_background_request, tasks_promote_to_background_result, tasks_remove_request, tasks_remove_result, tasks_send_message_request, tasks_send_message_result, tasks_start_agent_request, tasks_start_agent_result, tool, tool_list, tools_list_request, ui_elicitation_array_any_of_field, ui_elicitation_array_any_of_field_items, ui_elicitation_array_any_of_field_items_any_of, ui_elicitation_array_enum_field, ui_elicitation_array_enum_field_items, ui_elicitation_field_value, ui_elicitation_request, ui_elicitation_response, ui_elicitation_response_action, ui_elicitation_response_content, ui_elicitation_result, ui_elicitation_schema, ui_elicitation_schema_property, ui_elicitation_schema_property_boolean, ui_elicitation_schema_property_number, ui_elicitation_schema_property_number_type, ui_elicitation_schema_property_string, ui_elicitation_schema_property_string_format, ui_elicitation_string_enum_field, ui_elicitation_string_one_of_field, ui_elicitation_string_one_of_field_one_of, ui_handle_pending_elicitation_request, usage_get_metrics_result, usage_metrics_code_changes, usage_metrics_model_metric, usage_metrics_model_metric_requests, usage_metrics_model_metric_token_detail, usage_metrics_model_metric_usage, usage_metrics_token_detail, workspaces_create_file_request, workspaces_get_workspace_result, workspaces_list_files_result, workspaces_read_file_request, workspaces_read_file_result) + return RPC(account_get_quota_request, account_get_quota_result, account_quota_snapshot, agent_get_current_result, agent_info, agent_list, agent_reload_result, agent_select_request, agent_select_result, auth_info_type, command_list, commands_handle_pending_command_request, commands_handle_pending_command_result, commands_invoke_request, commands_list_request, commands_respond_to_queued_command_request, commands_respond_to_queued_command_result, connect_request, connect_result, current_model, discovered_mcp_server, discovered_mcp_server_source, discovered_mcp_server_type, extension, extension_list, extensions_disable_request, extensions_enable_request, extension_source, extension_status, external_tool_result, external_tool_text_result_for_llm, external_tool_text_result_for_llm_content, external_tool_text_result_for_llm_content_audio, external_tool_text_result_for_llm_content_image, external_tool_text_result_for_llm_content_resource, external_tool_text_result_for_llm_content_resource_details, external_tool_text_result_for_llm_content_resource_link, external_tool_text_result_for_llm_content_resource_link_icon, external_tool_text_result_for_llm_content_resource_link_icon_theme, external_tool_text_result_for_llm_content_terminal, external_tool_text_result_for_llm_content_text, filter_mapping, filter_mapping_string, filter_mapping_value, fleet_start_request, fleet_start_result, handle_pending_tool_call_request, handle_pending_tool_call_result, history_compact_context_window, history_compact_result, history_truncate_request, history_truncate_result, instructions_get_sources_result, instructions_sources, instructions_sources_location, instructions_sources_type, log_request, log_result, mcp_config_add_request, mcp_config_disable_request, mcp_config_enable_request, mcp_config_list, mcp_config_remove_request, mcp_config_update_request, mcp_disable_request, mcp_discover_request, mcp_discover_result, mcp_enable_request, mcp_oauth_login_request, mcp_oauth_login_result, mcp_server, mcp_server_config, mcp_server_config_http, mcp_server_config_http_oauth_grant_type, mcp_server_config_http_type, mcp_server_config_local, mcp_server_config_local_type, mcp_server_list, mcp_server_source, mcp_server_status, model, model_billing, model_billing_token_prices, model_capabilities, model_capabilities_limits, model_capabilities_limits_vision, model_capabilities_override, model_capabilities_override_limits, model_capabilities_override_limits_vision, model_capabilities_override_supports, model_capabilities_supports, model_list, model_picker_category, model_picker_price_category, model_policy, models_list_request, model_switch_to_request, model_switch_to_result, mode_set_request, name_get_result, name_set_request, permission_decision, permission_decision_approve_for_location, permission_decision_approve_for_location_approval, permission_decision_approve_for_location_approval_commands, permission_decision_approve_for_location_approval_custom_tool, permission_decision_approve_for_location_approval_extension_management, permission_decision_approve_for_location_approval_extension_permission_access, permission_decision_approve_for_location_approval_mcp, permission_decision_approve_for_location_approval_mcp_sampling, permission_decision_approve_for_location_approval_memory, permission_decision_approve_for_location_approval_read, permission_decision_approve_for_location_approval_write, permission_decision_approve_for_session, permission_decision_approve_for_session_approval, permission_decision_approve_for_session_approval_commands, permission_decision_approve_for_session_approval_custom_tool, permission_decision_approve_for_session_approval_extension_management, permission_decision_approve_for_session_approval_extension_permission_access, permission_decision_approve_for_session_approval_mcp, permission_decision_approve_for_session_approval_mcp_sampling, permission_decision_approve_for_session_approval_memory, permission_decision_approve_for_session_approval_read, permission_decision_approve_for_session_approval_write, permission_decision_approve_once, permission_decision_approve_permanently, permission_decision_reject, permission_decision_request, permission_decision_user_not_available, permission_request_result, permissions_reset_session_approvals_request, permissions_reset_session_approvals_result, permissions_set_approve_all_request, permissions_set_approve_all_result, ping_request, ping_result, plan_read_result, plan_update_request, plugin, plugin_list, queued_command_handled, queued_command_not_handled, queued_command_result, remote_enable_request, remote_enable_result, remote_session_mode, server_skill, server_skill_list, session_auth_status, session_fs_append_file_request, session_fs_error, session_fs_error_code, session_fs_exists_request, session_fs_exists_result, session_fs_mkdir_request, session_fs_readdir_request, session_fs_readdir_result, session_fs_readdir_with_types_entry, session_fs_readdir_with_types_entry_type, session_fs_readdir_with_types_request, session_fs_readdir_with_types_result, session_fs_read_file_request, session_fs_read_file_result, session_fs_rename_request, session_fs_rm_request, session_fs_set_provider_conventions, session_fs_set_provider_request, session_fs_set_provider_result, session_fs_stat_request, session_fs_stat_result, session_fs_write_file_request, session_log_level, session_mode, sessions_fork_request, sessions_fork_result, shell_exec_request, shell_exec_result, shell_kill_request, shell_kill_result, shell_kill_signal, skill, skill_list, skills_config_set_disabled_skills_request, skills_disable_request, skills_discover_request, skills_enable_request, skills_load_diagnostics, slash_command_agent_prompt_mode, slash_command_agent_prompt_result, slash_command_completed_result, slash_command_info, slash_command_input, slash_command_input_completion, slash_command_invocation_result, slash_command_kind, slash_command_text_result, task_agent_info, task_agent_info_execution_mode, task_agent_info_status, task_info, task_list, tasks_cancel_request, tasks_cancel_result, task_shell_info, task_shell_info_attachment_mode, task_shell_info_execution_mode, task_shell_info_status, tasks_promote_to_background_request, tasks_promote_to_background_result, tasks_remove_request, tasks_remove_result, tasks_send_message_request, tasks_send_message_result, tasks_start_agent_request, tasks_start_agent_result, tool, tool_list, tools_list_request, ui_elicitation_array_any_of_field, ui_elicitation_array_any_of_field_items, ui_elicitation_array_any_of_field_items_any_of, ui_elicitation_array_enum_field, ui_elicitation_array_enum_field_items, ui_elicitation_field_value, ui_elicitation_request, ui_elicitation_response, ui_elicitation_response_action, ui_elicitation_response_content, ui_elicitation_result, ui_elicitation_schema, ui_elicitation_schema_property, ui_elicitation_schema_property_boolean, ui_elicitation_schema_property_number, ui_elicitation_schema_property_number_type, ui_elicitation_schema_property_string, ui_elicitation_schema_property_string_format, ui_elicitation_string_enum_field, ui_elicitation_string_one_of_field, ui_elicitation_string_one_of_field_one_of, ui_handle_pending_elicitation_request, usage_get_metrics_result, usage_metrics_code_changes, usage_metrics_model_metric, usage_metrics_model_metric_requests, usage_metrics_model_metric_token_detail, usage_metrics_model_metric_usage, usage_metrics_token_detail, workspaces_create_file_request, workspaces_get_workspace_result, workspaces_list_files_result, workspaces_read_file_request, workspaces_read_file_result) def to_dict(self) -> dict: result: dict = {} @@ -6905,8 +6813,6 @@ def to_dict(self) -> dict: result["DiscoveredMcpServer"] = to_class(DiscoveredMCPServer, self.discovered_mcp_server) result["DiscoveredMcpServerSource"] = to_enum(MCPServerSource, self.discovered_mcp_server_source) result["DiscoveredMcpServerType"] = to_enum(DiscoveredMCPServerType, self.discovered_mcp_server_type) - result["EmbeddedBlobResourceContents"] = to_class(EmbeddedBlobResourceContents, self.embedded_blob_resource_contents) - result["EmbeddedTextResourceContents"] = to_class(EmbeddedTextResourceContents, self.embedded_text_resource_contents) result["Extension"] = to_class(Extension, self.extension) result["ExtensionList"] = to_class(ExtensionList, self.extension_list) result["ExtensionsDisableRequest"] = to_class(ExtensionsDisableRequest, self.extensions_disable_request) @@ -6919,7 +6825,7 @@ def to_dict(self) -> dict: result["ExternalToolTextResultForLlmContentAudio"] = to_class(ExternalToolTextResultForLlmContentAudio, self.external_tool_text_result_for_llm_content_audio) result["ExternalToolTextResultForLlmContentImage"] = to_class(ExternalToolTextResultForLlmContentImage, self.external_tool_text_result_for_llm_content_image) result["ExternalToolTextResultForLlmContentResource"] = to_class(ExternalToolTextResultForLlmContentResource, self.external_tool_text_result_for_llm_content_resource) - result["ExternalToolTextResultForLlmContentResourceDetails"] = to_class(ExternalToolTextResultForLlmContentResourceDetails, self.external_tool_text_result_for_llm_content_resource_details) + result["ExternalToolTextResultForLlmContentResourceDetails"] = from_union([lambda x: to_class(EmbeddedTextResourceContents, x), lambda x: to_class(EmbeddedBlobResourceContents, x)], self.external_tool_text_result_for_llm_content_resource_details) result["ExternalToolTextResultForLlmContentResourceLink"] = to_class(ExternalToolTextResultForLlmContentResourceLink, self.external_tool_text_result_for_llm_content_resource_link) result["ExternalToolTextResultForLlmContentResourceLinkIcon"] = to_class(ExternalToolTextResultForLlmContentResourceLinkIcon, self.external_tool_text_result_for_llm_content_resource_link_icon) result["ExternalToolTextResultForLlmContentResourceLinkIconTheme"] = to_enum(ExternalToolTextResultForLlmContentResourceLinkIconTheme, self.external_tool_text_result_for_llm_content_resource_link_icon_theme) diff --git a/python/copilot/generated/session_events.py b/python/copilot/generated/session_events.py index 8bab188bb..947793bda 100644 --- a/python/copilot/generated/session_events.py +++ b/python/copilot/generated/session_events.py @@ -1267,6 +1267,60 @@ def to_dict(self) -> dict: return result +@dataclass +class EmbeddedBlobResourceContents: + blob: str + uri: str + mime_type: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "EmbeddedBlobResourceContents": + assert isinstance(obj, dict) + blob = from_str(obj.get("blob")) + uri = from_str(obj.get("uri")) + mime_type = from_union([from_none, from_str], obj.get("mimeType")) + return EmbeddedBlobResourceContents( + blob=blob, + uri=uri, + mime_type=mime_type, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["blob"] = from_str(self.blob) + result["uri"] = from_str(self.uri) + if self.mime_type is not None: + result["mimeType"] = from_union([from_none, from_str], self.mime_type) + return result + + +@dataclass +class EmbeddedTextResourceContents: + text: str + uri: str + mime_type: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "EmbeddedTextResourceContents": + assert isinstance(obj, dict) + text = from_str(obj.get("text")) + uri = from_str(obj.get("uri")) + mime_type = from_union([from_none, from_str], obj.get("mimeType")) + return EmbeddedTextResourceContents( + text=text, + uri=uri, + mime_type=mime_type, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["text"] = from_str(self.text) + result["uri"] = from_str(self.uri) + if self.mime_type is not None: + result["mimeType"] = from_union([from_none, from_str], self.mime_type) + return result + + @dataclass class ExitPlanModeCompletedData: "Plan mode exit completion with the user's approval decision and optional feedback" @@ -3919,7 +3973,7 @@ class ToolExecutionCompleteContent: icons: list[ToolExecutionCompleteContentResourceLinkIcon] | None = None mime_type: str | None = None name: str | None = None - resource: Any = None + resource: ToolExecutionCompleteContentResourceDetails | None = None size: float | None = None text: str | None = None title: str | None = None @@ -3936,7 +3990,7 @@ def from_dict(obj: Any) -> "ToolExecutionCompleteContent": icons = from_union([from_none, lambda x: from_list(ToolExecutionCompleteContentResourceLinkIcon.from_dict, x)], obj.get("icons")) mime_type = from_union([from_none, from_str], obj.get("mimeType")) name = from_union([from_none, from_str], obj.get("name")) - resource = obj.get("resource") + resource = from_union([from_none, lambda x: from_union([EmbeddedTextResourceContents.from_dict, EmbeddedBlobResourceContents.from_dict], x)], obj.get("resource")) size = from_union([from_none, from_float], obj.get("size")) text = from_union([from_none, from_str], obj.get("text")) title = from_union([from_none, from_str], obj.get("title")) @@ -3975,7 +4029,7 @@ def to_dict(self) -> dict: if self.name is not None: result["name"] = from_union([from_none, from_str], self.name) if self.resource is not None: - result["resource"] = self.resource + result["resource"] = from_union([from_none, lambda x: from_union([lambda x: to_class(EmbeddedTextResourceContents, x), lambda x: to_class(EmbeddedBlobResourceContents, x)], x)], self.resource) if self.size is not None: result["size"] = from_union([from_none, to_float], self.size) if self.text is not None: @@ -4665,6 +4719,10 @@ def to_dict(self) -> dict: return result +# The embedded resource contents, either text or base64-encoded binary +ToolExecutionCompleteContentResourceDetails = EmbeddedTextResourceContents | EmbeddedBlobResourceContents + + class AbortReason(Enum): "Finite reason code describing why the current turn was aborted" USER_INITIATED = "user_initiated" diff --git a/rust/src/generated/api_types.rs b/rust/src/generated/api_types.rs index 277cce50a..2f74abb48 100644 --- a/rust/src/generated/api_types.rs +++ b/rust/src/generated/api_types.rs @@ -417,30 +417,6 @@ pub struct DiscoveredMcpServer { pub r#type: Option, } -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct EmbeddedBlobResourceContents { - /// Base64-encoded binary content of the resource - pub blob: String, - /// MIME type of the blob content - #[serde(skip_serializing_if = "Option::is_none")] - pub mime_type: Option, - /// URI identifying the resource - pub uri: String, -} - -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct EmbeddedTextResourceContents { - /// MIME type of the text content - #[serde(skip_serializing_if = "Option::is_none")] - pub mime_type: Option, - /// Text content of the resource - pub text: String, - /// URI identifying the resource - pub uri: String, -} - #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Extension { diff --git a/rust/src/generated/session_events.rs b/rust/src/generated/session_events.rs index 7c434f96d..e386f1300 100644 --- a/rust/src/generated/session_events.rs +++ b/rust/src/generated/session_events.rs @@ -1388,6 +1388,134 @@ pub struct ToolExecutionCompleteError { pub message: String, } +/// Plain text content block +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ToolExecutionCompleteContentText { + /// The text content + pub text: String, + /// Content block type discriminator + pub r#type: ToolExecutionCompleteContentTextType, +} + +/// Terminal/shell output content block with optional exit code and working directory +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ToolExecutionCompleteContentTerminal { + /// Working directory where the command was executed + #[serde(skip_serializing_if = "Option::is_none")] + pub cwd: Option, + /// Process exit code, if the command has completed + #[serde(skip_serializing_if = "Option::is_none")] + pub exit_code: Option, + /// Terminal/shell output text + pub text: String, + /// Content block type discriminator + pub r#type: ToolExecutionCompleteContentTerminalType, +} + +/// Image content block with base64-encoded data +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ToolExecutionCompleteContentImage { + /// Base64-encoded image data + pub data: String, + /// MIME type of the image (e.g., image/png, image/jpeg) + pub mime_type: String, + /// Content block type discriminator + pub r#type: ToolExecutionCompleteContentImageType, +} + +/// Audio content block with base64-encoded data +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ToolExecutionCompleteContentAudio { + /// Base64-encoded audio data + pub data: String, + /// MIME type of the audio (e.g., audio/wav, audio/mpeg) + pub mime_type: String, + /// Content block type discriminator + pub r#type: ToolExecutionCompleteContentAudioType, +} + +/// Icon image for a resource +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ToolExecutionCompleteContentResourceLinkIcon { + /// MIME type of the icon image + #[serde(skip_serializing_if = "Option::is_none")] + pub mime_type: Option, + /// Available icon sizes (e.g., ['16x16', '32x32']) + #[serde(default)] + pub sizes: Vec, + /// URL or path to the icon image + pub src: String, + /// Theme variant this icon is intended for + #[serde(skip_serializing_if = "Option::is_none")] + pub theme: Option, +} + +/// Resource link content block referencing an external resource +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ToolExecutionCompleteContentResourceLink { + /// Human-readable description of the resource + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + /// Icons associated with this resource + #[serde(default)] + pub icons: Vec, + /// MIME type of the resource content + #[serde(skip_serializing_if = "Option::is_none")] + pub mime_type: Option, + /// Resource name identifier + pub name: String, + /// Size of the resource in bytes + #[serde(skip_serializing_if = "Option::is_none")] + pub size: Option, + /// Human-readable display title for the resource + #[serde(skip_serializing_if = "Option::is_none")] + pub title: Option, + /// Content block type discriminator + pub r#type: ToolExecutionCompleteContentResourceLinkType, + /// URI identifying the resource + pub uri: String, +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct EmbeddedTextResourceContents { + /// MIME type of the text content + #[serde(skip_serializing_if = "Option::is_none")] + pub mime_type: Option, + /// Text content of the resource + pub text: String, + /// URI identifying the resource + pub uri: String, +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct EmbeddedBlobResourceContents { + /// Base64-encoded binary content of the resource + pub blob: String, + /// MIME type of the blob content + #[serde(skip_serializing_if = "Option::is_none")] + pub mime_type: Option, + /// URI identifying the resource + pub uri: String, +} + +/// Embedded resource content block with inline text or binary data +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ToolExecutionCompleteContentResource { + /// The embedded resource contents, either text or base64-encoded binary + pub resource: ToolExecutionCompleteContentResourceDetails, + /// Content block type discriminator + pub r#type: ToolExecutionCompleteContentResourceType, +} + /// Tool execution result on success #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -1396,7 +1524,7 @@ pub struct ToolExecutionCompleteResult { pub content: String, /// Structured content blocks (text, images, audio, resources) returned by the tool in their native format #[serde(default)] - pub contents: Vec, + pub contents: Vec, /// Full detailed tool result for UI/timeline display, preserving complete content such as diffs. Falls back to content when absent. #[serde(skip_serializing_if = "Option::is_none")] pub detailed_content: Option, @@ -2803,6 +2931,87 @@ pub enum AbortReason { Unknown, } +/// Content block type discriminator +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ToolExecutionCompleteContentTextType { + #[serde(rename = "text")] + #[default] + Text, +} + +/// Content block type discriminator +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ToolExecutionCompleteContentTerminalType { + #[serde(rename = "terminal")] + #[default] + Terminal, +} + +/// Content block type discriminator +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ToolExecutionCompleteContentImageType { + #[serde(rename = "image")] + #[default] + Image, +} + +/// Content block type discriminator +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ToolExecutionCompleteContentAudioType { + #[serde(rename = "audio")] + #[default] + Audio, +} + +/// Theme variant this icon is intended for +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ToolExecutionCompleteContentResourceLinkIconTheme { + #[serde(rename = "light")] + Light, + #[serde(rename = "dark")] + Dark, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + +/// Content block type discriminator +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ToolExecutionCompleteContentResourceLinkType { + #[serde(rename = "resource_link")] + #[default] + ResourceLink, +} + +/// The embedded resource contents, either text or base64-encoded binary +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ToolExecutionCompleteContentResourceDetails { + EmbeddedTextResourceContents(EmbeddedTextResourceContents), + EmbeddedBlobResourceContents(EmbeddedBlobResourceContents), +} + +/// Content block type discriminator +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ToolExecutionCompleteContentResourceType { + #[serde(rename = "resource")] + #[default] + Resource, +} + +/// A content block within a tool result, which may be text, terminal output, image, audio, or a resource +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ToolExecutionCompleteContent { + Text(ToolExecutionCompleteContentText), + Terminal(ToolExecutionCompleteContentTerminal), + Image(ToolExecutionCompleteContentImage), + Audio(ToolExecutionCompleteContentAudio), + ResourceLink(ToolExecutionCompleteContentResourceLink), + Resource(ToolExecutionCompleteContentResource), +} + /// Message role: "system" for system prompts, "developer" for developer-injected instructions #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum SystemMessageRole { diff --git a/scripts/codegen/csharp.ts b/scripts/codegen/csharp.ts index f66043170..9e8b6c400 100644 --- a/scripts/codegen/csharp.ts +++ b/scripts/codegen/csharp.ts @@ -18,7 +18,10 @@ import { getRpcSchemaTypeName, getSessionEventsSchemaPath, writeGeneratedFile, + collectExternalSchemaRefNames, collectDefinitionCollections, + collectReachableDefinitionNames, + findSharedSchemaDefinitions, postProcessSchema, resolveRef, resolveObjectSchema, @@ -34,6 +37,7 @@ import { getNullableInner, getSessionEventVariantSchemas, getSharedSessionEventEnvelopeProperties, + rewriteSharedDefinitionReferences, REPO_ROOT, type ApiSchema, type DefinitionCollections, @@ -156,6 +160,19 @@ function uniqueCSharpIdentifier(value: string, used: Set, fallback: stri return identifier; } +function isNonNullableCSharpValueType(typeName: string): boolean { + return [ + "bool", + "double", + "float", + "Guid", + "int", + "long", + "DateTimeOffset", + "TimeSpan", + ].includes(typeName) || generatedEnums.has(typeName) || emittedRpcEnumResultTypes.has(typeName); +} + async function formatCSharpFile(filePath: string): Promise { try { const projectFile = path.join(REPO_ROOT, "dotnet/src/GitHub.Copilot.SDK.csproj"); @@ -178,6 +195,10 @@ function collectRpcMethods(node: Record): RpcMethod[] { return results; } +function localRequestVariableName(paramEntries: [string, JSONSchema7Definition][], hasRequestParameter = false): string { + return hasRequestParameter || paramEntries.some(([name]) => name === "request") ? "rpcRequest" : "request"; +} + function schemaTypeToCSharp(schema: JSONSchema7, required: boolean, knownTypes: Map): string { const nullableInner = getNullableInner(schema); if (nullableInner) { @@ -708,6 +729,194 @@ function generateDerivedClass( return lines.join("\n"); } +interface JsonUnionVariant { + typeName: string; + propertyName: string; + schema?: JSONSchema7; +} + +function getUnionMembers(schema: JSONSchema7): JSONSchema7[] | undefined { + return (schema.anyOf ?? schema.oneOf) as JSONSchema7[] | undefined; +} + +function getNonNullUnionMembers(schema: JSONSchema7): JSONSchema7[] { + return (getUnionMembers(schema) ?? []).filter((s) => typeof s === "object" && s !== null && (s as JSONSchema7).type !== "null"); +} + +function getVariantSchema(variant: JSONSchema7, definitions: DefinitionCollections): JSONSchema7 | undefined { + if (variant.$ref) { + const resolved = resolveRef(variant.$ref, definitions); + return typeof resolved === "object" && resolved !== null ? resolved : undefined; + } + + const resolved = resolveObjectSchema(variant, definitions) ?? resolveSchema(variant, definitions) ?? variant; + return typeof resolved === "object" && resolved !== null ? resolved : undefined; +} + +function getJsonUnionMatchExpression(variant: JsonUnionVariant, variants: JsonUnionVariant[]): string | undefined { + const required = new Set(variant.schema?.required ?? []); + if (required.size === 0) return undefined; + + const otherRequired = new Set(); + for (const other of variants) { + if (other === variant) continue; + for (const property of other.schema?.required ?? []) { + otherRequired.add(property); + } + } + + const present = [...required].filter((property) => !otherRequired.has(property)); + if (present.length === 0) return undefined; + + const absent = new Set(); + for (const other of variants) { + if (other === variant) continue; + for (const property of other.schema?.required ?? []) { + if (!required.has(property)) absent.add(property); + } + } + + return [ + "element.ValueKind == JsonValueKind.Object", + ...present.map((property) => `element.TryGetProperty("${escapeCSharpStringLiteral(property)}", out _)`), + ...[...absent].sort().map((property) => `!element.TryGetProperty("${escapeCSharpStringLiteral(property)}", out _)`), + ].join(" && "); +} + +function generateJsonUnionClass(className: string, variants: JsonUnionVariant[], description: string | undefined, jsonContextType: string): string { + const lines: string[] = []; + lines.push(...xmlDocCommentWithFallback(description, `JSON union data type for ${escapeXml(className)}.`, "")); + lines.push(`[JsonConverter(typeof(Converter))]`); + lines.push(`public sealed partial class ${className}`); + lines.push(`{`); + + for (const variant of variants) { + lines.push(` /// Gets the value when this instance contains .`); + lines.push(` public ${variant.typeName}? ${variant.propertyName} { get; }`, ""); + } + + for (const variant of variants) { + lines.push(` /// Initializes a new instance of the class from .`); + lines.push(` public ${className}(${variant.typeName} value)`); + lines.push(` {`); + lines.push(` ArgumentNullException.ThrowIfNull(value);`); + lines.push(` ${variant.propertyName} = value;`); + lines.push(` }`, ""); + lines.push(` /// Converts to .`); + lines.push(` public static implicit operator ${className}(${variant.typeName} value) => new(value);`, ""); + } + + lines.push(` /// Provides a for serializing instances.`); + lines.push(` [EditorBrowsable(EditorBrowsableState.Never)]`); + lines.push(` public sealed class Converter : JsonConverter<${className}>`); + lines.push(` {`); + lines.push(` /// `); + lines.push(` public override ${className} Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)`); + lines.push(` {`); + lines.push(` if (reader.TokenType == JsonTokenType.Null)`); + lines.push(` {`); + lines.push(` throw new JsonException("Expected JSON object for ${escapeCSharpStringLiteral(className)}.");`); + lines.push(` }`); + lines.push(``); + lines.push(` using var document = JsonDocument.ParseValue(ref reader);`); + lines.push(` var element = document.RootElement;`); + + const fallbackVariants: JsonUnionVariant[] = []; + for (const variant of variants) { + const matchExpression = getJsonUnionMatchExpression(variant, variants); + if (!matchExpression) { + fallbackVariants.push(variant); + continue; + } + + const valueName = variant.propertyName.charAt(0).toLowerCase() + variant.propertyName.slice(1); + const deserializeExpression = `JsonSerializer.Deserialize(element, ${jsonContextType}.Default.${variant.typeName})`; + lines.push(` if (${matchExpression})`); + lines.push(` {`); + lines.push(` var ${valueName} = ${deserializeExpression};`); + lines.push(` return ${valueName} is null ? throw new JsonException("Expected ${escapeCSharpStringLiteral(variant.typeName)} value.") : new ${className}(${valueName});`); + lines.push(` }`); + } + + for (const variant of fallbackVariants) { + const valueName = variant.propertyName.charAt(0).toLowerCase() + variant.propertyName.slice(1); + const deserializeExpression = `JsonSerializer.Deserialize(element, ${jsonContextType}.Default.${variant.typeName})`; + lines.push(``); + lines.push(` try`); + lines.push(` {`); + lines.push(` var ${valueName} = ${deserializeExpression};`); + lines.push(` if (${valueName} is not null) return new ${className}(${valueName});`); + lines.push(` }`); + lines.push(` catch (JsonException)`); + lines.push(` {`); + lines.push(` }`); + } + + lines.push(``); + lines.push(` throw new JsonException("JSON value did not match any ${escapeCSharpStringLiteral(className)} variant.");`); + lines.push(` }`, ""); + lines.push(` /// `); + lines.push(` public override void Write(Utf8JsonWriter writer, ${className} value, JsonSerializerOptions options)`); + lines.push(` {`); + for (const variant of variants) { + const valueName = variant.propertyName.charAt(0).toLowerCase() + variant.propertyName.slice(1); + const serializeExpression = `JsonSerializer.Serialize(writer, ${valueName}, ${jsonContextType}.Default.${variant.typeName});`; + lines.push(` if (value.${variant.propertyName} is { } ${valueName})`); + lines.push(` {`); + lines.push(` ${serializeExpression}`); + lines.push(` return;`); + lines.push(` }`); + } + lines.push(``); + lines.push(` throw new JsonException("No ${escapeCSharpStringLiteral(className)} variant value is set.");`); + lines.push(` }`); + lines.push(` }`); + lines.push(`}`); + return lines.join("\n"); +} + +function toUnionVariantPropertyName(typeName: string, usedNames: Set): string { + const shortName = typeName.split(".").pop() ?? typeName; + return uniqueCSharpIdentifier(shortName, usedNames, "Value"); +} + +function tryGenerateSessionJsonUnionType( + schema: JSONSchema7, + parentClassName: string, + propName: string, + knownTypes: Map, + nestedClasses: Map, + enumOutput: string[] +): string | undefined { + const members = getNonNullUnionMembers(schema); + if (members.length <= 1) return undefined; + + const className = (schema.title as string) ?? `${parentClassName}${propName}`; + if (nestedClasses.has(className)) return className; + + const usedNames = new Set(); + const variants: JsonUnionVariant[] = []; + for (const member of members) { + const memberSchema = getVariantSchema(member, sessionDefinitions); + const typeName = member.$ref + ? typeToClassName(refTypeName(member.$ref, sessionDefinitions)) + : ((memberSchema?.title as string | undefined) ?? `${className}Variant${variants.length + 1}`); + if (!memberSchema || !isObjectSchema(memberSchema)) return undefined; + + if (!nestedClasses.has(typeName)) { + nestedClasses.set(typeName, generateNestedClass(typeName, memberSchema, knownTypes, nestedClasses, enumOutput)); + } + variants.push({ + typeName, + propertyName: toUnionVariantPropertyName(typeName, usedNames), + schema: memberSchema, + }); + } + + nestedClasses.set(className, generateJsonUnionClass(className, variants, schema.description, "SessionEventsJsonContext")); + return className; +} + function generateNestedClass( className: string, schema: JSONSchema7, @@ -800,6 +1009,13 @@ function resolveSessionPropertyType( return isRequired && !hasNull ? renamedBase : `${renamedBase}?`; } } + const unionType = tryGenerateSessionJsonUnionType(propSchema, parentClassName, propName, knownTypes, nestedClasses, enumOutput); + if (unionType) return isRequired ? unionType : `${unionType}?`; + return !isRequired ? "object?" : "object"; + } + if (propSchema.oneOf) { + const unionType = tryGenerateSessionJsonUnionType(propSchema, parentClassName, propName, knownTypes, nestedClasses, enumOutput); + if (unionType) return isRequired ? unionType : `${unionType}?`; return !isRequired ? "object?" : "object"; } if (propSchema.enum && Array.isArray(propSchema.enum)) { @@ -1034,7 +1250,12 @@ function getMethodResultSchema(method: RpcMethod): JSONSchema7 | undefined { } function resultTypeName(method: RpcMethod): string { - return getRpcSchemaTypeName(getMethodResultSchema(method), `${typeToClassName(method.rpcMethod)}Result`); + return getCSharpSchemaTypeName(getMethodResultSchema(method), `${typeToClassName(method.rpcMethod)}Result`); +} + +function getCSharpSchemaTypeName(schema: JSONSchema7 | null | undefined, fallback: string): string { + if (schema?.$ref) return typeToClassName(refTypeName(schema.$ref, rpcDefinitions)); + return getRpcSchemaTypeName(schema, fallback); } /** Returns the C# type for a method's result, accounting for nullable anyOf wrappers. */ @@ -1065,7 +1286,7 @@ function resultTaskType(method: RpcMethod): string { } function paramsTypeName(method: RpcMethod): string { - return getRpcSchemaTypeName(resolveMethodParamsSchema(method), `${typeToClassName(method.rpcMethod)}Request`); + return getCSharpSchemaTypeName(resolveMethodParamsSchema(method), `${typeToClassName(method.rpcMethod)}Request`); } function resolveMethodParamsSchema(method: RpcMethod): JSONSchema7 | undefined { @@ -1257,6 +1478,8 @@ function emitRpcClass( propAccessors = `{ get => field ??= new ${concreteType}(); set; }`; } else if (emittedRpcClassSchemas.has(csharpType)) { propAccessors = "{ get => field ??= new(); set; }"; + } else if (!isNonNullableCSharpValueType(csharpType)) { + defaultVal = " = null!;"; } } lines.push(` public ${csharpType} ${csharpName} ${propAccessors}${defaultVal}`); @@ -1444,14 +1667,15 @@ function emitServerInstanceMethod( sigParams.push("CancellationToken cancellationToken = default"); const taskType = !isVoidSchema(resultSchema) ? `Task<${resultClassName}>` : "Task"; + const localRequestName = localRequestVariableName(paramEntries); lines.push(`${indent}${methodVisibility} async ${taskType} ${methodName}Async(${sigParams.join(", ")})`); lines.push(`${indent}{`); if (requestClassName && bodyAssignments.length > 0) { - lines.push(`${indent} var request = new ${requestClassName} { ${bodyAssignments.join(", ")} };`); + lines.push(`${indent} var ${localRequestName} = new ${requestClassName} { ${bodyAssignments.join(", ")} };`); if (!isVoidSchema(resultSchema)) { - lines.push(`${indent} return await CopilotClient.InvokeRpcAsync<${resultClassName}>(_rpc, "${method.rpcMethod}", [request], cancellationToken);`); + lines.push(`${indent} return await CopilotClient.InvokeRpcAsync<${resultClassName}>(_rpc, "${method.rpcMethod}", [${localRequestName}], cancellationToken);`); } else { - lines.push(`${indent} await CopilotClient.InvokeRpcAsync(_rpc, "${method.rpcMethod}", [request], cancellationToken);`); + lines.push(`${indent} await CopilotClient.InvokeRpcAsync(_rpc, "${method.rpcMethod}", [${localRequestName}], cancellationToken);`); } } else { if (!isVoidSchema(resultSchema)) { @@ -1570,7 +1794,7 @@ function emitSessionMethod(key: string, method: RpcMethod, lines: string[], clas sigParams.push("CancellationToken cancellationToken = default"); const taskType = !isVoidSchema(resultSchema) ? `Task<${resultClassName}>` : "Task"; - const localRequestName = useRequestParameter ? "rpcRequest" : "request"; + const localRequestName = localRequestVariableName(paramEntries, useRequestParameter); lines.push(`${indent}${methodVisibility} async ${taskType} ${methodName}Async(${sigParams.join(", ")})`); lines.push(`${indent}{`, `${indent} var ${localRequestName} = new ${wireRequestClassName} { ${bodyAssignments.join(", ")} };`); if (!isVoidSchema(resultSchema)) { @@ -1752,7 +1976,10 @@ function emitClientSessionApiRegistration(clientSchema: Record, return lines; } -function generateRpcCode(schema: ApiSchema): string { +function generateRpcCode( + schema: ApiSchema, + externalJsonSerializableRefs: Map> = new Map() +): string { emittedRpcClassSchemas.clear(); emittedRpcEnumResultTypes.clear(); experimentalRpcTypes.clear(); @@ -1811,6 +2038,13 @@ namespace GitHub.Copilot.SDK.Rpc; lines.push(` AllowOutOfOrderMetadataProperties = true,`); lines.push(` DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]`); for (const t of ["bool", "double", "int", "long", "string"]) lines.push(`[JsonSerializable(typeof(${t}))]`); + for (const [schemaFile, names] of externalJsonSerializableRefs) { + if (schemaFile !== "session-events.schema.json") continue; + for (const name of [...names].sort()) { + const typeName = typeToClassName(name); + lines.push(`[JsonSerializable(typeof(GitHub.Copilot.SDK.${typeName}), TypeInfoPropertyName = "SessionEvents${typeName}")]`); + } + } for (const t of typeNames) lines.push(`[JsonSerializable(typeof(${t}))]`); lines.push(`internal partial class RpcJsonContext : JsonSerializerContext;`); } @@ -1818,11 +2052,48 @@ namespace GitHub.Copilot.SDK.Rpc; return lines.join("\n"); } -export async function generateRpc(schemaPath?: string): Promise { +export async function generateRpc(schemaPath?: string, sessionEventsSchema?: JSONSchema7): Promise { console.log("C#: generating RPC types..."); const resolvedPath = schemaPath ?? (await getApiSchemaPath()); - const schema = fixNullableRequiredRefsInApiSchema(cloneSchemaForCodegen(JSON.parse(await fs.readFile(resolvedPath, "utf-8")) as ApiSchema)); - const code = generateRpcCode(schema); + let schema = fixNullableRequiredRefsInApiSchema(cloneSchemaForCodegen(JSON.parse(await fs.readFile(resolvedPath, "utf-8")) as ApiSchema)); + if (sessionEventsSchema) { + const sharedDefinitions = findSharedSchemaDefinitions( + schema as unknown as Record, + sessionEventsSchema as unknown as Record + ); + const reachableDefinitions = collectReachableDefinitionNames(sessionEventsSchema as unknown as Record); + for (const name of [...sharedDefinitions]) { + if (!reachableDefinitions.has(name)) { + sharedDefinitions.delete(name); + } + } + schema = rewriteSharedDefinitionReferences(schema, sharedDefinitions, "session-events.schema.json"); + } + const externalJsonSerializableRefs = new Map>(); + if (sessionEventsSchema) { + const sessionEventsCode = generateSessionEventsCode(sessionEventsSchema); + const externalRefs = collectExternalSchemaRefNames(schema); + const sessionEventRefs = externalRefs.get("session-events.schema.json"); + if (sessionEventRefs && sessionEventRefs.size > 0) { + const reachableDefinitions = collectReachableDefinitionNames( + sessionEventsSchema as unknown as Record, + sessionEventRefs + ); + const emittedDefinitions = new Set(); + for (const name of reachableDefinitions) { + const typeName = typeToClassName(name); + const declarationPattern = new RegExp(`\\bpublic\\s+(?:(?:sealed|abstract|partial|readonly)\\s+)*(?:class|struct)\\s+${typeName}\\b`); + if (declarationPattern.test(sessionEventsCode)) { + emittedDefinitions.add(name); + } + } + externalJsonSerializableRefs.set( + "session-events.schema.json", + emittedDefinitions + ); + } + } + const code = generateRpcCode(schema, externalJsonSerializableRefs); const outPath = await writeGeneratedFile("dotnet/src/Generated/Rpc.cs", code); console.log(` ✓ ${outPath}`); await formatCSharpFile(outPath); @@ -1835,7 +2106,9 @@ export async function generateRpc(schemaPath?: string): Promise { async function generate(sessionSchemaPath?: string, apiSchemaPath?: string): Promise { await generateSessionEvents(sessionSchemaPath); try { - await generateRpc(apiSchemaPath); + const resolvedSessionPath = sessionSchemaPath ?? (await getSessionEventsSchemaPath()); + const sessionSchema = postProcessSchema(cloneSchemaForCodegen(JSON.parse(await fs.readFile(resolvedSessionPath, "utf-8")) as JSONSchema7)); + await generateRpc(apiSchemaPath, sessionSchema); } catch (err) { if ((err as NodeJS.ErrnoException).code === "ENOENT" && !apiSchemaPath) { console.log("C#: skipping RPC (api.schema.json not found)"); diff --git a/scripts/codegen/go.ts b/scripts/codegen/go.ts index c4643c320..717e0a82d 100644 --- a/scripts/codegen/go.ts +++ b/scripts/codegen/go.ts @@ -13,9 +13,12 @@ import { promisify } from "util"; import wordwrap from "wordwrap"; import { cloneSchemaForCodegen, + collectExternalSchemaRefNames, collectDefinitionCollections, + collectReachableDefinitionNames, filterNodeByVisibility, fixNullableRequiredRefsInApiSchema, + findSharedSchemaDefinitions, getApiSchemaPath, getNullableInner, getRpcSchemaTypeName, @@ -29,11 +32,13 @@ import { isSchemaDeprecated, isSchemaExperimental, isVoidSchema, + parseExternalSchemaRef, postProcessSchema, refTypeName, resolveObjectSchema, resolveRef, resolveSchema, + rewriteSharedDefinitionReferences, writeGeneratedFile, type ApiSchema, type DefinitionCollections, @@ -43,6 +48,17 @@ import { const execFileAsync = promisify(execFile); +interface GoExternalSchemaImport { + path: string; + qualifier: string; + packageName: string; +} + +const EXTERNAL_SCHEMA_GO_IMPORT: Record = { + "api.schema.json": { path: "github.com/github/copilot-sdk/go/rpc", qualifier: "rpc", packageName: "rpc" }, + "session-events.schema.json": { path: "github.com/github/copilot-sdk/go/rpc", qualifier: "rpc", packageName: "rpc" }, +}; + // ── Utilities ─────────────────────────────────────────────────────────────── // Go initialisms that should be all-caps @@ -69,6 +85,20 @@ function toGoFieldName(jsonName: string): string { .join(""); } +function goRefTypeName(ref: string, definitions?: DefinitionCollections, currentPackage?: string): string { + const externalRef = parseExternalSchemaRef(ref); + if (externalRef) { + const externalImport = EXTERNAL_SCHEMA_GO_IMPORT[externalRef.schemaFile]; + const typeName = toGoFieldName(externalRef.definitionName); + if (externalImport && externalImport.packageName !== currentPackage) { + return `${externalImport.qualifier}.${typeName}`; + } + return typeName; + } + + return toGoFieldName(refTypeName(ref, definitions)); +} + function compareGoFieldNames(left: string, right: string): number { return left.localeCompare(right); } @@ -241,6 +271,10 @@ function collectRpcMethods(node: Record): RpcMethod[] { } let rpcDefinitions: DefinitionCollections = { definitions: {}, $defs: {} }; +let rpcSessionEventTopLevelNames: { types: Set; consts: Set } = { + types: new Set(), + consts: new Set(), +}; function withRootTitle(schema: JSONSchema7, title: string): JSONSchema7 { return { ...schema, title }; @@ -378,6 +412,8 @@ interface GoCodegenCtx { wrapComments?: boolean; discriminatedUnionRawVariantSuffix?: string; skipDefinitionTypeNames?: Set; + encodingBlocks?: Set; + packageName?: string; } function extractGoEventVariants(schema: JSONSchema7): GoEventVariant[] { @@ -665,12 +701,18 @@ function resolveGoPropertyType( // Handle $ref — resolve the reference and generate the referenced type if (propSchema.$ref && typeof propSchema.$ref === "string") { - const typeName = toGoFieldName(refTypeName(propSchema.$ref, ctx.definitions)); + const typeName = goRefTypeName(propSchema.$ref, ctx.definitions, ctx.packageName); const resolved = resolveRef(propSchema.$ref, ctx.definitions); if (resolved) { if (resolved.enum) { - const enumType = getOrCreateGoEnum(typeName, resolved.enum as string[], ctx, resolved.description, isSchemaDeprecated(resolved), isSchemaExperimental(resolved)); - return isRequired ? enumType : `*${enumType}`; + if ((resolved.enum as unknown[]).every((value) => typeof value === "string")) { + const enumType = getOrCreateGoEnum(typeName, resolved.enum as string[], ctx, resolved.description, isSchemaDeprecated(resolved), isSchemaExperimental(resolved)); + return isRequired ? enumType : `*${enumType}`; + } + if (resolved.enum.length === 1) { + return resolveGoPropertyType(schemaForConstValue(resolved.enum[0]), parentTypeName, jsonPropName, isRequired, ctx); + } + return "any"; } if (isNamedGoObjectSchema(resolved)) { emitGoStruct(typeName, resolved, ctx); @@ -723,8 +765,14 @@ function resolveGoPropertyType( // Handle enum if (propSchema.enum && Array.isArray(propSchema.enum)) { - const enumType = getOrCreateGoEnum((propSchema.title as string) || nestedName, propSchema.enum as string[], ctx, propSchema.description, isSchemaDeprecated(propSchema), isSchemaExperimental(propSchema)); - return isRequired ? enumType : `*${enumType}`; + if ((propSchema.enum as unknown[]).every((value) => typeof value === "string")) { + const enumType = getOrCreateGoEnum((propSchema.title as string) || nestedName, propSchema.enum as string[], ctx, propSchema.description, isSchemaDeprecated(propSchema), isSchemaExperimental(propSchema)); + return isRequired ? enumType : `*${enumType}`; + } + if (propSchema.enum.length === 1) { + return resolveGoPropertyType(schemaForConstValue(propSchema.enum[0]), parentTypeName, jsonPropName, isRequired, ctx); + } + return "any"; } // Handle const values. String consts stay enum-like to preserve generated names for @@ -863,7 +911,11 @@ function goDiscriminatedUnionField(goType: string, ctx: GoCodegenCtx): GoDiscrim function pushGoEncodingBlock(blockLines: string[], ctx: GoCodegenCtx): void { if (blockLines.length === 0) return; - ctx.encoding.push(blockLines.join("\n")); + const block = blockLines.join("\n"); + ctx.encodingBlocks ??= new Set(); + if (ctx.encodingBlocks.has(block)) return; + ctx.encodingBlocks.add(block); + ctx.encoding.push(block); } function pushGoStructUnmarshalJSON(lines: string[], typeName: string, fields: GoStructField[], ctx: GoCodegenCtx): void { @@ -1636,40 +1688,43 @@ function emitGoFlatDiscriminatedUnion( for (const mappedVariant of unionVariants) { const variant = mappedVariant.schema; const variantTypeName = mappedVariant.typeName; - if (variant.description) { - pushGoCommentForContext(lines, variant.description, ctx); - } - ctx.generatedNames.add(variantTypeName); - lines.push(`type ${variantTypeName} struct {`); - const required = new Set(variant.required || []); - const fields: GoStructField[] = []; - for (const [propName, propSchema] of sortByGoFieldName(Object.entries(variant.properties || {}))) { - if (typeof propSchema !== "object") continue; - const prop = propSchema as JSONSchema7; - if (propName === discriminatorProp) { - if (mappedVariant.discriminatorValues.length <= 1) continue; - const goType = resolveGoPropertyType(prop, variantTypeName, propName, true, ctx); - const jsonTag = `json:"${propName},omitempty"`; - lines.push(`\tDiscriminator ${goType} \`${jsonTag}\``); - fields.push({ propName, goName: "Discriminator", goType, jsonTag }); - continue; + const variantAlreadyGenerated = ctx.generatedNames.has(variantTypeName); + if (!variantAlreadyGenerated) { + if (variant.description) { + pushGoCommentForContext(lines, variant.description, ctx); } - const goName = toGoFieldName(propName); - const goType = resolveGoPropertyType(prop, variantTypeName, propName, required.has(propName), ctx); - const omit = required.has(propName) ? "" : ",omitempty"; - if (prop.description) { - pushGoCommentForContext(lines, prop.description, ctx, "\t"); - } - if (isSchemaDeprecated(prop)) { - pushGoCommentForContext(lines, `Deprecated: ${goName} is deprecated.`, ctx, "\t"); + ctx.generatedNames.add(variantTypeName); + lines.push(`type ${variantTypeName} struct {`); + const required = new Set(variant.required || []); + const fields: GoStructField[] = []; + for (const [propName, propSchema] of sortByGoFieldName(Object.entries(variant.properties || {}))) { + if (typeof propSchema !== "object") continue; + const prop = propSchema as JSONSchema7; + if (propName === discriminatorProp) { + if (mappedVariant.discriminatorValues.length <= 1) continue; + const goType = resolveGoPropertyType(prop, variantTypeName, propName, true, ctx); + const jsonTag = `json:"${propName},omitempty"`; + lines.push(`\tDiscriminator ${goType} \`${jsonTag}\``); + fields.push({ propName, goName: "Discriminator", goType, jsonTag }); + continue; + } + const goName = toGoFieldName(propName); + const goType = resolveGoPropertyType(prop, variantTypeName, propName, required.has(propName), ctx); + const omit = required.has(propName) ? "" : ",omitempty"; + if (prop.description) { + pushGoCommentForContext(lines, prop.description, ctx, "\t"); + } + if (isSchemaDeprecated(prop)) { + pushGoCommentForContext(lines, `Deprecated: ${goName} is deprecated.`, ctx, "\t"); + } + const jsonTag = `json:"${propName}${omit}"`; + lines.push(`\t${goName} ${goType} \`${jsonTag}\``); + fields.push({ propName, goName, goType, jsonTag }); } - const jsonTag = `json:"${propName}${omit}"`; - lines.push(`\t${goName} ${goType} \`${jsonTag}\``); - fields.push({ propName, goName, goType, jsonTag }); + lines.push(`}`); + pushGoStructUnmarshalJSON(lines, variantTypeName, fields, ctx); + lines.push(``); } - lines.push(`}`); - pushGoStructUnmarshalJSON(lines, variantTypeName, fields, ctx); - lines.push(``); lines.push(`func (${variantTypeName}) ${markerName}() {}`); const defaultConstName = goDiscriminatorValueExpr(mappedVariant.discriminatorValues[0], discEnumName); if (mappedVariant.discriminatorValues.length <= 1) { @@ -1770,32 +1825,35 @@ function emitGoRequiredFieldDiscriminatedUnion( for (const mappedVariant of unionVariants) { const variant = mappedVariant.schema; const variantTypeName = mappedVariant.typeName; - if (variant.description) { - pushGoCommentForContext(lines, variant.description, ctx); - } - ctx.generatedNames.add(variantTypeName); - lines.push(`type ${variantTypeName} struct {`); - const required = new Set(variant.required || []); - const fields: GoStructField[] = []; - for (const [propName, propSchema] of sortByGoFieldName(Object.entries(variant.properties || {}))) { - if (typeof propSchema !== "object") continue; - const prop = propSchema as JSONSchema7; - const goName = toGoFieldName(propName); - const goType = resolveGoPropertyType(prop, variantTypeName, propName, required.has(propName), ctx); - const omit = required.has(propName) ? "" : ",omitempty"; - if (prop.description) { - pushGoCommentForContext(lines, prop.description, ctx, "\t"); + const variantAlreadyGenerated = ctx.generatedNames.has(variantTypeName); + if (!variantAlreadyGenerated) { + if (variant.description) { + pushGoCommentForContext(lines, variant.description, ctx); } - if (isSchemaDeprecated(prop)) { - pushGoCommentForContext(lines, `Deprecated: ${goName} is deprecated.`, ctx, "\t"); + ctx.generatedNames.add(variantTypeName); + lines.push(`type ${variantTypeName} struct {`); + const required = new Set(variant.required || []); + const fields: GoStructField[] = []; + for (const [propName, propSchema] of sortByGoFieldName(Object.entries(variant.properties || {}))) { + if (typeof propSchema !== "object") continue; + const prop = propSchema as JSONSchema7; + const goName = toGoFieldName(propName); + const goType = resolveGoPropertyType(prop, variantTypeName, propName, required.has(propName), ctx); + const omit = required.has(propName) ? "" : ",omitempty"; + if (prop.description) { + pushGoCommentForContext(lines, prop.description, ctx, "\t"); + } + if (isSchemaDeprecated(prop)) { + pushGoCommentForContext(lines, `Deprecated: ${goName} is deprecated.`, ctx, "\t"); + } + const jsonTag = `json:"${propName}${omit}"`; + lines.push(`\t${goName} ${goType} \`${jsonTag}\``); + fields.push({ propName, goName, goType, jsonTag }); } - const jsonTag = `json:"${propName}${omit}"`; - lines.push(`\t${goName} ${goType} \`${jsonTag}\``); - fields.push({ propName, goName, goType, jsonTag }); + lines.push(`}`); + pushGoStructUnmarshalJSON(lines, variantTypeName, fields, ctx); + lines.push(``); } - lines.push(`}`); - pushGoStructUnmarshalJSON(lines, variantTypeName, fields, ctx); - lines.push(``); lines.push(`func (${variantTypeName}) ${markerName}() {}`); lines.push(``); } @@ -1889,6 +1947,10 @@ function goNonNullUnionMembers(schema: JSONSchema7): JSONSchema7[] { }) ?? []; } +function goUnionHasExternalRef(members: JSONSchema7[]): boolean { + return members.some((member) => typeof member.$ref === "string" && parseExternalSchemaRef(member.$ref) !== undefined); +} + function collectGoDiscriminatedUnionVariantDefinitionTypeNames( definitions: Record, ctx: GoCodegenCtx @@ -1919,6 +1981,13 @@ function collectGoDiscriminatedUnionVariantDefinitionTypeNames( function resolveGoUnionMember(member: JSONSchema7, definitions: DefinitionCollections | undefined): JSONSchema7 { if (member.$ref) { + const externalRef = parseExternalSchemaRef(member.$ref); + if (externalRef) { + const localDefinition = definitions?.definitions?.[externalRef.definitionName] ?? definitions?.$defs?.[externalRef.definitionName]; + if (localDefinition && typeof localDefinition === "object") { + return localDefinition as JSONSchema7; + } + } return resolveRef(member.$ref, definitions) ?? member; } return member; @@ -2487,6 +2556,10 @@ function planGoUnion(typeName: string, schema: JSONSchema7, ctx: GoCodegenCtx, i return { kind: "primitive", typeName, schema, description, variants: primitiveVariants }; } + if (goUnionHasExternalRef(members)) { + return includeWrapper ? { kind: "wrapper", typeName, schema, description } : undefined; + } + const requiredFieldDiscriminator = findGoRequiredFieldDiscriminator(members, ctx, typeName); if (requiredFieldDiscriminator) { return { kind: "requiredFieldDiscriminated", typeName, schema, description, discriminator: requiredFieldDiscriminator }; @@ -2559,7 +2632,7 @@ function emitGoUnionWrapperStruct(typeName: string, schema: JSONSchema7, ctx: Go lines.push(`type ${typeName} struct {`); const emittedFields = new Set(); - const fields: { name: string; type: string }[] = []; + const fields: { name: string; type: string; member: JSONSchema7 }[] = []; for (const member of members) { const fieldNameBase = goUnionFieldName(member, ctx); let fieldName = fieldNameBase; @@ -2569,7 +2642,7 @@ function emitGoUnionWrapperStruct(typeName: string, schema: JSONSchema7, ctx: Go } emittedFields.add(fieldName); const fieldType = goUnionFieldType(member, fieldName, typeName, ctx); - fields.push({ name: fieldName, type: fieldType }); + fields.push({ name: fieldName, type: fieldType, member }); } fields.sort((left, right) => compareGoFieldNames(left.name, right.name)); @@ -2579,6 +2652,24 @@ function emitGoUnionWrapperStruct(typeName: string, schema: JSONSchema7, ctx: Go lines.push(`}`); const encodingLines: string[] = []; + const matchFunctionsByField = new Map(); + const objectVariantSchemas = fields.map((field) => ({ + field, + schema: goObjectUnionMemberSchema(field.member, ctx), + })); + if (objectVariantSchemas.length > 1 && objectVariantSchemas.every((variant) => variant.schema !== undefined)) { + const matchVariants: GoDiscriminatedUnionVariant[] = objectVariantSchemas.map(({ field, schema }) => ({ + schema: schema!, + typeName: `${typeName}${field.name}`, + discriminatorValues: [], + })); + for (const variant of matchVariants) { + pushGoEncodingBlock(goVariantMatchFunctionLines(variant, matchVariants, "", ctx), ctx); + } + for (const [index, variant] of matchVariants.entries()) { + matchFunctionsByField.set(objectVariantSchemas[index].field.name, goVariantMatchFuncName(variant.typeName)); + } + } encodingLines.push(`func (r ${typeName}) MarshalJSON() ([]byte, error) {`); for (const field of fields) { encodingLines.push(`\tif ${goUnionFieldMarshalIsSet(field.name, field.type, ctx)} {`); @@ -2594,13 +2685,25 @@ function emitGoUnionWrapperStruct(typeName: string, schema: JSONSchema7, ctx: Go encodingLines.push(`\t\treturn nil`); encodingLines.push(`\t}`); for (const field of fields) { - encodingLines.push(`\t{`); - encodingLines.push(`\t\tvar value ${goUnionFieldUnmarshalType(field.type)}`); - encodingLines.push(`\t\tif err := json.Unmarshal(data, &value); err == nil {`); - encodingLines.push(`\t\t\t${goUnionFieldUnmarshalAssignment(typeName, field.name, field.type)}`); - encodingLines.push(`\t\t\treturn nil`); - encodingLines.push(`\t\t}`); - encodingLines.push(`\t}`); + const matchFunction = matchFunctionsByField.get(field.name); + if (matchFunction) { + encodingLines.push(`\tif ${matchFunction}(data) {`); + encodingLines.push(`\t\tvar value ${goUnionFieldUnmarshalType(field.type)}`); + encodingLines.push(`\t\tif err := json.Unmarshal(data, &value); err != nil {`); + encodingLines.push(`\t\t\treturn err`); + encodingLines.push(`\t\t}`); + encodingLines.push(`\t\t${goUnionFieldUnmarshalAssignment(typeName, field.name, field.type)}`); + encodingLines.push(`\t\treturn nil`); + encodingLines.push(`\t}`); + } else { + encodingLines.push(`\t{`); + encodingLines.push(`\t\tvar value ${goUnionFieldUnmarshalType(field.type)}`); + encodingLines.push(`\t\tif err := json.Unmarshal(data, &value); err == nil {`); + encodingLines.push(`\t\t\t${goUnionFieldUnmarshalAssignment(typeName, field.name, field.type)}`); + encodingLines.push(`\t\t\treturn nil`); + encodingLines.push(`\t\t}`); + encodingLines.push(`\t}`); + } } encodingLines.push(`\treturn errors.New("data did not match any union variant for ${typeName}")`); encodingLines.push(`}`); @@ -2703,6 +2806,9 @@ function goGeneratedEncodingFileCode(schemaFileName: string, packageName: string if (generatedEncodingCode.includes("time.Time")) { imports.push(`"time"`); } + if (packageName !== "rpc" && generatedEncodingCode.includes("rpc.")) { + imports.push(`"github.com/github/copilot-sdk/go/rpc"`); + } lines.push(`import (`); for (const imp of imports) { lines.push(`\t${imp}`); @@ -2724,6 +2830,7 @@ function generateGoRpcTypeCode(definitions: Record, definit discriminatedUnions: new Map(), generatedNames: new Set(), definitions: definitionCollections, + packageName: "rpc", }; ctx.skipDefinitionTypeNames = collectGoDiscriminatedUnionVariantDefinitionTypeNames(definitions, ctx); const schemaKeysByTypeName = new Map(); @@ -2760,7 +2867,7 @@ function goDeclaredTypeName(code: string): string { /** * Generate the complete Go session-events file content. */ -function generateGoSessionEventsCode(schema: JSONSchema7): GoGeneratedTypeCode { +function generateGoSessionEventsCode(schema: JSONSchema7, packageName: string): GoGeneratedTypeCode { const variants = extractGoEventVariants(schema); const ctx: GoCodegenCtx = { structs: [], @@ -2772,6 +2879,7 @@ function generateGoSessionEventsCode(schema: JSONSchema7): GoGeneratedTypeCode { definitions: collectDefinitionCollections(schema as Record), wrapComments: false, discriminatedUnionRawVariantSuffix: "", + packageName, }; const envelopeProperties = getGoSharedEventEnvelopeProperties(schema, ctx); const sessionEventStructFields = [ @@ -2890,15 +2998,24 @@ function generateGoSessionEventsCode(schema: JSONSchema7): GoGeneratedTypeCode { // Assemble file const out: string[] = []; + const externalImports = [...collectExternalSchemaRefNames(schema).keys()] + .map((schemaFile) => EXTERNAL_SCHEMA_GO_IMPORT[schemaFile]) + .filter((externalImport): externalImport is GoExternalSchemaImport => Boolean(externalImport)) + .filter((externalImport) => externalImport.packageName !== packageName) + .sort((left, right) => left.path.localeCompare(right.path)); out.push(...goDoNotEditHeader("session-events.schema.json")); out.push(``); - out.push(`package copilot`); + out.push(`package ${packageName}`); out.push(``); // Imports — time is always needed for SessionEvent.Timestamp out.push(`import (`); out.push(`\t"encoding/json"`); out.push(`\t"time"`); + for (const externalImport of externalImports) { + out.push(``); + out.push(`\t"${externalImport.path}"`); + } out.push(`)`); out.push(``); @@ -3082,26 +3199,173 @@ function generateGoSessionEventsCode(schema: JSONSchema7): GoGeneratedTypeCode { }; } -async function generateSessionEvents(schemaPath?: string): Promise { +function collectGoTopLevelNames(code: string, keyword: "type" | "const"): string[] { + const names = new Set(); + const lines = code.split(/\r?\n/); + let inBlock = false; + + for (const line of lines) { + if (inBlock) { + if (/^\)/.test(line)) { + inBlock = false; + continue; + } + + const blockMatch = /^\t([A-Z]\w*)\b/.exec(line); + if (blockMatch) { + names.add(blockMatch[1]); + } + continue; + } + + if (new RegExp(`^${keyword}\\s*\\(`).test(line)) { + inBlock = true; + continue; + } + + const singleMatch = new RegExp(`^${keyword}\\s+([A-Z]\\w*)\\b`).exec(line); + if (singleMatch) { + names.add(singleMatch[1]); + } + } + + return [...names].sort(compareGoTypeNames); +} + +function generateGoSessionEventAliasFile( + generatedSessionTypeCode: string, + additionalTypeNames: Iterable = [], + additionalConstNames: Iterable = [] +): string { + const typeNames = [...new Set([...collectGoTopLevelNames(generatedSessionTypeCode, "type"), ...additionalTypeNames])] + .sort(compareGoTypeNames); + const constNames = [...new Set([...collectGoTopLevelNames(generatedSessionTypeCode, "const"), ...additionalConstNames])] + .sort(compareGoTypeNames); + const lines: string[] = []; + + lines.push(...goDoNotEditHeader("session-events.schema.json")); + lines.push(``); + lines.push(`package copilot`); + lines.push(``); + lines.push(`import "github.com/github/copilot-sdk/go/rpc"`); + lines.push(``); + + if (typeNames.length > 0) { + lines.push(`// Session-event types are generated in the rpc package and aliased here for source compatibility.`); + lines.push(`type (`); + for (const typeName of typeNames) { + lines.push(`\t${typeName} = rpc.${typeName}`); + } + lines.push(`)`); + lines.push(``); + } + + if (constNames.length > 0) { + lines.push(`// Session-event constants are generated in the rpc package and re-exported here for source compatibility.`); + lines.push(`const (`); + for (const constName of constNames) { + lines.push(`\t${constName} = rpc.${constName}`); + } + lines.push(`)`); + lines.push(``); + } + + return joinGoCode(lines); +} + +function collectGoSharedSessionEventAliasNames( + sharedDefinitionNames: Iterable, + apiSchema: ApiSchema +): { typeNames: string[]; constNames: string[] } { + const apiDefinitions = collectDefinitionCollections(apiSchema as Record); + const definitions = { ...apiDefinitions.$defs, ...apiDefinitions.definitions }; + const typeNames = new Set(); + const constNames = new Set(); + + for (const definitionName of sharedDefinitionNames) { + const typeName = toGoFieldName(definitionName); + typeNames.add(typeName); + + const definition = definitions[definitionName]; + if (!definition || typeof definition !== "object" || Array.isArray(definition)) continue; + + const schema = definition as JSONSchema7; + const values = isStringEnumDefinition(schema) + ? schema.enum + : typeof schema.const === "string" + ? [schema.const] + : undefined; + for (const value of values ?? []) { + constNames.add(`${typeName}${goEnumConstSuffix(value)}`); + } + } + + return { + typeNames: [...typeNames].sort(compareGoTypeNames), + constNames: [...constNames].sort(compareGoTypeNames), + }; +} + +function assertNoGoRpcSessionEventConflicts(rpcGeneratedTypeCode: string): void { + const duplicateTypes = collectGoTopLevelNames(rpcGeneratedTypeCode, "type") + .filter((name) => rpcSessionEventTopLevelNames.types.has(name)); + const duplicateConsts = collectGoTopLevelNames(rpcGeneratedTypeCode, "const") + .filter((name) => rpcSessionEventTopLevelNames.consts.has(name)); + + if (duplicateTypes.length > 0 || duplicateConsts.length > 0) { + const details = [ + duplicateTypes.length > 0 ? `types: ${duplicateTypes.join(", ")}` : undefined, + duplicateConsts.length > 0 ? `consts: ${duplicateConsts.join(", ")}` : undefined, + ].filter(Boolean).join("; "); + throw new Error(`Generated Go rpc package has duplicate session-event/API declarations (${details}). Shared definitions must be referenced once, not emitted twice.`); + } +} + +async function generateSessionEvents(schemaPath?: string, apiSchema?: ApiSchema): Promise { console.log("Go: generating session-events..."); const resolvedPath = schemaPath ?? (await getSessionEventsSchemaPath()); const schema = cloneSchemaForCodegen(JSON.parse(await fs.readFile(resolvedPath, "utf-8")) as JSONSchema7); const processed = postProcessSchema(schema); + const sharedDefinitions = apiSchema + ? findSharedSchemaDefinitions( + processed as unknown as Record, + postProcessSchema(cloneSchemaForCodegen(apiSchema as JSONSchema7)) as unknown as Record + ) + : new Set(); + const reachableDefinitions = collectReachableDefinitionNames(processed as unknown as Record); + const sharedSessionEventDefinitions = new Set([...sharedDefinitions].filter((name) => reachableDefinitions.has(name))); + const sessionSchema = rewriteSharedDefinitionReferences(processed, sharedDefinitions, "api.schema.json", true); - const generatedSessionCode = generateGoSessionEventsCode(processed); + const generatedSessionCode = generateGoSessionEventsCode(sessionSchema, "rpc"); const generatedTypeCode = stripTrailingGoWhitespace(generatedSessionCode.typeCode); const generatedEncodingCode = stripTrailingGoWhitespace(generatedSessionCode.encodingCode); + rpcSessionEventTopLevelNames = { + types: new Set(collectGoTopLevelNames(generatedTypeCode, "type")), + consts: new Set(collectGoTopLevelNames(generatedTypeCode, "const")), + }; - const outPath = await writeGeneratedFile("go/zsession_events.go", generatedTypeCode); - console.log(` ✓ ${outPath}`); + const rpcOutPath = await writeGeneratedFile("go/rpc/zsession_events.go", generatedTypeCode); + console.log(` ✓ ${rpcOutPath}`); - await formatGoFile(outPath); + await formatGoFile(rpcOutPath); - const encodingOutPath = await writeGeneratedFile("go/zsession_encoding.go", goGeneratedEncodingFileCode("session-events.schema.json", "copilot", generatedEncodingCode)); - console.log(` ✓ ${encodingOutPath}`); + const rpcEncodingOutPath = await writeGeneratedFile("go/rpc/zsession_encoding.go", goGeneratedEncodingFileCode("session-events.schema.json", "rpc", generatedEncodingCode, true)); + console.log(` ✓ ${rpcEncodingOutPath}`); + + await formatGoFile(rpcEncodingOutPath); + + const sharedAliasNames = apiSchema + ? collectGoSharedSessionEventAliasNames(sharedSessionEventDefinitions, apiSchema) + : { typeNames: [], constNames: [] }; + const aliasOutPath = await writeGeneratedFile( + "go/zsession_events.go", + generateGoSessionEventAliasFile(generatedTypeCode, sharedAliasNames.typeNames, sharedAliasNames.constNames) + ); + console.log(` ✓ ${aliasOutPath}`); + + await formatGoFile(aliasOutPath); - await formatGoFile(encodingOutPath); } // ── RPC Types ─────────────────────────────────────────────────────────────── @@ -3253,6 +3517,7 @@ async function generateRpc(schemaPath?: string): Promise { } // Remove trailing blank lines before appending. generatedTypeCode = generatedTypeCode.replace(/\n+$/, ""); + assertNoGoRpcSessionEventConflicts(generatedTypeCode); // Build method wrappers const lines: string[] = []; @@ -3642,7 +3907,17 @@ function emitClientSessionApiRegistration(lines: string[], clientSchema: Record< // ── Main ──────────────────────────────────────────────────────────────────── async function generate(sessionSchemaPath?: string, apiSchemaPath?: string): Promise { - await generateSessionEvents(sessionSchemaPath); + let apiSchemaForSharing: ApiSchema | undefined; + try { + const resolvedApiPath = apiSchemaPath ?? (await getApiSchemaPath()); + apiSchemaForSharing = fixNullableRequiredRefsInApiSchema(cloneSchemaForCodegen(JSON.parse(await fs.readFile(resolvedApiPath, "utf-8")) as ApiSchema)); + } catch (err) { + if ((err as NodeJS.ErrnoException).code !== "ENOENT" || apiSchemaPath) { + throw err; + } + } + + await generateSessionEvents(sessionSchemaPath, apiSchemaForSharing); try { await generateRpc(apiSchemaPath); } catch (err) { diff --git a/scripts/codegen/python.ts b/scripts/codegen/python.ts index 6c0a4b572..ebad322ff 100644 --- a/scripts/codegen/python.ts +++ b/scripts/codegen/python.ts @@ -29,10 +29,14 @@ import { stripBooleanLiterals, writeGeneratedFile, collectDefinitionCollections, + collectReachableDefinitionNames, + findSharedSchemaDefinitions, hasSchemaPayload, + parseExternalSchemaRef, refTypeName, resolveObjectSchema, resolveSchema, + rewriteSharedDefinitionReferences, withSharedDefinitions, getSessionEventVariantSchemas, getSharedSessionEventEnvelopeProperties, @@ -44,12 +48,168 @@ import { // ── Utilities ─────────────────────────────────────────────────────────────── +const EXTERNAL_SCHEMA_PY_MODULE: Record = { + "session-events.schema.json": ".session_events", +}; + type PyExperimentalSubject = "type" | "enum" | "event"; function pyExperimentalComment(subject: PyExperimentalSubject, indent = ""): string { return `${indent}# Experimental: this ${subject} is part of an experimental API and may change or be removed.`; } +function rewriteExternalRefsForPython(schema: JSONSchema7 & { definitions?: Record }): { + placeholderNames: Map; + imports: Map>; +} { + const placeholderNames = new Map(); + const imports = new Map>(); + const placeholderFor = (typeName: string): string => `__ExternalRef_${typeName}`; + + const visit = (value: unknown): void => { + if (Array.isArray(value)) { + for (const item of value) visit(item); + return; + } + if (!value || typeof value !== "object") return; + + const node = value as Record; + if (typeof node.$ref === "string" && !node.$ref.startsWith("#")) { + const externalRef = parseExternalSchemaRef(node.$ref); + const module = externalRef ? EXTERNAL_SCHEMA_PY_MODULE[externalRef.schemaFile] : undefined; + if (externalRef && module) { + const placeholder = placeholderFor(externalRef.definitionName); + placeholderNames.set(placeholder, externalRef.definitionName); + let bucket = imports.get(module); + if (!bucket) { + bucket = new Set(); + imports.set(module, bucket); + } + bucket.add(externalRef.definitionName); + node.$ref = `#/definitions/${placeholder}`; + } + } + + for (const child of Object.values(node)) visit(child); + }; + + visit(schema); + + if (placeholderNames.size > 0) { + if (!schema.definitions) schema.definitions = {}; + for (const placeholder of placeholderNames.keys()) { + if (!schema.definitions[placeholder]) { + const markerProperty = `__externalRefMarker_${placeholder}`; + schema.definitions[placeholder] = { + type: "object", + additionalProperties: false, + title: placeholder, + properties: { + [markerProperty]: { type: "string" }, + }, + required: [markerProperty], + }; + } + } + } + + return { placeholderNames, imports }; +} + +function placeholderToQuicktypeIdentifier(placeholder: string): string { + return placeholder + .replace(/^_+/, "") + .split("_") + .map((segment) => (segment ? segment[0].toUpperCase() + segment.slice(1) : "")) + .join(""); +} + +function postProcessExternalRefsForPython(code: string, placeholderToReal: Map): string { + for (const [placeholder, realName] of placeholderToReal) { + const quicktypeName = placeholderToQuicktypeIdentifier(placeholder); + code = code.replace( + new RegExp( + `(?:^|\\n)@dataclass\\r?\\nclass ${quicktypeName}\\b[\\s\\S]*?(?=\\n@dataclass\\b|\\nclass\\s+\\w|\\ndef\\s+\\w|$)`, + "g" + ), + "\n" + ); + code = code.replace( + new RegExp( + `(?:^|\\n)class ${quicktypeName}\\w*\\(Enum\\):[\\s\\S]*?(?=\\nclass\\s+\\w|\\n@dataclass\\b|\\ndef\\s+\\w|$)`, + "g" + ), + "\n" + ); + code = code.replace(new RegExp(`\\b${quicktypeName}\\b`, "g"), realName); + } + + return code.replace(/\n{3,}/g, "\n\n"); +} + +function collectExternalUnionAliasesForPython( + definitions: Record, + placeholderToReal: Map +): Map { + const aliases = new Map(); + for (const [definitionName, definition] of Object.entries(definitions)) { + const variants = definition.anyOf ?? definition.oneOf; + if (!Array.isArray(variants)) continue; + + const realNames: string[] = []; + let allExternal = true; + for (const variant of variants) { + if (!variant || typeof variant !== "object") { + allExternal = false; + break; + } + const ref = (variant as JSONSchema7).$ref; + if (!ref?.startsWith("#/definitions/")) { + allExternal = false; + break; + } + const placeholder = ref.slice("#/definitions/".length); + const realName = placeholderToReal.get(placeholder); + if (!realName) { + allExternal = false; + break; + } + realNames.push(realName); + } + + if (allExternal && realNames.length > 0) { + aliases.set(definitionName, realNames); + } + } + return aliases; +} + +function postProcessExternalUnionAliasesForPython(code: string, aliases: Map): string { + for (const [aliasName, realNames] of aliases) { + const aliasLine = `${aliasName} = ${realNames.join(" | ")}`; + const classPattern = new RegExp( + `(?:^|\\n)@dataclass\\r?\\nclass ${aliasName}\\b[\\s\\S]*?(?=\\n@dataclass\\b|\\nclass\\s+\\w|\\ndef\\s+\\w|$)`, + "g" + ); + if (classPattern.test(code)) { + code = code.replace(classPattern, `\n${aliasLine}\n`); + } else if (!new RegExp(`^${aliasName}\\s*=`, "m").test(code)) { + code = `${aliasLine}\n\n${code}`; + } + + code = code.replace( + new RegExp(`${aliasName}\\.from_dict`, "g"), + `(lambda x: from_union([${realNames.map((name) => `${name}.from_dict`).join(", ")}], x))` + ); + code = code.replace( + new RegExp(`to_class\\(${aliasName},\\s*([^)]+)\\)`, "g"), + `from_union([${realNames.map((name) => `lambda x: to_class(${name}, x)`).join(", ")}], $1)` + ); + } + + return code.replace(/\n{3,}/g, "\n\n"); +} + function pushPyExperimentalComment(lines: string[], subject: PyExperimentalSubject, indent = ""): void { lines.push(pyExperimentalComment(subject, indent)); } @@ -509,7 +669,9 @@ function pythonParamsTypeName(method: RpcMethod): string { if (method.rpcMethod.startsWith("session.") && method.params?.$ref) { return fallback; } - return getRpcSchemaTypeName(getMethodParamsSchema(method), fallback); + const schema = getMethodParamsSchema(method); + if (schema?.$ref) return toPascalCase(refTypeName(schema.$ref, rpcDefinitions)); + return getRpcSchemaTypeName(schema, fallback); } // ── Session Events ────────────────────────────────────────────────────────── @@ -539,6 +701,8 @@ interface PyResolvedType { interface PyCodegenCtx { classes: string[]; + aliases: string[]; + aliasesByName: Set; enums: string[]; enumsByName: Map; generatedNames: Set; @@ -762,6 +926,108 @@ function findPyDiscriminator( return null; } +function isPyNullLikeSchema(schema: JSONSchema7): boolean { + return schema.type === "null" || + (typeof schema.not === "object" && schema.not !== null && Object.keys(schema.not).length === 0); +} + +function getPyNamedSchemaType( + schema: JSONSchema7, + ctx: PyCodegenCtx +): { typeName: string; resolved: PyResolvedType } | undefined { + const resolved = resolveSchema(schema, ctx.definitions) ?? schema; + const typeName = schema.$ref + ? toPascalCase(refTypeName(schema.$ref, ctx.definitions)) + : typeof resolved.title === "string" + ? resolved.title + : undefined; + + if (!typeName) { + return undefined; + } + + if (resolved.enum && Array.isArray(resolved.enum) && resolved.enum.every((value) => typeof value === "string")) { + const enumType = getOrCreatePyEnum( + typeName, + resolved.enum as string[], + ctx, + resolved.description, + isSchemaDeprecated(resolved), + isSchemaExperimental(resolved) + ); + return { + typeName: enumType, + resolved: { + annotation: enumType, + fromExpr: (expr) => `parse_enum(${enumType}, ${expr})`, + toExpr: (expr) => `to_enum(${enumType}, ${expr})`, + }, + }; + } + + const resolvedObject = resolveObjectSchema(schema, ctx.definitions) ?? resolveObjectSchema(resolved, ctx.definitions); + if (isNamedPyObjectSchema(resolvedObject)) { + emitPyClass(typeName, resolvedObject, ctx, resolvedObject.description); + return { + typeName, + resolved: { + annotation: typeName, + fromExpr: (expr) => `${typeName}.from_dict(${expr})`, + toExpr: (expr) => `to_class(${typeName}, ${expr})`, + }, + }; + } + + return undefined; +} + +function getOrCreatePyUnionAlias( + aliasName: string, + members: string[], + ctx: PyCodegenCtx, + description?: string +): string { + if (!ctx.aliasesByName.has(aliasName)) { + const lines: string[] = []; + if (description) { + lines.push(`# ${description}`); + } + lines.push(`${aliasName} = ${members.join(" | ")}`); + ctx.aliasesByName.add(aliasName); + ctx.aliases.push(lines.join("\n")); + } + return aliasName; +} + +function resolvePyNamedUnion( + typeName: string, + schemas: JSONSchema7[], + ctx: PyCodegenCtx, + description?: string +): PyResolvedType | undefined { + const members = schemas + .filter((schema) => !isPyNullLikeSchema(schema)) + .map((schema) => getPyNamedSchemaType(schema, ctx)); + + if (members.length === 0 || members.some((member) => member === undefined)) { + return undefined; + } + + const namedMembers = members as Array<{ typeName: string; resolved: PyResolvedType }>; + const aliasName = getOrCreatePyUnionAlias( + typeName, + namedMembers.map((member) => member.typeName), + ctx, + description + ); + + return { + annotation: aliasName, + fromExpr: (expr) => `from_union([${namedMembers.map((member) => member.resolved.fromExpr).map((fromExpr) => fromExpr("x")).map((expr) => `lambda x: ${expr}`).join(", ")}], ${expr})`, + toExpr: (expr) => `from_union([${namedMembers.map((member) => member.resolved.toExpr).map((toExpr) => toExpr("x")).map((expr) => `lambda x: ${expr}`).join(", ")}], ${expr})`, + }; +} + function getOrCreatePyEnum( enumName: string, values: string[], @@ -846,15 +1112,17 @@ function resolvePyPropertyType( } if (propSchema.anyOf) { - const variants = (propSchema.anyOf as JSONSchema7[]) + const variantSchemas = (propSchema.anyOf as JSONSchema7[]) .filter((item) => typeof item === "object") + .map((item) => item as JSONSchema7); + const variants = variantSchemas .map( (item) => - resolveObjectSchema(item as JSONSchema7, ctx.definitions) ?? - resolveSchema(item as JSONSchema7, ctx.definitions) ?? - (item as JSONSchema7) + resolveObjectSchema(item, ctx.definitions) ?? + resolveSchema(item, ctx.definitions) ?? + item ); - const nonNull = variants.filter((item) => item.type !== "null"); + const nonNull = variants.filter((item) => !isPyNullLikeSchema(item)); const hasNull = variants.length !== nonNull.length; if (nonNull.length === 1) { @@ -881,6 +1149,16 @@ function resolvePyPropertyType( return hasNull || !isRequired ? pyOptionalResolvedType(resolved) : resolved; } + const namedUnion = resolvePyNamedUnion( + nestedName, + variantSchemas.filter((schema) => !isPyNullLikeSchema(resolveSchema(schema, ctx.definitions) ?? schema)), + ctx, + propSchema.description + ); + if (namedUnion) { + return hasNull || !isRequired ? pyOptionalResolvedType(namedUnion) : namedUnion; + } + return pyAnyResolvedType(); } } @@ -1333,6 +1611,8 @@ export function generatePythonSessionEventsCode(schema: JSONSchema7): string { const variants = extractPyEventVariants(schema); const ctx: PyCodegenCtx = { classes: [], + aliases: [], + aliasesByName: new Set(), enums: [], enumsByName: new Map(), generatedNames: new Set(), @@ -1579,6 +1859,11 @@ export function generatePythonSessionEventsCode(schema: JSONSchema7): string { out.push(``); out.push(``); } + for (const aliasDef of ctx.aliases.sort()) { + out.push(aliasDef); + out.push(``); + out.push(``); + } for (const enumDef of ctx.enums.sort()) { out.push(enumDef); out.push(``); @@ -1680,12 +1965,26 @@ async function generateSessionEvents(schemaPath?: string): Promise { // ── RPC Types ─────────────────────────────────────────────────────────────── -async function generateRpc(schemaPath?: string): Promise { +async function generateRpc(schemaPath?: string, sessionEventsSchema?: JSONSchema7): Promise { console.log("Python: generating RPC types..."); const { FetchingJSONSchemaStore, InputData, JSONSchemaInput, quicktype } = await import("quicktype-core"); const resolvedPath = schemaPath ?? (await getApiSchemaPath()); - const schema = fixNullableRequiredRefsInApiSchema(cloneSchemaForCodegen(JSON.parse(await fs.readFile(resolvedPath, "utf-8")) as ApiSchema)); + let schema = fixNullableRequiredRefsInApiSchema(cloneSchemaForCodegen(JSON.parse(await fs.readFile(resolvedPath, "utf-8")) as ApiSchema)); + if (sessionEventsSchema) { + const sharedDefinitions = findSharedSchemaDefinitions( + schema as unknown as Record, + sessionEventsSchema as unknown as Record + ); + const reachableDefinitions = collectReachableDefinitionNames(sessionEventsSchema as unknown as Record); + const exportedSessionEventTypes = collectPythonSessionEventExportedTypeNames(sessionEventsSchema); + for (const name of [...sharedDefinitions]) { + if (!reachableDefinitions.has(name) || !exportedSessionEventTypes.has(name)) { + sharedDefinitions.delete(name); + } + } + schema = rewriteSharedDefinitionReferences(schema, sharedDefinitions, "session-events.schema.json"); + } const allMethods = [ ...collectRpcMethods(schema.server || {}), @@ -1757,6 +2056,11 @@ async function generateRpc(schemaPath?: string): Promise { ), required: Object.keys(allDefinitions), }; + const externalRefs = rewriteExternalRefsForPython(singleSchema as JSONSchema7 & { definitions?: Record }); + const externalUnionAliases = collectExternalUnionAliasesForPython( + singleSchema.definitions as Record, + externalRefs.placeholderNames + ); await schemaInput.addSource({ name: "RPC", schema: JSON.stringify(singleSchema) }); const inputData = new InputData(); @@ -1779,21 +2083,21 @@ async function generateRpc(schemaPath?: string): Promise { typesCode = modernizePython(typesCode); const knownDefNames = new Set(Object.keys(allDefinitions).map((n) => n.toLowerCase())); typesCode = collapsePlaceholderPythonDataclasses(typesCode, knownDefNames); + typesCode = postProcessExternalUnionAliasesForPython(typesCode, externalUnionAliases); + typesCode = postProcessExternalRefsForPython(typesCode, externalRefs.placeholderNames); // Fix quicktype's Enum-suffix renaming: quicktype sometimes renames "Xyz" to // "XyzEnum" to avoid internal collisions. Strip the suffix to match our schema - // definition names, but fail the build if that introduces a duplicate definition. + // definition names when that is unambiguous. If the schema already led + // quicktype to emit both names, keep quicktype's disambiguated suffix. for (const defName of Object.keys(allDefinitions)) { const enumSuffixed = defName + "Enum"; + if (Object.prototype.hasOwnProperty.call(allDefinitions, enumSuffixed)) continue; if (!new RegExp(`\\bclass ${enumSuffixed}\\b`).test(typesCode)) continue; const renamed = typesCode.replace(new RegExp(`\\b${enumSuffixed}\\b`, "g"), defName); const classCount = (renamed.match(new RegExp(`^class ${defName}\\b`, "gm")) ?? []).length; if (classCount > 1) { - throw new Error( - `Python codegen: stripping quicktype's "Enum" suffix from "${enumSuffixed}" ` + - `would produce a duplicate definition for "${defName}". ` + - `Fix the schema definition name or add .withTypeName() to disambiguate.` - ); + continue; } typesCode = renamed; } @@ -1911,6 +2215,10 @@ Generated from: api.schema.json from typing import TYPE_CHECKING +${[...externalRefs.imports.entries()] + .map(([module, names]) => `from ${module} import ${[...names].sort().join(", ")}`) + .join("\n")} + if TYPE_CHECKING: from .._jsonrpc import JsonRpcClient @@ -2001,6 +2309,24 @@ def _patch_model_capabilities(data: dict) -> dict: console.log(` ✓ ${outPath}`); } +function collectPythonSessionEventExportedTypeNames(schema: JSONSchema7): Set { + const definitions = collectDefinitionCollections(schema as Record); + const definitionNames = new Set([...Object.keys(definitions.definitions), ...Object.keys(definitions.$defs)]); + const code = generatePythonSessionEventsCode(schema); + const exported = new Set(); + const symbolPattern = /^(?:class\s+([A-Za-z_]\w*)\b|([A-Za-z_]\w*)\s*=)/gm; + let match: RegExpExecArray | null; + + while ((match = symbolPattern.exec(code)) !== null) { + const name = match[1] ?? match[2]; + if (definitionNames.has(name)) { + exported.add(name); + } + } + + return exported; +} + function emitPyApiGroup( lines: string[], apiName: string, @@ -2337,7 +2663,9 @@ function emitClientSessionRegistrationMethod( async function generate(sessionSchemaPath?: string, apiSchemaPath?: string): Promise { await generateSessionEvents(sessionSchemaPath); try { - await generateRpc(apiSchemaPath); + const resolvedSessionPath = sessionSchemaPath ?? (await getSessionEventsSchemaPath()); + const sessionSchema = postProcessSchema(cloneSchemaForCodegen(JSON.parse(await fs.readFile(resolvedSessionPath, "utf-8")) as JSONSchema7)); + await generateRpc(apiSchemaPath, sessionSchema); } catch (err) { if ((err as NodeJS.ErrnoException).code === "ENOENT" && !apiSchemaPath) { console.log("Python: skipping RPC (api.schema.json not found)"); diff --git a/scripts/codegen/rust.ts b/scripts/codegen/rust.ts index 92d178545..e95ab7b64 100644 --- a/scripts/codegen/rust.ts +++ b/scripts/codegen/rust.ts @@ -23,6 +23,8 @@ import { type RpcMethod, collectDefinitionCollections, collectDefinitions, + collectReachableDefinitionNames, + findSharedSchemaDefinitions, getApiSchemaPath, getNullableInner, getRpcSchemaTypeName, @@ -32,11 +34,13 @@ import { isSchemaDeprecated, isSchemaExperimental, isVoidSchema, + parseExternalSchemaRef, postProcessSchema, refTypeName, resolveObjectSchema, resolveRef, resolveSchema, + rewriteSharedDefinitionReferences, stripBooleanLiterals, } from "./utils.js"; @@ -44,6 +48,16 @@ const execFileAsync = promisify(execFile); const GENERATED_DIR = path.join(REPO_ROOT, "rust/src/generated"); +const EXTERNAL_SCHEMA_RUST_MODULE: Record = { + "session-events.schema.json": "super::session_events", +}; + +const EXTERNAL_SCHEMA_RUST_TYPE_MODULE: Record> = { + "session-events.schema.json": { + SessionEvent: "crate::types", + }, +}; + /** * JSON property names that should be emitted as a hand-authored newtype rather * than `String`. The newtype is `#[serde(transparent)]`, so the wire format is @@ -59,11 +73,13 @@ const STRING_NEWTYPE_OVERRIDES: Record = { // ── Naming helpers ────────────────────────────────────────────────────────── function toPascalCase(s: string): string { - return s + const name = s .split(/[^A-Za-z0-9]+/) .filter(Boolean) .map((w) => w.charAt(0).toUpperCase() + w.slice(1)) .join(""); + if (!name) return "Value"; + return /^[0-9]/.test(name) ? `Value${name}` : name; } function toRustPascalIdentifier(value: string, fallback: string): string { @@ -181,6 +197,14 @@ interface RustCodegenCtx { nonDefaultableTypes: Set; /** Schema definitions for $ref resolution. */ definitions?: DefinitionCollections; + /** When set, only these const-valued properties are accepted as union discriminators. */ + unionDiscriminatorProperties?: Set; + /** Whether unions without a const-valued discriminator should be emitted. */ + allowUntaggedUnions: boolean; + /** Specific union type names allowed even when their discriminator is not generally allowed. */ + allowedUnionTypeNames: Set; + /** External schema references that are actually emitted in generated types. */ + externalTypeRefs: Map>; } function stripOption(typeName: string): string { @@ -195,7 +219,52 @@ function getUnionVariants(schema: JSONSchema7): JSONSchema7[] | null { return null; } -function tryEmitRustDiscriminatedUnion( +interface RustUnionVariant { + schema: JSONSchema7; + typeName: string; +} + +function findRustDiscriminator(variants: RustUnionVariant[]): string | null { + const first = variants[0]?.schema; + if (!isObjectSchema(first) || !first.properties) return null; + + for (const [propName, propSchema] of Object.entries(first.properties).sort( + ([a], [b]) => a.localeCompare(b), + )) { + if (typeof propSchema !== "object") continue; + if ((propSchema as JSONSchema7).const === undefined) continue; + + const values = new Set(); + let isValid = true; + for (const { schema } of variants) { + if (!isObjectSchema(schema) || !schema.properties) { + isValid = false; + break; + } + const candidate = schema.properties[propName]; + if (typeof candidate !== "object") { + isValid = false; + break; + } + const value = (candidate as JSONSchema7).const; + if (value === undefined) { + isValid = false; + break; + } + const key = String(value); + if (values.has(key)) { + isValid = false; + break; + } + values.add(key); + } + if (isValid) return propName; + } + + return null; +} + +function tryEmitRustUnion( schema: JSONSchema7, parentTypeName: string, jsonPropName: string, @@ -211,42 +280,52 @@ function tryEmitRustDiscriminatedUnion( (typeof schema.title === "string" && schema.title) || parentTypeName + toPascalCase(jsonPropName); - const resolvedVariants = nonNull.map((variant) => { + const resolvedVariants: RustUnionVariant[] = []; + for (let i = 0; i < nonNull.length; i++) { + const variant = nonNull[i]; if (variant.$ref && typeof variant.$ref === "string") { const resolved = resolveRef(variant.$ref, ctx.definitions); - return { + if (resolved && !isObjectSchema(resolved)) return null; + resolvedVariants.push({ schema: (resolved ?? variant) as JSONSchema7, - typeName: toPascalCase(refTypeName(variant.$ref, ctx.definitions)), - }; + typeName: rustRefTypeName(variant.$ref, ctx.definitions), + }); + continue; } const resolved = resolveObjectSchema(variant, ctx.definitions) ?? resolveSchema(variant, ctx.definitions) ?? variant; - const kindConst = (resolved.properties?.kind as JSONSchema7 | undefined) - ?.const; + if (!isObjectSchema(resolved)) return null; + const discriminatorValue = Object.values(resolved.properties ?? {}).find( + (prop) => typeof prop === "object" && (prop as JSONSchema7).const !== undefined, + ) as JSONSchema7 | undefined; const typeName = (typeof resolved.title === "string" && resolved.title) || - (typeof kindConst === "string" - ? `${enumName}${toPascalCase(kindConst)}` - : `${enumName}Variant`); + (discriminatorValue?.const !== undefined + ? `${enumName}${toPascalCase(String(discriminatorValue.const))}` + : `${enumName}Variant${i + 1}`); - return { + resolvedVariants.push({ schema: resolved as JSONSchema7, typeName, - }; - }); - - const isDiscriminated = resolvedVariants.every( - ({ schema: variantSchema }) => { - if (!isObjectSchema(variantSchema) || !variantSchema.properties) - return false; - const kind = variantSchema.properties.kind as JSONSchema7 | undefined; - return typeof kind?.const === "string"; - }, - ); - if (!isDiscriminated) return null; + }); + } + + const discriminator = findRustDiscriminator(resolvedVariants); + const isAllowedUnionType = ctx.allowedUnionTypeNames.has(enumName); + if (discriminator) { + if ( + ctx.unionDiscriminatorProperties && + !ctx.unionDiscriminatorProperties.has(discriminator) && + !isAllowedUnionType + ) { + return null; + } + } else if (!ctx.allowUntaggedUnions || !isAllowedUnionType) { + return null; + } if (ctx.generatedNames.has(enumName)) { return enumName; @@ -276,10 +355,13 @@ function tryEmitRustDiscriminatedUnion( const usedVariantNames = new Set(); for (const { schema: variantSchema, typeName } of resolvedVariants) { - const kind = ((variantSchema.properties?.kind as JSONSchema7 | undefined) - ?.const ?? typeName) as string; + const discriminatorValue = + discriminator && isObjectSchema(variantSchema) + ? (variantSchema.properties?.[discriminator] as JSONSchema7 | undefined) + ?.const + : undefined; const variantName = uniqueRustPascalIdentifier( - kind, + discriminatorValue === undefined ? typeName : String(discriminatorValue), usedVariantNames, "Variant", ); @@ -291,13 +373,39 @@ function tryEmitRustDiscriminatedUnion( return enumName; } -function makeCtx(definitions?: DefinitionCollections): RustCodegenCtx { +function recordExternalRustTypeRef(ref: string, ctx: RustCodegenCtx): void { + const externalRef = parseExternalSchemaRef(ref); + if (!externalRef) return; + + let typeNames = ctx.externalTypeRefs.get(externalRef.schemaFile); + if (!typeNames) { + typeNames = new Set(); + ctx.externalTypeRefs.set(externalRef.schemaFile, typeNames); + } + typeNames.add(externalRef.definitionName); +} + +function makeCtx( + definitions?: DefinitionCollections, + options: { + unionDiscriminatorProperties?: Set | null; + allowUntaggedUnions?: boolean; + allowedUnionTypeNames?: Iterable; + } = {}, +): RustCodegenCtx { return { structs: [], enums: [], generatedNames: new Set(), nonDefaultableTypes: new Set(), definitions, + unionDiscriminatorProperties: + options.unionDiscriminatorProperties === null + ? undefined + : (options.unionDiscriminatorProperties ?? new Set(["kind"])), + allowUntaggedUnions: options.allowUntaggedUnions ?? false, + allowedUnionTypeNames: new Set(options.allowedUnionTypeNames ?? []), + externalTypeRefs: new Map(), }; } @@ -322,6 +430,11 @@ function pushRustExperimentalDocs( // ── Type resolution ───────────────────────────────────────────────────────── +function rustRefTypeName(ref: string, definitions?: DefinitionCollections): string { + const externalRef = parseExternalSchemaRef(ref); + return toPascalCase(externalRef?.definitionName ?? refTypeName(ref, definitions)); +} + /** * Map a JSON Schema to a Rust type string. Emits nested type definitions as * side effects into ctx. @@ -337,9 +450,8 @@ function resolveRustType( // $ref — resolve and recurse if (propSchema.$ref && typeof propSchema.$ref === "string") { - const typeName = toPascalCase( - refTypeName(propSchema.$ref, ctx.definitions), - ); + recordExternalRustTypeRef(propSchema.$ref, ctx); + const typeName = rustRefTypeName(propSchema.$ref, ctx.definitions); const resolved = resolveRef(propSchema.$ref, ctx.definitions); if (resolved) { if (resolved.enum) { @@ -369,14 +481,14 @@ function resolveRustType( // anyOf — nullable pattern or union if (propSchema.anyOf) { - const discriminatedUnion = tryEmitRustDiscriminatedUnion( + const unionType = tryEmitRustUnion( propSchema, parentTypeName, jsonPropName, ctx, ); - if (discriminatedUnion) { - return wrapOption(discriminatedUnion, isRequired); + if (unionType) { + return wrapOption(unionType, isRequired); } const nonNull = (propSchema.anyOf as JSONSchema7[]).filter( @@ -406,14 +518,14 @@ function resolveRustType( // oneOf — treat like anyOf for now if (propSchema.oneOf) { - const discriminatedUnion = tryEmitRustDiscriminatedUnion( + const unionType = tryEmitRustUnion( propSchema, parentTypeName, jsonPropName, ctx, ); - if (discriminatedUnion) { - return wrapOption(discriminatedUnion, isRequired); + if (unionType) { + return wrapOption(unionType, isRequired); } const nonNull = (propSchema.oneOf as JSONSchema7[]).filter( @@ -819,6 +931,13 @@ function generateSessionEventsCode(schema: JSONSchema7): string { const variants = extractEventVariants(schema); const ctx = makeCtx( collectDefinitionCollections(schema as Record), + { + allowUntaggedUnions: true, + allowedUnionTypeNames: [ + "ToolExecutionCompleteContent", + "ToolExecutionCompleteContentResourceDetails", + ], + }, ); // Generate per-event data structs @@ -981,14 +1100,22 @@ function collectRpcMethods( return methods; } -function rustParamsTypeName(method: RpcMethod): string { +function rustParamsTypeName(method: RpcMethod, ctx: RustCodegenCtx): string { + if (method.params?.$ref && parseExternalSchemaRef(method.params.$ref)) { + recordExternalRustTypeRef(method.params.$ref, ctx); + return rustRefTypeName(method.params.$ref); + } return getRpcSchemaTypeName( method.params, `${toPascalCase(method.rpcMethod)}Params`, ); } -function rustResultTypeName(method: RpcMethod): string { +function rustResultTypeName(method: RpcMethod, ctx: RustCodegenCtx): string { + if (method.result?.$ref && parseExternalSchemaRef(method.result.$ref)) { + recordExternalRustTypeRef(method.result.$ref, ctx); + return rustRefTypeName(method.result.$ref); + } return getRpcSchemaTypeName( method.result, `${toPascalCase(method.rpcMethod)}Result`, @@ -1016,7 +1143,7 @@ function generateApiTypesCode(apiSchema: ApiSchema): string { isSchemaExperimental(schema), ); } else if (getUnionVariants(schema)) { - tryEmitRustDiscriminatedUnion(schema, name, "", ctx); + tryEmitRustUnion(schema, name, "", ctx); } else if (isObjectSchema(schema)) { emitRustStruct(name, schema, ctx, schema.description); } @@ -1055,11 +1182,11 @@ function generateApiTypesCode(apiSchema: ApiSchema): string { isObjectSchema(method.params) && !isVoidSchema(method.params) ) { - const paramsName = rustParamsTypeName(method); + const paramsName = rustParamsTypeName(method, ctx); emitRustStruct(paramsName, method.params, ctx, method.params.description); } if (method.result && !isVoidSchema(method.result)) { - const resultName = rustResultTypeName(method); + const resultName = rustResultTypeName(method, ctx); const resolved = resolveSchema(method.result, defCollections); if (resolved) { if (resolved.enum && Array.isArray(resolved.enum)) { @@ -1081,6 +1208,29 @@ function generateApiTypesCode(apiSchema: ApiSchema): string { out.push(""); out.push("use serde::{Deserialize, Serialize};"); out.push(""); + const externalImports = new Map>(); + for (const [schemaFile, typeNames] of ctx.externalTypeRefs) { + const defaultModule = EXTERNAL_SCHEMA_RUST_MODULE[schemaFile]; + const typeModules = EXTERNAL_SCHEMA_RUST_TYPE_MODULE[schemaFile] ?? {}; + for (const typeName of typeNames) { + const module = typeModules[typeName] ?? defaultModule; + if (!module) continue; + let names = externalImports.get(module); + if (!names) { + names = new Set(); + externalImports.set(module, names); + } + names.add(typeName); + } + } + for (const [module, typeNames] of [...externalImports].sort(([left], [right]) => + left.localeCompare(right), + )) { + out.push(`use ${module}::{${[...typeNames].sort().join(", ")}};`); + } + if (externalImports.size > 0) { + out.push(""); + } out.push("use crate::types::{RequestId, SessionId};"); out.push(""); @@ -1554,20 +1704,38 @@ async function generate(): Promise { // Generate session events console.log("Generating session_events.rs..."); const sessionEventsCode = generateSessionEventsCode(sessionEventsSchema); + const sharedDefinitions = findSharedSchemaDefinitions( + apiSchema as unknown as Record, + sessionEventsSchema as unknown as Record, + ); + const reachableDefinitions = collectReachableDefinitionNames( + sessionEventsSchema as unknown as Record, + ); + for (const name of [...sharedDefinitions]) { + const declarationPattern = new RegExp(`\\bpub\\s+(?:struct|enum)\\s+${name}\\b`); + if (!reachableDefinitions.has(name) || !declarationPattern.test(sessionEventsCode)) { + sharedDefinitions.delete(name); + } + } + const apiSchemaForGeneration = rewriteSharedDefinitionReferences( + apiSchema, + sharedDefinitions, + "session-events.schema.json", + ); const sessionEventsPath = path.join(GENERATED_DIR, "session_events.rs"); await fs.writeFile(sessionEventsPath, sessionEventsCode, "utf-8"); await rustfmt(sessionEventsPath); // Generate API types console.log("Generating api_types.rs..."); - const apiTypesCode = generateApiTypesCode(apiSchema); + const apiTypesCode = generateApiTypesCode(apiSchemaForGeneration); const apiTypesPath = path.join(GENERATED_DIR, "api_types.rs"); await fs.writeFile(apiTypesPath, apiTypesCode, "utf-8"); await rustfmt(apiTypesPath); // Generate typed RPC namespace console.log("Generating rpc.rs..."); - const rpcCode = generateRpcCode(apiSchema); + const rpcCode = generateRpcCode(apiSchemaForGeneration); const rpcPath = path.join(GENERATED_DIR, "rpc.rs"); await fs.writeFile(rpcPath, rpcCode, "utf-8"); await rustfmt(rpcPath); diff --git a/scripts/codegen/typescript.ts b/scripts/codegen/typescript.ts index 0e6922e13..cd0a0b07a 100644 --- a/scripts/codegen/typescript.ts +++ b/scripts/codegen/typescript.ts @@ -17,10 +17,15 @@ import { getSessionEventsSchemaPath, postProcessSchema, writeGeneratedFile, + collectExternalSchemaRefNames, collectDefinitionCollections, + collectReachableDefinitionNames, + findSharedSchemaDefinitions, hasSchemaPayload, + parseExternalSchemaRef, resolveObjectSchema, resolveSchema, + rewriteSharedDefinitionReferences, withSharedDefinitions, isRpcMethod, isNodeFullyExperimental, @@ -33,6 +38,9 @@ import { } from "./utils.js"; const TS_EXPERIMENTAL_JSDOC = "/** @experimental */"; +const EXTERNAL_SCHEMA_TS_IMPORT: Record = { + "session-events.schema.json": "./session-events.js", +}; function tsExperimentalJSDoc(indent = ""): string { return `${indent}${TS_EXPERIMENTAL_JSDOC}`; @@ -202,16 +210,24 @@ function normalizeSchemaForTypeScript(schema: JSONSchema7): JSONSchema7 { ) as Record; if (typeof rewritten.$ref === "string") { - if (rewritten.$ref.startsWith("#/$defs/")) { + const externalRef = parseExternalSchemaRef(rewritten.$ref); + if (externalRef && EXTERNAL_SCHEMA_TS_IMPORT[externalRef.schemaFile]) { + rewritten.tsType = externalRef.definitionName; + for (const key of Object.keys(rewritten)) { + if (key !== "tsType") { + delete rewritten[key]; + } + } + } else if (rewritten.$ref.startsWith("#/$defs/")) { const definitionName = rewritten.$ref.slice("#/$defs/".length); rewritten.$ref = `#/definitions/${draftDefinitionAliases.get(definitionName) ?? definitionName}`; } // json-schema-to-typescript treats sibling keywords alongside $ref as a // new inline type instead of reusing the referenced definition. Strip // siblings so that $ref-only objects compile to a single shared type. - for (const key of Object.keys(rewritten)) { - if (key !== "$ref") { - delete rewritten[key]; + if ("$ref" in rewritten) { + for (const key of Object.keys(rewritten)) { + if (key !== "$ref") delete rewritten[key]; } } } @@ -306,10 +322,9 @@ function isParamsOptional(method: RpcMethod): boolean { } function resultTypeName(method: RpcMethod): string { - return getRpcSchemaTypeName( - getMethodResultSchema(method), - method.rpcMethod.split(".").map(toPascalCase).join("") + "Result" - ); + const schema = getMethodResultSchema(method); + const externalRef = schema?.$ref ? parseExternalSchemaRef(schema.$ref) : undefined; + return externalRef?.definitionName ?? getRpcSchemaTypeName(schema, method.rpcMethod.split(".").map(toPascalCase).join("") + "Result"); } function tsNullableResultTypeName(method: RpcMethod): string | undefined { @@ -336,14 +351,29 @@ function paramsTypeName(method: RpcMethod): string { if (method.rpcMethod.startsWith("session.") && method.params?.$ref) { return fallback; } - return getRpcSchemaTypeName(getMethodParamsSchema(method), fallback); + const schema = getMethodParamsSchema(method); + const externalRef = schema?.$ref ? parseExternalSchemaRef(schema.$ref) : undefined; + return externalRef?.definitionName ?? getRpcSchemaTypeName(schema, fallback); } -async function generateRpc(schemaPath?: string): Promise { +async function generateRpc(schemaPath?: string, sessionEventsSchema?: JSONSchema7): Promise { console.log("TypeScript: generating RPC types..."); const resolvedPath = schemaPath ?? (await getApiSchemaPath()); - const schema = fixNullableRequiredRefsInApiSchema(JSON.parse(await fs.readFile(resolvedPath, "utf-8")) as ApiSchema); + let schema = fixNullableRequiredRefsInApiSchema(JSON.parse(await fs.readFile(resolvedPath, "utf-8")) as ApiSchema); + if (sessionEventsSchema) { + const sharedDefinitions = findSharedSchemaDefinitions( + schema as unknown as Record, + sessionEventsSchema as unknown as Record + ); + const reachableDefinitions = collectReachableDefinitionNames(sessionEventsSchema as unknown as Record); + for (const name of [...sharedDefinitions]) { + if (!reachableDefinitions.has(name)) { + sharedDefinitions.delete(name); + } + } + schema = rewriteSharedDefinitionReferences(schema, sharedDefinitions, "session-events.schema.json"); + } const lines: string[] = []; lines.push(`/** @@ -354,6 +384,17 @@ async function generateRpc(schemaPath?: string): Promise { import type { MessageConnection } from "vscode-jsonrpc/node.js"; `); + const externalSchemaRefs = collectExternalSchemaRefNames(schema); + for (const [schemaFile, typeNames] of externalSchemaRefs) { + const importPath = EXTERNAL_SCHEMA_TS_IMPORT[schemaFile]; + if (importPath) { + lines.push(`import type { ${[...typeNames].sort().join(", ")} } from "${importPath}";`); + } + } + if (externalSchemaRefs.size > 0) { + lines.push(""); + } + const allMethods = [...collectRpcMethods(schema.server || {}), ...collectRpcMethods(schema.session || {})]; const clientSessionMethods = collectRpcMethods(schema.clientSession || {}); const seenBlocks = new Map(); @@ -748,7 +789,9 @@ function emitClientSessionApiRegistration(clientSchema: Record) async function generate(sessionSchemaPath?: string, apiSchemaPath?: string): Promise { await generateSessionEvents(sessionSchemaPath); try { - await generateRpc(apiSchemaPath); + const resolvedSessionPath = sessionSchemaPath ?? (await getSessionEventsSchemaPath()); + const sessionSchema = postProcessSchema(JSON.parse(await fs.readFile(resolvedSessionPath, "utf-8")) as JSONSchema7); + await generateRpc(apiSchemaPath, sessionSchema); } catch (err) { if ((err as NodeJS.ErrnoException).code === "ENOENT" && !apiSchemaPath) { console.log("TypeScript: skipping RPC (api.schema.json not found)"); diff --git a/scripts/codegen/utils.ts b/scripts/codegen/utils.ts index 84c2d4a03..f38f005fb 100644 --- a/scripts/codegen/utils.ts +++ b/scripts/codegen/utils.ts @@ -325,6 +325,19 @@ export function cloneSchemaForCodegen(value: T): T { return value; } +export function stableStringify(value: unknown): string { + if (Array.isArray(value)) { + return `[${value.map((item) => stableStringify(item)).join(",")}]`; + } + + if (value && typeof value === "object") { + const entries = Object.entries(value as Record).sort(([a], [b]) => a.localeCompare(b)); + return `{${entries.map(([key, entryValue]) => `${JSON.stringify(key)}:${stableStringify(entryValue)}`).join(",")}}`; + } + + return JSON.stringify(value) ?? "undefined"; +} + export interface ApiSchema { definitions?: Record; $defs?: Record; @@ -472,6 +485,42 @@ export function refTypeName(ref: string, definitions?: DefinitionCollections): s return baseName; } +export function parseExternalSchemaRef(ref: string): { schemaFile: string; definitionName: string } | undefined { + const match = ref.match(/^([^#]+)#\/(?:definitions|\$defs)\/(.+)$/); + return match ? { schemaFile: match[1], definitionName: match[2] } : undefined; +} + +export function collectExternalSchemaRefNames(schema: unknown): Map> { + const refs = new Map>(); + + const visit = (value: unknown): void => { + if (Array.isArray(value)) { + for (const item of value) visit(item); + return; + } + + if (!value || typeof value !== "object") return; + + const node = value as Record; + if (typeof node.$ref === "string") { + const externalRef = parseExternalSchemaRef(node.$ref); + if (externalRef) { + let bucket = refs.get(externalRef.schemaFile); + if (!bucket) { + bucket = new Set(); + refs.set(externalRef.schemaFile, bucket); + } + bucket.add(externalRef.definitionName); + } + } + + for (const child of Object.values(node)) visit(child); + }; + + visit(schema); + return refs; +} + /** Resolve a `$ref` path against a definitions map, returning the referenced schema. */ export function resolveRef( ref: string, @@ -687,6 +736,310 @@ export function collectDefinitions( return { ...$defs, ...definitions }; } +export function findSharedSchemaDefinitions( + sourceSchema: Record, + canonicalSchema: Record +): Set { + const sourceDefinitions = collectDefinitions(sourceSchema); + const canonicalDefinitions = collectDefinitions(canonicalSchema); + const shared = new Set(); + + for (const [name, sourceDefinition] of Object.entries(sourceDefinitions)) { + const canonicalDefinition = canonicalDefinitions[name]; + if ( + canonicalDefinition !== undefined && + stableStringify(normalizeDefinitionForComparison(sourceDefinition)) === + stableStringify(normalizeDefinitionForComparison(canonicalDefinition)) + ) { + shared.add(name); + } + } + + let changed = true; + while (changed) { + changed = false; + for (const name of [...shared]) { + const refs = new Set([ + ...collectLocalDefinitionRefNames(sourceDefinitions[name]), + ...collectLocalDefinitionRefNames(canonicalDefinitions[name]), + ]); + for (const refName of refs) { + if (refName !== name && !shared.has(refName)) { + shared.delete(name); + changed = true; + break; + } + } + } + } + + return shared; +} + +export function collectReachableDefinitionNames( + schema: Record, + rootDefinitionNames: Iterable = ["SessionEvent"] +): Set { + const definitions = collectDefinitions(schema); + const reachable = new Set(); + const visiting = new Set(); + + const visitDefinition = (name: string): void => { + if (reachable.has(name) || visiting.has(name)) return; + const definition = definitions[name]; + if (definition === undefined) return; + + visiting.add(name); + reachable.add(name); + visitSchema(definition); + visiting.delete(name); + }; + + const visitSchema = (value: unknown): void => { + if (!value || typeof value !== "object") return; + if (Array.isArray(value)) { + for (const item of value) visitSchema(item); + return; + } + + const record = value as Record; + if (typeof record.$ref === "string") { + const localRef = parseLocalDefinitionRef(record.$ref); + if (localRef) visitDefinition(localRef); + } + for (const child of Object.values(record)) visitSchema(child); + }; + + for (const rootName of rootDefinitionNames) { + visitDefinition(rootName); + } + + return reachable; +} + +export function rewriteSharedDefinitionReferences( + schema: T, + sharedDefinitionNames: Iterable, + externalSchemaFile: string, + preserveDefinitions = false +): T { + const sharedNames = new Set(sharedDefinitionNames); + if (sharedNames.size === 0) return cloneSchemaForCodegen(schema); + + const rewriteRef = (ref: string): string => { + const localRef = parseLocalDefinitionRef(ref); + return localRef && sharedNames.has(localRef) ? `${externalSchemaFile}#/definitions/${localRef}` : ref; + }; + + const rewrite = (value: unknown): unknown => { + if (Array.isArray(value)) { + return value.map((item) => rewrite(item)); + } + + if (!value || typeof value !== "object") { + return value; + } + + const source = value as Record; + const result: Record = {}; + for (const [childKey, childValue] of Object.entries(source)) { + if ((childKey === "definitions" || childKey === "$defs") && childValue && typeof childValue === "object" && !Array.isArray(childValue)) { + const definitions: Record = {}; + for (const [definitionName, definitionValue] of Object.entries(childValue as Record)) { + if (preserveDefinitions || !sharedNames.has(definitionName)) { + definitions[definitionName] = rewrite(definitionValue); + } + } + result[childKey] = definitions; + continue; + } + + result[childKey] = rewrite(childValue); + } + + if (typeof result.$ref === "string") { + result.$ref = rewriteRef(result.$ref); + } + + return result; + }; + + return rewrite(schema) as T; +} + +export function inlineExternalSchemaDefinitions( + schema: T, + externalSchema: Record, + externalSchemaFile: string, + options: { conflictingDefinitionNamePrefix?: string } = {} +): { schema: T; inlinedDefinitionNames: Set } { + const cloned = cloneSchemaForCodegen(schema) as Record; + const externalRefs = collectExternalSchemaRefNames(cloned).get(externalSchemaFile); + if (!externalRefs || externalRefs.size === 0) { + return { schema: cloned as T, inlinedDefinitionNames: new Set() }; + } + + const externalDefinitions = collectDefinitions(externalSchema); + const reachableDefinitions = collectReachableDefinitionNames(externalSchema, externalRefs); + const inlinedDefinitionNames = new Set(); + const targetDefinitions = { + ...((cloned.definitions ?? {}) as Record), + }; + const nameMap = new Map(); + const usedNames = new Set([...Object.keys(targetDefinitions), ...reachableDefinitions]); + + for (const name of [...reachableDefinitions].sort()) { + const definition = externalDefinitions[name]; + if (definition === undefined) continue; + + const existing = targetDefinitions[name]; + if ( + existing !== undefined && + stableStringify(normalizeDefinitionForComparison(existing)) !== + stableStringify(normalizeDefinitionForComparison(definition)) + ) { + if (!options.conflictingDefinitionNamePrefix) { + throw new Error( + `Cannot inline ${externalSchemaFile}#/definitions/${name}; api.schema.json already defines a different schema with that name.` + ); + } + + let renamed = `${options.conflictingDefinitionNamePrefix}${name}`; + let suffix = 2; + while (usedNames.has(renamed)) { + renamed = `${options.conflictingDefinitionNamePrefix}${name}${suffix++}`; + } + usedNames.add(renamed); + nameMap.set(name, renamed); + } else { + nameMap.set(name, name); + } + } + + const rewriteInlinedRefs = (value: unknown): unknown => { + if (Array.isArray(value)) { + return value.map((item) => rewriteInlinedRefs(item)); + } + + if (!value || typeof value !== "object") { + return value; + } + + const result: Record = {}; + for (const [key, child] of Object.entries(value as Record)) { + result[key] = rewriteInlinedRefs(child); + } + + if (typeof result.$ref === "string") { + const localRef = parseLocalDefinitionRef(result.$ref); + const externalRef = parseExternalSchemaRef(result.$ref); + const mappedName = + localRef ? nameMap.get(localRef) : + externalRef?.schemaFile === externalSchemaFile ? nameMap.get(externalRef.definitionName) : + undefined; + if (mappedName) { + result.$ref = `#/definitions/${mappedName}`; + } + } + + return result; + }; + + for (const name of [...reachableDefinitions].sort()) { + const definition = externalDefinitions[name]; + const targetName = nameMap.get(name); + if (definition === undefined || !targetName) continue; + + targetDefinitions[targetName] = rewriteInlinedRefs(cloneSchemaForCodegen(definition)) as JSONSchema7Definition; + inlinedDefinitionNames.add(targetName); + } + + cloned.definitions = targetDefinitions; + + const rewrite = (value: unknown): unknown => { + if (Array.isArray(value)) { + return value.map((item) => rewrite(item)); + } + + if (!value || typeof value !== "object") { + return value; + } + + const result: Record = {}; + for (const [key, child] of Object.entries(value as Record)) { + result[key] = rewrite(child); + } + + if (typeof result.$ref === "string") { + const externalRef = parseExternalSchemaRef(result.$ref); + const targetName = externalRef?.schemaFile === externalSchemaFile ? nameMap.get(externalRef.definitionName) : undefined; + if (targetName) { + result.$ref = `#/definitions/${targetName}`; + } + } + + return result; + }; + + return { schema: rewrite(cloned) as T, inlinedDefinitionNames }; +} + +function normalizeDefinitionForComparison(definition: JSONSchema7Definition): unknown { + if (Array.isArray(definition)) { + return definition.map((item) => + typeof item === "object" && item !== null ? normalizeDefinitionForComparison(item as JSONSchema7Definition) : item + ); + } + + if (!definition || typeof definition !== "object") { + return definition; + } + + const result: Record = {}; + for (const [key, value] of Object.entries(definition as Record)) { + if (key === "$ref" && typeof value === "string") { + const localRef = parseLocalDefinitionRef(value); + result[key] = localRef ? `#/definitions/${localRef}` : value; + } else if (Array.isArray(value)) { + result[key] = value.map((item) => + typeof item === "object" && item !== null ? normalizeDefinitionForComparison(item as JSONSchema7Definition) : item + ); + } else if (value && typeof value === "object") { + result[key] = normalizeDefinitionForComparison(value as JSONSchema7Definition); + } else { + result[key] = value; + } + } + return result; +} + +function collectLocalDefinitionRefNames(value: unknown): Set { + const refs = new Set(); + + const visit = (node: unknown): void => { + if (!node || typeof node !== "object") return; + if (Array.isArray(node)) { + for (const item of node) visit(item); + return; + } + + const record = node as Record; + if (typeof record.$ref === "string") { + const localRef = parseLocalDefinitionRef(record.$ref); + if (localRef) refs.add(localRef); + } + for (const child of Object.values(record)) visit(child); + }; + + visit(value); + return refs; +} + +function parseLocalDefinitionRef(ref: string): string | undefined { + const match = ref.match(/^#\/(?:definitions|\$defs)\/(.+)$/); + return match?.[1]; +} + export function withSharedDefinitions( schema: T, definitions: DefinitionCollections