diff --git a/.github/actions/setup-copilot/action.yml b/.github/actions/setup-copilot/action.yml index 4b5ef0ef8..3769bc375 100644 --- a/.github/actions/setup-copilot/action.yml +++ b/.github/actions/setup-copilot/action.yml @@ -22,7 +22,16 @@ runs: shell: bash - name: Set CLI path id: cli-path - run: echo "path=$(pwd)/nodejs/node_modules/@github/copilot/index.js" >> $GITHUB_OUTPUT + run: | + # As of CLI 1.0.64-1 the @github/copilot package is a thin loader; the + # runnable index.js ships in the installed platform package + # (e.g. @github/copilot-linux-x64). Exactly one is installed. + cli_path=$(ls "$(pwd)"/nodejs/node_modules/@github/copilot-*/index.js 2>/dev/null | head -n1) + if [ -z "$cli_path" ]; then + echo "Could not find @github/copilot platform package (index.js) under nodejs/node_modules" >&2 + exit 1 + fi + echo "path=$cli_path" >> $GITHUB_OUTPUT shell: bash - name: Verify CLI works run: node ${{ steps.cli-path.outputs.path }} --version diff --git a/.github/workflows/java-sdk-tests.yml b/.github/workflows/java-sdk-tests.yml index d9c699e16..e310fa8ba 100644 --- a/.github/workflows/java-sdk-tests.yml +++ b/.github/workflows/java-sdk-tests.yml @@ -72,7 +72,7 @@ jobs: run: mvn javadoc:javadoc -q - name: Verify CLI works - run: node ../nodejs/node_modules/@github/copilot/index.js --version + run: node ../nodejs/node_modules/@github/copilot/npm-loader.js --version - name: Run spotless check if: matrix.test-jdk == '25' diff --git a/docs/features/image-input.md b/docs/features/image-input.md index db13973fc..c63a80c11 100644 --- a/docs/features/image-input.md +++ b/docs/features/image-input.md @@ -345,7 +345,7 @@ func main() { Prompt: "Describe what you see in this image", Attachments: []copilot.Attachment{ &copilot.AttachmentBlob{ - Data: base64ImageData, + Data: &base64ImageData, MIMEType: mimeType, DisplayName: &displayName, }, @@ -362,7 +362,7 @@ session.Send(ctx, copilot.MessageOptions{ Prompt: "Describe what you see in this image", Attachments: []copilot.Attachment{ &copilot.AttachmentBlob{ - Data: base64ImageData, // base64-encoded string + Data: &base64ImageData, // base64-encoded string MIMEType: mimeType, DisplayName: &displayName, }, diff --git a/dotnet/src/Generated/Rpc.cs b/dotnet/src/Generated/Rpc.cs index d11b8efc7..7ec632a5e 100644 --- a/dotnet/src/Generated/Rpc.cs +++ b/dotnet/src/Generated/Rpc.cs @@ -69,11 +69,19 @@ internal sealed class ConnectRequest /// Long context tier pricing (available for models with extended context windows). public sealed class ModelBillingTokenPricesLongContext { - /// AI Credits cost per billing batch of cached tokens. + /// Deprecated: use cacheReadPrice. AI Credits cost per billing batch of cached tokens. [JsonPropertyName("cachePrice")] public double? CachePrice { get; set; } - /// Prompt token budget (max_prompt_tokens) for the long context tier. The total context window is this value plus the model's max_output_tokens. + /// AI Credits cost per billing batch of cached (read) tokens. + [JsonPropertyName("cacheReadPrice")] + public double? CacheReadPrice { get; set; } + + /// AI Credits cost per billing batch of cache-write (cache creation) tokens. + [JsonPropertyName("cacheWritePrice")] + public double? CacheWritePrice { get; set; } + + /// Deprecated: use maxPromptTokens. Prompt token budget for the long context tier. The total context window is this value plus the model's max_output_tokens. [JsonPropertyName("contextMax")] public long? ContextMax { get; set; } @@ -81,6 +89,10 @@ public sealed class ModelBillingTokenPricesLongContext [JsonPropertyName("inputPrice")] public double? InputPrice { get; set; } + /// Prompt token budget for the long context tier. The total context window is this value plus the model's max_output_tokens. + [JsonPropertyName("maxPromptTokens")] + public long? MaxPromptTokens { get; set; } + /// AI Credits cost per billing batch of output tokens. [JsonPropertyName("outputPrice")] public double? OutputPrice { get; set; } @@ -93,11 +105,19 @@ public sealed class ModelBillingTokenPrices [JsonPropertyName("batchSize")] public long? BatchSize { get; set; } - /// AI Credits cost per billing batch of cached tokens. + /// Deprecated: use cacheReadPrice. AI Credits cost per billing batch of cached tokens. [JsonPropertyName("cachePrice")] public double? CachePrice { get; set; } - /// Prompt token budget (max_prompt_tokens) for the default tier. The total context window is this value plus the model's max_output_tokens. + /// AI Credits cost per billing batch of cached (read) tokens. + [JsonPropertyName("cacheReadPrice")] + public double? CacheReadPrice { get; set; } + + /// AI Credits cost per billing batch of cache-write (cache creation) tokens. + [JsonPropertyName("cacheWritePrice")] + public double? CacheWritePrice { get; set; } + + /// Deprecated: use maxPromptTokens. Prompt token budget for the default tier. The total context window is this value plus the model's max_output_tokens. [JsonPropertyName("contextMax")] public long? ContextMax { get; set; } @@ -109,6 +129,10 @@ public sealed class ModelBillingTokenPrices [JsonPropertyName("longContext")] public ModelBillingTokenPricesLongContext? LongContext { get; set; } + /// Prompt token budget for the default tier. The total context window is this value plus the model's max_output_tokens. + [JsonPropertyName("maxPromptTokens")] + public long? MaxPromptTokens { get; set; } + /// AI Credits cost per billing batch of output tokens. [JsonPropertyName("outputPrice")] public double? OutputPrice { get; set; } @@ -1141,6 +1165,92 @@ internal sealed class SessionFsSetProviderRequest public string SessionStatePath { get; set; } = string.Empty; } +/// Indicates whether the calling client was registered as the LLM inference provider. +[Experimental(Diagnostics.Experimental)] +public sealed class LlmInferenceSetProviderResult +{ + /// Whether the provider was set successfully. + [JsonPropertyName("success")] + public bool Success { get; set; } +} + +/// Whether the start frame was accepted. +[Experimental(Diagnostics.Experimental)] +public sealed class LlmInferenceHttpResponseStartResult +{ + /// True when the response start was matched to a pending request; false when unknown. + [JsonPropertyName("accepted")] + public bool Accepted { get; set; } +} + +/// Response head. +[Experimental(Diagnostics.Experimental)] +internal sealed class LlmInferenceHttpResponseStartRequest +{ + /// Gets or sets the headers value. + [JsonPropertyName("headers")] + public IDictionary> Headers { get => field ??= new Dictionary>(); set; } + + /// Matches the requestId from the originating httpRequestStart frame. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; + + /// HTTP status code. + [JsonPropertyName("status")] + public long Status { get; set; } + + /// Optional HTTP status reason phrase. + [JsonPropertyName("statusText")] + public string? StatusText { get; set; } +} + +/// Whether the chunk was accepted. +[Experimental(Diagnostics.Experimental)] +public sealed class LlmInferenceHttpResponseChunkResult +{ + /// True when the chunk was matched to a pending request; false when unknown. + [JsonPropertyName("accepted")] + public bool Accepted { get; set; } +} + +/// Set to terminate the response with a transport-level failure. Implies end-of-stream; any further chunks for this requestId are ignored. +[Experimental(Diagnostics.Experimental)] +public sealed class LlmInferenceHttpResponseChunkError +{ + /// Optional machine-readable error code. + [JsonPropertyName("code")] + public string? Code { get; set; } + + /// Human-readable failure description. + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; +} + +/// A response body chunk or terminal error. +[Experimental(Diagnostics.Experimental)] +internal sealed class LlmInferenceHttpResponseChunkRequest +{ + /// When true, `data` is base64-encoded bytes. When absent or false, `data` is UTF-8 text. + [JsonPropertyName("binary")] + public bool? Binary { get; set; } + + /// Body byte range. UTF-8 text when `binary` is absent or false; base64-encoded bytes when `binary` is true. May be empty (e.g. when the response body is empty: send a single chunk with empty data and end=true). + [JsonPropertyName("data")] + public string Data { get; set; } = string.Empty; + + /// When true, this is the final body chunk for the response. The runtime treats the response body as complete after receiving an end-marked chunk. + [JsonPropertyName("end")] + public bool? End { get; set; } + + /// Set to terminate the response with a transport-level failure. Implies end-of-stream; any further chunks for this requestId are ignored. + [JsonPropertyName("error")] + public LlmInferenceHttpResponseChunkError? Error { get; set; } + + /// Matches the requestId from the originating httpRequestStart frame. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; +} + /// Pre-resolved working-directory context for session startup. [Experimental(Diagnostics.Experimental)] public sealed class SessionContext @@ -5402,6 +5512,85 @@ internal sealed class McpOauthRespondRequest public string SessionId { get; set; } = string.Empty; } +/// Indicates whether the pending MCP OAuth response was accepted. +[Experimental(Diagnostics.Experimental)] +public sealed class McpOauthHandlePendingResult +{ + /// Whether the response was accepted. False if the request was unknown, timed out, or already resolved. + [JsonPropertyName("success")] + public bool Success { get; set; } +} + +/// Host response to the pending OAuth request. +/// Polymorphic base type discriminated by kind. +[Experimental(Diagnostics.Experimental)] +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(McpOauthPendingRequestResponseToken), "token")] +[JsonDerivedType(typeof(McpOauthPendingRequestResponseCancelled), "cancelled")] +public partial class McpOauthPendingRequestResponse +{ + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} + + +/// The token variant of . +[Experimental(Diagnostics.Experimental)] +public partial class McpOauthPendingRequestResponseToken : McpOauthPendingRequestResponse +{ + /// + [JsonIgnore] + public override string Kind => "token"; + + /// Access token acquired by the SDK host. + [JsonPropertyName("accessToken")] + public required string AccessToken { get; set; } + + /// Token lifetime in seconds, if known. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("expiresIn")] + public long? ExpiresIn { get; set; } + + /// Refresh token supplied by the host, if available. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("refreshToken")] + public string? RefreshToken { get; set; } + + /// OAuth token type. Defaults to Bearer when omitted. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("tokenType")] + public string? TokenType { get; set; } +} + +/// The cancelled variant of . +[Experimental(Diagnostics.Experimental)] +public partial class McpOauthPendingRequestResponseCancelled : McpOauthPendingRequestResponse +{ + /// + [JsonIgnore] + public override string Kind => "cancelled"; +} + +/// Pending MCP OAuth request ID and host-provided token or cancellation response. +[Experimental(Diagnostics.Experimental)] +internal sealed class McpOauthHandlePendingRequest +{ + /// OAuth request identifier from the mcp.oauth_required event. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; + + /// Host response to the pending OAuth request. + [JsonPropertyName("result")] + public McpOauthPendingRequestResponse Result { get => field ??= new(); set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + /// OAuth authorization URL the caller should open, or empty when cached tokens already authenticated the server. [Experimental(Diagnostics.Experimental)] public sealed class McpOauthLoginResult @@ -16049,6 +16238,12 @@ internal async Task ConnectAsync(string? token = null, Cancellati Interlocked.CompareExchange(ref field, new(_rpc), null) ?? field; + /// LlmInference APIs. + public ServerLlmInferenceApi LlmInference => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + /// Sessions APIs. public ServerSessionsApi Sessions => field ?? @@ -16630,6 +16825,59 @@ public async Task SetProviderAsync(string initialCwd } } +/// Provides server-scoped LlmInference APIs. +[Experimental(Diagnostics.Experimental)] +public sealed class ServerLlmInferenceApi +{ + private readonly JsonRpc _rpc; + + internal ServerLlmInferenceApi(JsonRpc rpc) + { + _rpc = rpc; + } + + /// Registers an SDK client as the LLM inference callback provider. + /// The to monitor for cancellation requests. The default is . + /// Indicates whether the calling client was registered as the LLM inference provider. + public async Task SetProviderAsync(CancellationToken cancellationToken = default) + { + return await CopilotClient.InvokeRpcAsync(_rpc, "llmInference.setProvider", [], cancellationToken); + } + + /// Delivers the response head (status + headers) for an in-flight request, correlated by the requestId the runtime supplied in httpRequestStart. Must be called exactly once per request before any httpResponseChunk frames. + /// Matches the requestId from the originating httpRequestStart frame. + /// HTTP status code. + /// The headers parameter. + /// Optional HTTP status reason phrase. + /// The to monitor for cancellation requests. The default is . + /// Whether the start frame was accepted. + public async Task HttpResponseStartAsync(string requestId, long status, IDictionary> headers, string? statusText = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(requestId); + ArgumentNullException.ThrowIfNull(headers); + + var request = new LlmInferenceHttpResponseStartRequest { RequestId = requestId, Status = status, Headers = headers, StatusText = statusText }; + return await CopilotClient.InvokeRpcAsync(_rpc, "llmInference.httpResponseStart", [request], cancellationToken); + } + + /// Delivers a body byte range (or a terminal transport error) for an in-flight response, correlated by requestId. Set `end` true on the last chunk. When `error` is set the response terminates with a transport-level failure and the runtime raises an APIConnectionError. + /// Matches the requestId from the originating httpRequestStart frame. + /// Body byte range. UTF-8 text when `binary` is absent or false; base64-encoded bytes when `binary` is true. May be empty (e.g. when the response body is empty: send a single chunk with empty data and end=true). + /// When true, `data` is base64-encoded bytes. When absent or false, `data` is UTF-8 text. + /// When true, this is the final body chunk for the response. The runtime treats the response body as complete after receiving an end-marked chunk. + /// Set to terminate the response with a transport-level failure. Implies end-of-stream; any further chunks for this requestId are ignored. + /// The to monitor for cancellation requests. The default is . + /// Whether the chunk was accepted. + public async Task HttpResponseChunkAsync(string requestId, string data, bool? binary = null, bool? end = null, LlmInferenceHttpResponseChunkError? error = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(requestId); + ArgumentNullException.ThrowIfNull(data); + + var request = new LlmInferenceHttpResponseChunkRequest { RequestId = requestId, Data = data, Binary = binary, End = end, Error = error }; + return await CopilotClient.InvokeRpcAsync(_rpc, "llmInference.httpResponseChunk", [request], cancellationToken); + } +} + /// Provides server-scoped Sessions APIs. [Experimental(Diagnostics.Experimental)] public sealed class ServerSessionsApi @@ -18355,7 +18603,7 @@ internal McpOauthApi(CopilotSession session) _session = session; } - /// Responds to a pending MCP OAuth provider request. Marked internal because the `provider` argument is an in-process OAuthClientProvider instance that cannot be carried over the wire; the public OAuth surface will route the response through a wire-clean handshake once the CLI moves on top of the SDK. + /// Responds to a pending MCP OAuth request with an in-process provider. This internal CLI-only API accepts a live OAuthClientProvider instance and cannot be used over the SDK JSON-RPC boundary. Use session.mcp.oauth.handlePendingRequest instead for the public SDK-safe response path. /// OAuth request identifier from mcp.oauth_required. /// In-process OAuthClientProvider instance, or omitted to deny. Marked internal: cannot be serialized across the JSON-RPC boundary. /// The to monitor for cancellation requests. The default is . @@ -18369,6 +18617,21 @@ internal async Task RespondAsync(string requestId, object return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.mcp.oauth.respond", [request], cancellationToken); } + /// Resolves a pending MCP OAuth request with a host-provided token or cancellation. The pending request is emitted as mcp.oauth_required with the data necessary to authorize the request. + /// OAuth request identifier from the mcp.oauth_required event. + /// Host response to the pending OAuth request. + /// The to monitor for cancellation requests. The default is . + /// Indicates whether the pending MCP OAuth response was accepted. + public async Task HandlePendingRequestAsync(string requestId, McpOauthPendingRequestResponse result, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(requestId); + ArgumentNullException.ThrowIfNull(result); + _session.ThrowIfDisposed(); + + var request = new McpOauthHandlePendingRequest { SessionId = _session.SessionId, RequestId = requestId, Result = result }; + return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.mcp.oauth.handlePendingRequest", [request], cancellationToken); + } + /// Starts OAuth authentication for a remote MCP server. /// Name of the remote MCP server to authenticate. /// When true, clears any cached OAuth token for the server and runs a full new authorization. Use when the user explicitly wants to switch accounts or believes their session is stuck. @@ -20109,6 +20372,16 @@ public static void RegisterClientSessionApiHandlers(JsonRpc rpc, FuncProvider-agnostic citations linking spans of this message's content to the sources that support them. Experimental; only populated when citation emission is enabled. + [Experimental(Diagnostics.Experimental)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("citations")] + public Citations? Citations { get; set; } + /// The assistant's text response content. [JsonPropertyName("content")] public required string Content { get; set; } @@ -2459,17 +2465,32 @@ public sealed partial class ModelCallFailureData [JsonPropertyName("apiCallId")] public string? ApiCallId { get; set; } + /// For HTTP 400 failures only: whether the response carried a structured CAPI error envelope (structured_error, a deterministic validation failure) or no error body (bodyless, the transient gateway/proxy signature). Absent for non-400 failures. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("badRequestKind")] + public ModelCallFailureBadRequestKind? BadRequestKind { get; set; } + /// Duration of the failed API call in milliseconds. [JsonConverter(typeof(MillisecondsTimeSpanConverter))] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("durationMs")] public TimeSpan? Duration { get; set; } + /// For HTTP 400 failures only: the `code` from the CAPI error envelope (e.g. 'model_max_prompt_tokens_exceeded') identifying which deterministic validation failure occurred. Raw server-controlled string, emitted only through restricted telemetry. Absent for bodyless or non-400 failures. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("errorCode")] + public string? ErrorCode { get; set; } + /// Raw provider/runtime error message for restricted telemetry. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("errorMessage")] public string? ErrorMessage { get; set; } + /// For HTTP 400 failures only: the `type` from the CAPI error envelope (e.g. 'websocket_error'), a coarser companion to errorCode for envelopes that carry no code. Raw server-controlled string, emitted only through restricted telemetry. Absent for bodyless or non-400 failures. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("errorType")] + public string? ErrorType { get; set; } + /// What initiated this API call (e.g., "sub-agent", "mcp-sampling"); absent for user-initiated calls. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("initiator")] @@ -3128,10 +3149,15 @@ public sealed partial class SamplingCompletedData /// OAuth authentication request for an MCP server. public sealed partial class McpOauthRequiredData { - /// Unique identifier for this OAuth request; used to respond via session.respondToMcpOAuth(). + /// Unique identifier for this OAuth request; used to respond via session.mcp.oauth.handlePendingRequest. [JsonPropertyName("requestId")] public required string RequestId { get; set; } + /// Raw OAuth protected-resource metadata document fetched for the MCP server, if available. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("resourceMetadata")] + public string? ResourceMetadata { get; set; } + /// Display name of the MCP server that requires OAuth. [JsonPropertyName("serverName")] public required string ServerName { get; set; } @@ -3144,11 +3170,20 @@ public sealed partial class McpOauthRequiredData [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("staticClientConfig")] public McpOauthRequiredStaticClientConfig? StaticClientConfig { get; set; } + + /// OAuth WWW-Authenticate parameters parsed from the auth challenge, if available. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("wwwAuthenticateParams")] + public McpOauthWWWAuthenticateParams? WwwAuthenticateParams { get; set; } } /// MCP OAuth request completion notification. public sealed partial class McpOauthCompletedData { + /// How the pending OAuth request was completed. + [JsonPropertyName("outcome")] + public required McpOauthCompletionOutcome Outcome { get; set; } + /// Request ID of the resolved OAuth request. [JsonPropertyName("requestId")] public required string RequestId { get; set; } @@ -3827,6 +3862,16 @@ public sealed partial class AttachmentFile : Attachment [JsonIgnore] public override string Type => "file"; + /// Internal: content-addressed id of the session.binary_asset event holding this attachment's model-facing bytes (e.g. "sha256:..."). Absent externally. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("assetId")] + public string? AssetId { get; set; } + + /// Internal: decoded byte length of the attachment's model-facing bytes. Absent externally. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("byteLength")] + public long? ByteLength { get; set; } + /// User-facing display name for the attachment. [JsonPropertyName("displayName")] public required string DisplayName { get; set; } @@ -3836,6 +3881,16 @@ public sealed partial class AttachmentFile : Attachment [JsonPropertyName("lineRange")] public AttachmentFileLineRange? LineRange { get; set; } + /// Internal: MIME type of the file's model-facing bytes (post-resize for images). Set when the file's bytes are interned to an asset. Absent externally. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("mimeType")] + public string? MimeType { get; set; } + + /// Internal: why model-facing bytes are absent from persistence. Absent externally. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("omittedReason")] + public OmittedBinaryOmittedReason? OmittedReason { get; set; } + /// Absolute file path. [JsonPropertyName("path")] public required string Path { get; set; } @@ -3959,10 +4014,21 @@ public sealed partial class AttachmentBlob : Attachment [JsonIgnore] public override string Type => "blob"; - /// Base64-encoded content. + /// Internal: content-addressed id of the session.binary_asset event holding this attachment's model-facing bytes (e.g. "sha256:..."). Absent externally. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("assetId")] + public string? AssetId { get; set; } + + /// Internal: decoded byte length of the attachment's model-facing bytes. Absent externally. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("byteLength")] + public long? ByteLength { get; set; } + + /// Base64-encoded content. Present on input and for external consumers; replaced by an internal `assetId` reference in persisted events when interned to a content-addressed asset. [Base64String] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("data")] - public required string Data { get; set; } + public string? Data { get; set; } /// User-facing display name for the attachment. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] @@ -3972,6 +4038,11 @@ public sealed partial class AttachmentBlob : Attachment /// MIME type of the inline data. [JsonPropertyName("mimeType")] public required string MimeType { get; set; } + + /// Internal: why model-facing bytes are absent from persistence. Absent externally. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("omittedReason")] + public OmittedBinaryOmittedReason? OmittedReason { get; set; } } /// Structured context contributed by an extension. Composer pills displayed in the host are forwarded back through session.send.attachments, then rendered into the model prompt as an <extension_context> XML block. @@ -4031,6 +4102,163 @@ public partial class Attachment } +/// A source that backs one or more cited spans in the assistant's response. +/// Nested data type for CitationSource. +[Experimental(Diagnostics.Experimental)] +public sealed partial class CitationSource +{ + /// Stable, turn-scoped identifier for this source, referenced by CitationReference.sourceId. + [JsonPropertyName("id")] + public required string Id { get; set; } + + /// File path relative to the agent's workspace root, when the source is a file. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("path")] + public string? Path { get; set; } + + /// The system that produced this citation. + [JsonPropertyName("provider")] + public required CitationProvider Provider { get; set; } + + /// Human-readable title of the source. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("title")] + public string? Title { get; set; } + + /// URL of the source, when it is a web resource. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("url")] + public string? Url { get; set; } +} + +/// A character range within the source's text content. +/// The char variant of . +[Experimental(Diagnostics.Experimental)] +public sealed partial class CitationLocationChar : CitationLocation +{ + /// + [JsonIgnore] + public override string Type => "char"; + + /// End character offset within the source text (zero-based, exclusive). + [JsonPropertyName("endIndex")] + public required long EndIndex { get; set; } + + /// Start character offset within the source text (zero-based, inclusive). + [JsonPropertyName("startIndex")] + public required long StartIndex { get; set; } +} + +/// A page range within a paginated source document. +/// The page variant of . +[Experimental(Diagnostics.Experimental)] +public sealed partial class CitationLocationPage : CitationLocation +{ + /// + [JsonIgnore] + public override string Type => "page"; + + /// Last page number of the cited range (inclusive). + [JsonPropertyName("endPage")] + public required long EndPage { get; set; } + + /// First page number of the cited range. + [JsonPropertyName("startPage")] + public required long StartPage { get; set; } +} + +/// A content-block range within a structured source document. +/// The block variant of . +[Experimental(Diagnostics.Experimental)] +public sealed partial class CitationLocationBlock : CitationLocation +{ + /// + [JsonIgnore] + public override string Type => "block"; + + /// Index of the last content block of the cited range (zero-based, exclusive). + [JsonPropertyName("endBlock")] + public required long EndBlock { get; set; } + + /// Index of the first content block of the cited range (zero-based, inclusive). + [JsonPropertyName("startBlock")] + public required long StartBlock { get; set; } +} + +/// Location within a cited source (character, page, or content-block range) that supports a span. +/// Polymorphic base type discriminated by type. +[Experimental(Diagnostics.Experimental)] +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "type", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(CitationLocationChar), "char")] +[JsonDerivedType(typeof(CitationLocationPage), "page")] +[JsonDerivedType(typeof(CitationLocationBlock), "block")] +public partial class CitationLocation +{ + /// The type discriminator. + [JsonPropertyName("type")] + public virtual string Type { get; set; } = string.Empty; +} + + +/// A single citation occurrence linking a span of generated text to a supporting source. +/// Nested data type for CitationReference. +[Experimental(Diagnostics.Experimental)] +public sealed partial class CitationReference +{ + /// The exact text from the source that supports the cited span, when provided by the model. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("citedText")] + public string? CitedText { get; set; } + + /// Location within the source that supports the cited span, when the provider reports one. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("location")] + public CitationLocation? Location { get; set; } + + /// Provider-native citation correlation data (e.g. Anthropic search_result_index / document_index), passed through opaquely for debugging and forward compatibility. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("providerMetadata")] + public JsonElement? ProviderMetadata { get; set; } + + /// Identifier of the CitationSource this reference points to (CitationSource.id). + [JsonPropertyName("sourceId")] + public required string SourceId { get; set; } +} + +/// A contiguous span of generated assistant text and the source references that support it. +/// Nested data type for CitationSpan. +[Experimental(Diagnostics.Experimental)] +public sealed partial class CitationSpan +{ + /// End offset of the cited span within the final assistant message content (UTF-16 code units, zero-based, exclusive). + [JsonPropertyName("endIndex")] + public required long EndIndex { get; set; } + + /// The sources that support this span of generated text. + [JsonPropertyName("references")] + public required CitationReference[] References { get; set; } + + /// Start offset of the cited span within the final assistant message content (UTF-16 code units, zero-based, inclusive). + [JsonPropertyName("startIndex")] + public required long StartIndex { get; set; } +} + +/// Provider-agnostic citations linking spans of the assistant's response to their supporting sources. +/// Nested data type for Citations. +[Experimental(Diagnostics.Experimental)] +public sealed partial class Citations +{ + /// Deduplicated set of sources referenced by the citation spans. + [JsonPropertyName("sources")] + public required CitationSource[] Sources { get; set; } + + /// Spans of generated text annotated with the sources that support them. + [JsonPropertyName("spans")] + public required CitationSpan[] Spans { get; set; } +} + /// Neutral provider-tagged server-side tool-use payload (tool search, advisor) for verbatim round-tripping. /// Nested data type for AssistantMessageServerTools. [Experimental(Diagnostics.Experimental)] @@ -4437,6 +4665,35 @@ public override void Write(Utf8JsonWriter writer, PersistedBinaryResult value, J } } +/// A source supplied by a tool that should be made available to the model as citable content. +/// Nested data type for CitableSource. +[Experimental(Diagnostics.Experimental)] +public sealed partial class CitableSource +{ + /// The source text made available to the model as citable content. + [JsonPropertyName("content")] + public required string Content { get; set; } + + /// Stable identifier for this source within the tool result. Used for deduplication and may be used by future provider integrations to correlate response citations back to the originating source. + [JsonPropertyName("id")] + public required string Id { get; set; } + + /// File path relative to the agent's workspace root, when the source is a file. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("path")] + public string? Path { get; set; } + + /// Human-readable title of the source. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("title")] + public string? Title { get; set; } + + /// URL of the source, when it is a web resource. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("url")] + public string? Url { get; set; } +} + /// Plain text content block. /// The text variant of . public sealed partial class ToolExecutionCompleteContentText : ToolExecutionCompleteContent @@ -4869,6 +5126,12 @@ public sealed partial class ToolExecutionCompleteResult [JsonPropertyName("binaryResultsForLlm")] public PersistedBinaryResult[]? BinaryResultsForLlm { get; set; } + /// Provider-neutral source material this tool makes available to the model as citable content. Persisted so it survives session resume. Experimental. + [Experimental(Diagnostics.Experimental)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("citableSources")] + public CitableSource[]? CitableSources { get; set; } + /// Concise tool result text sent to the LLM for chat completion, potentially truncated for token efficiency. [JsonPropertyName("content")] public required string Content { get; set; } @@ -6139,6 +6402,25 @@ public sealed partial class McpOauthRequiredStaticClientConfig public bool? PublicClient { get; set; } } +/// OAuth WWW-Authenticate parameters parsed from an MCP auth challenge. +/// Nested data type for McpOauthWWWAuthenticateParams. +public sealed partial class McpOauthWWWAuthenticateParams +{ + /// OAuth error from the WWW-Authenticate error parameter, if present. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("error")] + public string? Error { get; set; } + + /// Protected resource metadata URL from the WWW-Authenticate resource_metadata parameter. + [JsonPropertyName("resourceMetadataUrl")] + public required string ResourceMetadataUrl { get; set; } + + /// Requested OAuth scopes from the WWW-Authenticate scope parameter, if present. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("scope")] + public string? Scope { get; set; } +} + /// Schema for the `CommandsChangedCommand` type. /// Nested data type for CommandsChangedCommand. public sealed partial class CommandsChangedCommand @@ -7086,6 +7368,67 @@ public override void Write(Utf8JsonWriter writer, UserMessageAgentMode value, Js } } +/// Why the binary data is absent: it exceeded the inline size limit, or its asset was unavailable. +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct OmittedBinaryOmittedReason : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public OmittedBinaryOmittedReason(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// Bytes exceeded the session's inline size limit. + public static OmittedBinaryOmittedReason TooLarge { get; } = new("too_large"); + + /// The referenced binary asset could not be found (e.g. a truncated log). + public static OmittedBinaryOmittedReason AssetUnavailable { get; } = new("asset_unavailable"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(OmittedBinaryOmittedReason left, OmittedBinaryOmittedReason right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(OmittedBinaryOmittedReason left, OmittedBinaryOmittedReason right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is OmittedBinaryOmittedReason other && Equals(other); + + /// + public bool Equals(OmittedBinaryOmittedReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override OmittedBinaryOmittedReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, OmittedBinaryOmittedReason value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(OmittedBinaryOmittedReason)); + } + } +} + /// Type of GitHub reference. [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] @@ -7150,6 +7493,71 @@ public override void Write(Utf8JsonWriter writer, AttachmentGitHubReferenceType } } +/// The system that produced a citation. +[Experimental(Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct CitationProvider : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public CitationProvider(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// Citation produced by an Anthropic (Claude) model response. + public static CitationProvider Anthropic { get; } = new("anthropic"); + + /// Citation produced by an OpenAI model response. + public static CitationProvider Openai { get; } = new("openai"); + + /// Citation synthesized client-side by the runtime from tool output. + public static CitationProvider Client { get; } = new("client"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CitationProvider left, CitationProvider right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CitationProvider left, CitationProvider right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is CitationProvider other && Equals(other); + + /// + public bool Equals(CitationProvider other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override CitationProvider Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, CitationProvider value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CitationProvider)); + } + } +} + /// Tool call type: "function" for standard tool calls, "custom" for grammar-based tool calls. Defaults to "function" when absent. [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] @@ -7278,6 +7686,67 @@ public override void Write(Utf8JsonWriter writer, AssistantUsageApiEndpoint valu } } +/// For HTTP 400 failures only: whether the response carried a structured CAPI error envelope (structured_error, a deterministic validation failure) or no error body (bodyless, the transient gateway/proxy signature). Absent for non-400 failures. +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ModelCallFailureBadRequestKind : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ModelCallFailureBadRequestKind(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// The 400 response carried no error body (transient gateway/proxy signature). + public static ModelCallFailureBadRequestKind Bodyless { get; } = new("bodyless"); + + /// The 400 response carried a structured CAPI error envelope (deterministic validation failure). + public static ModelCallFailureBadRequestKind StructuredError { get; } = new("structured_error"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ModelCallFailureBadRequestKind left, ModelCallFailureBadRequestKind right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ModelCallFailureBadRequestKind left, ModelCallFailureBadRequestKind right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is ModelCallFailureBadRequestKind other && Equals(other); + + /// + public bool Equals(ModelCallFailureBadRequestKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override ModelCallFailureBadRequestKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, ModelCallFailureBadRequestKind value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ModelCallFailureBadRequestKind)); + } + } +} + /// Where the failed model call originated. [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] @@ -7528,67 +7997,6 @@ public override void Write(Utf8JsonWriter writer, PersistedBinaryImageType value } } -/// Why the binary data is absent: it exceeded the inline size limit, or its asset was unavailable. -[JsonConverter(typeof(Converter))] -[DebuggerDisplay("{Value,nq}")] -public readonly struct OmittedBinaryOmittedReason : IEquatable -{ - private readonly string? _value; - - /// Initializes a new instance of the struct. - /// The value to associate with this . - [JsonConstructor] - public OmittedBinaryOmittedReason(string value) - { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; - } - - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; - - /// Bytes exceeded the session's inline size limit. - public static OmittedBinaryOmittedReason TooLarge { get; } = new("too_large"); - - /// The referenced binary asset could not be found (e.g. a truncated log). - public static OmittedBinaryOmittedReason AssetUnavailable { get; } = new("asset_unavailable"); - - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(OmittedBinaryOmittedReason left, OmittedBinaryOmittedReason right) => left.Equals(right); - - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(OmittedBinaryOmittedReason left, OmittedBinaryOmittedReason right) => !(left == right); - - /// - public override bool Equals(object? obj) => obj is OmittedBinaryOmittedReason other && Equals(other); - - /// - public bool Equals(OmittedBinaryOmittedReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - - /// - public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - - /// - public override string ToString() => Value; - - /// Provides a for serializing instances. - [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter - { - /// - public override OmittedBinaryOmittedReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); - } - - /// - public override void Write(Utf8JsonWriter writer, OmittedBinaryOmittedReason value, JsonSerializerOptions options) - { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(OmittedBinaryOmittedReason)); - } - } -} - /// Binary result type discriminator. Use "image" for images and "resource" for other binary data. [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] @@ -8391,6 +8799,67 @@ public override void Write(Utf8JsonWriter writer, ElicitationCompletedAction val } } +/// How the pending MCP OAuth request was completed. +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct McpOauthCompletionOutcome : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public McpOauthCompletionOutcome(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// The request completed with a token-backed OAuth provider. + public static McpOauthCompletionOutcome Token { get; } = new("token"); + + /// The request completed without an OAuth provider. + public static McpOauthCompletionOutcome Cancelled { get; } = new("cancelled"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpOauthCompletionOutcome left, McpOauthCompletionOutcome right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpOauthCompletionOutcome left, McpOauthCompletionOutcome right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is McpOauthCompletionOutcome other && Equals(other); + + /// + public bool Equals(McpOauthCompletionOutcome other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override McpOauthCompletionOutcome Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, McpOauthCompletionOutcome value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpOauthCompletionOutcome)); + } + } +} + /// The user's auto-mode-switch choice. [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] @@ -9053,6 +9522,15 @@ public override void Write(Utf8JsonWriter writer, CanvasOpenedAvailability value [JsonSerializable(typeof(CapabilitiesChangedData))] [JsonSerializable(typeof(CapabilitiesChangedEvent))] [JsonSerializable(typeof(CapabilitiesChangedUI))] +[JsonSerializable(typeof(CitableSource))] +[JsonSerializable(typeof(CitationLocation))] +[JsonSerializable(typeof(CitationLocationBlock))] +[JsonSerializable(typeof(CitationLocationChar))] +[JsonSerializable(typeof(CitationLocationPage))] +[JsonSerializable(typeof(CitationReference))] +[JsonSerializable(typeof(CitationSource))] +[JsonSerializable(typeof(CitationSpan))] +[JsonSerializable(typeof(Citations))] [JsonSerializable(typeof(CommandCompletedData))] [JsonSerializable(typeof(CommandCompletedEvent))] [JsonSerializable(typeof(CommandExecuteData))] @@ -9100,6 +9578,7 @@ public override void Write(Utf8JsonWriter writer, CanvasOpenedAvailability value [JsonSerializable(typeof(McpOauthRequiredData))] [JsonSerializable(typeof(McpOauthRequiredEvent))] [JsonSerializable(typeof(McpOauthRequiredStaticClientConfig))] +[JsonSerializable(typeof(McpOauthWWWAuthenticateParams))] [JsonSerializable(typeof(McpServersLoadedServer))] [JsonSerializable(typeof(ModelCallFailureData))] [JsonSerializable(typeof(ModelCallFailureEvent))] diff --git a/dotnet/test/Harness/E2ETestContext.cs b/dotnet/test/Harness/E2ETestContext.cs index 8c6465f05..2e2043183 100644 --- a/dotnet/test/Harness/E2ETestContext.cs +++ b/dotnet/test/Harness/E2ETestContext.cs @@ -140,11 +140,21 @@ private static string GetCliPath(string repoRoot) var envPath = Environment.GetEnvironmentVariable("COPILOT_CLI_PATH"); if (!string.IsNullOrEmpty(envPath)) return envPath; - var path = Path.Combine(repoRoot, "nodejs/node_modules/@github/copilot/index.js"); - if (!File.Exists(path)) - throw new InvalidOperationException($"CLI not found at {path}. Run 'npm install' in the nodejs directory first."); + // As of CLI 1.0.64-1 the @github/copilot package is a thin loader; the + // runnable index.js ships in the installed platform package + // (e.g. @github/copilot-linux-x64). Exactly one is installed. + var githubModules = Path.Join(repoRoot, "nodejs", "node_modules", "@github"); + if (Directory.Exists(githubModules)) + { + var candidate = Directory.EnumerateDirectories(githubModules, "copilot-*") + .Select(dir => Path.Join(dir, "index.js")) + .FirstOrDefault(File.Exists); + if (candidate != null) + return candidate; + } - return path; + throw new InvalidOperationException( + $"CLI not found under {githubModules}. Run 'npm install' in the nodejs directory first."); } public async Task ConfigureForTestAsync(string testFile, [CallerMemberName] string? testName = null) @@ -173,6 +183,11 @@ public Dictionary GetEnvironment() .ToDictionary(e => (string)e.Key, e => e.Value?.ToString()); env["COPILOT_API_URL"] = ProxyUrl; + // Route GitHub API calls (e.g. the MCP registry policy check) to the + // replay proxy so MCP enablement stays hermetic. Without this the CLI + // reaches the real api.github.com, which is slow/unreachable on macOS + // CI runners and makes MCP servers time out before reaching connected. + env["COPILOT_DEBUG_GITHUB_API_URL"] = ProxyUrl; env["COPILOT_HOME"] = HomeDir; env["GH_CONFIG_DIR"] = HomeDir; env["XDG_CONFIG_HOME"] = HomeDir; diff --git a/go/client_test.go b/go/client_test.go index e54689fa9..a3051f881 100644 --- a/go/client_test.go +++ b/go/client_test.go @@ -420,18 +420,16 @@ func TestClient_SessionIdleTimeoutSeconds(t *testing.T) { } func findCLIPathForTest() string { - abs, _ := filepath.Abs("../nodejs/node_modules/@github/copilot/index.js") - if fileExistsForTest(abs) { - return abs + base, err := filepath.Abs("../nodejs/node_modules/@github") + if err == nil { + matches, _ := filepath.Glob(filepath.Join(base, "copilot-*", "index.js")) + if len(matches) > 0 { + return matches[0] + } } return "" } -func fileExistsForTest(path string) bool { - _, err := os.Stat(path) - return err == nil -} - func TestCreateSessionRequest_ClientName(t *testing.T) { t.Run("includes clientName in JSON when set", func(t *testing.T) { req := createSessionRequest{ClientName: "my-app"} @@ -1098,7 +1096,7 @@ func TestModelBillingTokenPricesJSON(t *testing.T) { "inputPrice": 4.0, "outputPrice": 16.0, "cachePrice": 1.0, - "contextMax": 1000000 + "maxPromptTokens": 1000000 } } }` @@ -1109,10 +1107,10 @@ func TestModelBillingTokenPricesJSON(t *testing.T) { BatchSize: int64Ptr(1000000), ContextMax: int64Ptr(128000), LongContext: &rpc.ModelBillingTokenPricesLongContext{ - InputPrice: Float64(4.0), - OutputPrice: Float64(16.0), - CachePrice: Float64(1.0), - ContextMax: int64Ptr(1000000), + InputPrice: Float64(4.0), + OutputPrice: Float64(16.0), + CachePrice: Float64(1.0), + MaxPromptTokens: int64Ptr(1000000), }, } @@ -1135,8 +1133,8 @@ func TestModelBillingTokenPricesJSON(t *testing.T) { if lc.InputPrice == nil || *lc.InputPrice != 4.0 { t.Errorf("unexpected LongContext.InputPrice: %v", lc.InputPrice) } - if lc.ContextMax == nil || *lc.ContextMax != 1000000 { - t.Errorf("unexpected LongContext.ContextMax: %v", lc.ContextMax) + if lc.MaxPromptTokens == nil || *lc.MaxPromptTokens != 1000000 { + t.Errorf("unexpected LongContext.MaxPromptTokens: %v", lc.MaxPromptTokens) } // Round-trip back to JSON and ensure the nested structure survives. diff --git a/go/internal/e2e/session_e2e_test.go b/go/internal/e2e/session_e2e_test.go index dc2d54ca8..9e21e82be 100644 --- a/go/internal/e2e/session_e2e_test.go +++ b/go/internal/e2e/session_e2e_test.go @@ -1104,7 +1104,7 @@ func TestSessionBlobAttachmentE2E(t *testing.T) { Prompt: "Describe this image", Attachments: []copilot.Attachment{ &copilot.AttachmentBlob{ - Data: data, + Data: &data, MIMEType: mimeType, DisplayName: &displayName, }, diff --git a/go/internal/e2e/testharness/context.go b/go/internal/e2e/testharness/context.go index fae5623aa..adceb9a74 100644 --- a/go/internal/e2e/testharness/context.go +++ b/go/internal/e2e/testharness/context.go @@ -29,11 +29,17 @@ func CLIPath() string { return } - // Look for CLI in sibling nodejs directory's node_modules - abs, err := filepath.Abs("../../../nodejs/node_modules/@github/copilot/index.js") - if err == nil && fileExists(abs) { - cliPath = abs - return + // Look for CLI in sibling nodejs directory's node_modules. As of CLI + // 1.0.64-1 the @github/copilot package is a thin loader; the runnable + // index.js ships in the installed platform package + // (e.g. @github/copilot-linux-x64). + base, err := filepath.Abs("../../../nodejs/node_modules/@github") + if err == nil { + matches, _ := filepath.Glob(filepath.Join(base, "copilot-*", "index.js")) + if len(matches) > 0 { + cliPath = matches[0] + return + } } }) return cliPath @@ -207,6 +213,11 @@ func (c *TestContext) Env() []string { env = append(env, c.proxy.ProxyEnv()...) env = append(env, "COPILOT_API_URL="+c.ProxyURL, + // Route GitHub API calls (e.g. the MCP registry policy check) to the + // replay proxy so MCP enablement stays hermetic. Without this the CLI + // reaches the real api.github.com, which is slow/unreachable on macOS + // CI runners and makes MCP servers time out before reaching connected. + "COPILOT_DEBUG_GITHUB_API_URL="+c.ProxyURL, "COPILOT_HOME="+c.HomeDir, "COPILOT_SDK_AUTH_TOKEN="+defaultGitHubToken, "GH_CONFIG_DIR="+c.HomeDir, diff --git a/go/rpc/zrpc.go b/go/rpc/zrpc.go index b00bf1c30..b5c10e9b3 100644 --- a/go/rpc/zrpc.go +++ b/go/rpc/zrpc.go @@ -396,12 +396,21 @@ func (r RawAttachmentData) Type() AttachmentType { // Blob attachment with inline base64-encoded data // Experimental: AttachmentBlob is part of an experimental API and may change or be removed. type AttachmentBlob struct { - // Base64-encoded content - Data string `json:"data"` + // Internal: content-addressed id of the session.binary_asset event holding this + // attachment's model-facing bytes (e.g. "sha256:..."). Absent externally. + AssetID *string `json:"assetId,omitempty"` + // Internal: decoded byte length of the attachment's model-facing bytes. Absent externally. + ByteLength *int64 `json:"byteLength,omitempty"` + // Base64-encoded content. Present on input and for external consumers; replaced by an + // internal `assetId` reference in persisted events when interned to a content-addressed + // asset. + Data *string `json:"data,omitempty"` // User-facing display name for the attachment DisplayName *string `json:"displayName,omitempty"` // MIME type of the inline data MIMEType string `json:"mimeType"` + // Internal: why model-facing bytes are absent from persistence. Absent externally. + OmittedReason *OmittedBinaryOmittedReason `json:"omittedReason,omitempty"` } func (AttachmentBlob) attachment() {} @@ -454,10 +463,20 @@ func (AttachmentExtensionContext) Type() AttachmentType { // File attachment // Experimental: AttachmentFile is part of an experimental API and may change or be removed. type AttachmentFile struct { + // Internal: content-addressed id of the session.binary_asset event holding this + // attachment's model-facing bytes (e.g. "sha256:..."). Absent externally. + AssetID *string `json:"assetId,omitempty"` + // Internal: decoded byte length of the attachment's model-facing bytes. Absent externally. + ByteLength *int64 `json:"byteLength,omitempty"` // 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 *AttachmentFileLineRange `json:"lineRange,omitempty"` + // Internal: MIME type of the file's model-facing bytes (post-resize for images). Set when + // the file's bytes are interned to an asset. Absent externally. + MIMEType *string `json:"mimeType,omitempty"` + // Internal: why model-facing bytes are absent from persistence. Absent externally. + OmittedReason *OmittedBinaryOmittedReason `json:"omittedReason,omitempty"` // Absolute file path Path string `json:"path"` } @@ -1916,6 +1935,134 @@ type InstructionSource struct { Type InstructionSourceType `json:"type"` } +// HTTP headers as a map from lowercased header name to a list of values. Multi-valued +// headers (e.g. Set-Cookie) preserve all values. +// Experimental: LlmInferenceHeaders is part of an experimental API and may change or be +// removed. +type LlmInferenceHeaders map[string][]string + +// A request body chunk or cancellation signal. +type LlmInferenceHTTPRequestChunkRequest struct { + // When true, `data` is base64-encoded bytes. When absent or false, `data` is UTF-8 text. + Binary *bool `json:"binary,omitempty"` + // When true, the runtime is cancelling the in-flight request (e.g. upstream consumer + // aborted). `data` is ignored. Implies end-of-request. + Cancel *bool `json:"cancel,omitempty"` + // Optional human-readable reason for the cancellation, propagated for logging. + CancelReason *string `json:"cancelReason,omitempty"` + // Body byte range. UTF-8 text when `binary` is absent or false; base64-encoded bytes when + // `binary` is true. May be empty. + Data string `json:"data"` + // When true, this is the final body chunk for the request. The SDK may rely on having + // received an end-marked chunk before treating the request body as complete. + End *bool `json:"end,omitempty"` + // Matches the requestId from the originating httpRequestStart frame. + RequestID string `json:"requestId"` +} + +// Acknowledgement. The SDK is free to ignore the ack and treat chunk delivery as +// fire-and-forget. +type LlmInferenceHTTPRequestChunkResult struct { +} + +// The head of an outbound model-layer HTTP request. +type LlmInferenceHTTPRequestStartRequest struct { + Headers map[string][]string `json:"headers"` + // HTTP method, e.g. GET, POST. + Method string `json:"method"` + // Opaque runtime-minted id, unique per in-flight request. The SDK uses this to correlate + // httpRequestChunk frames and to address its httpResponseStart / httpResponseChunk replies + // back to the runtime. + RequestID string `json:"requestId"` + // Id of the runtime session that triggered this request, when one is in scope. Absent for + // requests issued outside any session (e.g. startup model-catalog or capability + // resolution). This is a payload field — not a dispatch key — because the client-global API + // is registered process-wide rather than per session. + SessionID *string `json:"sessionId,omitempty"` + // Transport the runtime would otherwise use for this request. `http` (the default when + // absent) covers plain HTTP and SSE responses; `websocket` indicates a full-duplex message + // channel where each body chunk maps to one WebSocket message and the `binary` flag + // distinguishes text from binary frames. The SDK consumer uses this to decide whether to + // service the request with an HTTP client or a WebSocket client. It is the one piece of + // request metadata the consumer cannot reliably infer from the URL or headers alone. + Transport *LlmInferenceHTTPRequestStartTransport `json:"transport,omitempty"` + // Absolute request URL. + URL string `json:"url"` +} + +// Acknowledgement. Returning successfully simply means the SDK accepted the start frame; it +// does not imply the request will succeed. +type LlmInferenceHTTPRequestStartResult struct { +} + +// Set to terminate the response with a transport-level failure. Implies end-of-stream; any +// further chunks for this requestId are ignored. +// Experimental: LlmInferenceHTTPResponseChunkError is part of an experimental API and may +// change or be removed. +type LlmInferenceHTTPResponseChunkError struct { + // Optional machine-readable error code. + Code *string `json:"code,omitempty"` + // Human-readable failure description. + Message string `json:"message"` +} + +// A response body chunk or terminal error. +// Experimental: LlmInferenceHTTPResponseChunkRequest is part of an experimental API and may +// change or be removed. +type LlmInferenceHTTPResponseChunkRequest struct { + // When true, `data` is base64-encoded bytes. When absent or false, `data` is UTF-8 text. + Binary *bool `json:"binary,omitempty"` + // Body byte range. UTF-8 text when `binary` is absent or false; base64-encoded bytes when + // `binary` is true. May be empty (e.g. when the response body is empty: send a single chunk + // with empty data and end=true). + Data string `json:"data"` + // When true, this is the final body chunk for the response. The runtime treats the response + // body as complete after receiving an end-marked chunk. + End *bool `json:"end,omitempty"` + // Set to terminate the response with a transport-level failure. Implies end-of-stream; any + // further chunks for this requestId are ignored. + Error *LlmInferenceHTTPResponseChunkError `json:"error,omitempty"` + // Matches the requestId from the originating httpRequestStart frame. + RequestID string `json:"requestId"` +} + +// Whether the chunk was accepted. +// Experimental: LlmInferenceHTTPResponseChunkResult is part of an experimental API and may +// change or be removed. +type LlmInferenceHTTPResponseChunkResult struct { + // True when the chunk was matched to a pending request; false when unknown. + Accepted bool `json:"accepted"` +} + +// Response head. +// Experimental: LlmInferenceHTTPResponseStartRequest is part of an experimental API and may +// change or be removed. +type LlmInferenceHTTPResponseStartRequest struct { + Headers map[string][]string `json:"headers"` + // Matches the requestId from the originating httpRequestStart frame. + RequestID string `json:"requestId"` + // HTTP status code. + Status int64 `json:"status"` + // Optional HTTP status reason phrase. + StatusText *string `json:"statusText,omitempty"` +} + +// Whether the start frame was accepted. +// Experimental: LlmInferenceHTTPResponseStartResult is part of an experimental API and may +// change or be removed. +type LlmInferenceHTTPResponseStartResult struct { + // True when the response start was matched to a pending request; false when unknown. + Accepted bool `json:"accepted"` +} + +// Indicates whether the calling client was registered as the LLM inference provider. +// Experimental: LlmInferenceSetProviderResult is part of an experimental API and may change +// or be removed. +type LlmInferenceSetProviderResult struct { + // Whether the provider was set successfully + Success bool `json:"success"` +} + // Schema for the `LocalSessionMetadataValue` type. // Experimental: LocalSessionMetadataValue is part of an experimental API and may change or // be removed. @@ -2471,6 +2618,25 @@ type MCPListToolsResult struct { Tools []MCPTools `json:"tools"` } +// Pending MCP OAuth request ID and host-provided token or cancellation response. +// Experimental: MCPOauthHandlePendingRequest is part of an experimental API and may change +// or be removed. +type MCPOauthHandlePendingRequest struct { + // OAuth request identifier from the mcp.oauth_required event + RequestID string `json:"requestId"` + // Host response to the pending OAuth request. + Result MCPOauthPendingRequestResponse `json:"result"` +} + +// Indicates whether the pending MCP OAuth response was accepted. +// Experimental: MCPOauthHandlePendingResult is part of an experimental API and may change +// or be removed. +type MCPOauthHandlePendingResult struct { + // Whether the response was accepted. False if the request was unknown, timed out, or + // already resolved. + Success bool `json:"success"` +} + // Remote MCP server name and optional overrides controlling reauthentication, OAuth client // display name, and the callback success-page copy. // Experimental: MCPOauthLoginRequest is part of an experimental API and may change or be @@ -2507,6 +2673,48 @@ type MCPOauthLoginResult struct { AuthorizationURL *string `json:"authorizationUrl,omitempty"` } +// Host response to the pending OAuth request. +// Experimental: MCPOauthPendingRequestResponse is part of an experimental API and may +// change or be removed. +type MCPOauthPendingRequestResponse interface { + mcpOauthPendingRequestResponse() + Kind() MCPOauthPendingRequestResponseKind +} + +type RawMCPOauthPendingRequestResponseData struct { + Discriminator MCPOauthPendingRequestResponseKind + Raw json.RawMessage +} + +func (RawMCPOauthPendingRequestResponseData) mcpOauthPendingRequestResponse() {} +func (r RawMCPOauthPendingRequestResponseData) Kind() MCPOauthPendingRequestResponseKind { + return r.Discriminator +} + +type MCPOauthPendingRequestResponseCancelled struct { +} + +func (MCPOauthPendingRequestResponseCancelled) mcpOauthPendingRequestResponse() {} +func (MCPOauthPendingRequestResponseCancelled) Kind() MCPOauthPendingRequestResponseKind { + return MCPOauthPendingRequestResponseKindCancelled +} + +type MCPOauthPendingRequestResponseToken struct { + // Access token acquired by the SDK host + AccessToken string `json:"accessToken"` + // Token lifetime in seconds, if known. + ExpiresIn *int64 `json:"expiresIn,omitempty"` + // Refresh token supplied by the host, if available. + RefreshToken *string `json:"refreshToken,omitempty"` + // OAuth token type. Defaults to Bearer when omitted. + TokenType *string `json:"tokenType,omitempty"` +} + +func (MCPOauthPendingRequestResponseToken) mcpOauthPendingRequestResponse() {} +func (MCPOauthPendingRequestResponseToken) Kind() MCPOauthPendingRequestResponseKind { + return MCPOauthPendingRequestResponseKindToken +} + // MCP OAuth request id and optional provider response. // Experimental: MCPOauthRespondRequest is part of an experimental API and may change or be // removed. @@ -2974,28 +3182,42 @@ type ModelBilling struct { type ModelBillingTokenPrices struct { // Number of tokens per standard billing batch BatchSize *int64 `json:"batchSize,omitempty"` - // AI Credits cost per billing batch of cached tokens + // Deprecated: use cacheReadPrice. AI Credits cost per billing batch of cached tokens CachePrice *float64 `json:"cachePrice,omitempty"` - // Prompt token budget (max_prompt_tokens) for the default tier. The total context window is - // this value plus the model's max_output_tokens. + // AI Credits cost per billing batch of cached (read) tokens + CacheReadPrice *float64 `json:"cacheReadPrice,omitempty"` + // AI Credits cost per billing batch of cache-write (cache creation) tokens. + CacheWritePrice *float64 `json:"cacheWritePrice,omitempty"` + // Deprecated: use maxPromptTokens. Prompt token budget for the default tier. The total + // context window is this value plus the model's max_output_tokens. ContextMax *int64 `json:"contextMax,omitempty"` // AI Credits cost per billing batch of input tokens InputPrice *float64 `json:"inputPrice,omitempty"` // Long context tier pricing (available for models with extended context windows) LongContext *ModelBillingTokenPricesLongContext `json:"longContext,omitempty"` + // Prompt token budget for the default tier. The total context window is this value plus the + // model's max_output_tokens. + MaxPromptTokens *int64 `json:"maxPromptTokens,omitempty"` // AI Credits cost per billing batch of output tokens OutputPrice *float64 `json:"outputPrice,omitempty"` } // Long context tier pricing (available for models with extended context windows) type ModelBillingTokenPricesLongContext struct { - // AI Credits cost per billing batch of cached tokens + // Deprecated: use cacheReadPrice. AI Credits cost per billing batch of cached tokens CachePrice *float64 `json:"cachePrice,omitempty"` - // Prompt token budget (max_prompt_tokens) for the long context tier. The total context - // window is this value plus the model's max_output_tokens. + // AI Credits cost per billing batch of cached (read) tokens + CacheReadPrice *float64 `json:"cacheReadPrice,omitempty"` + // AI Credits cost per billing batch of cache-write (cache creation) tokens. + CacheWritePrice *float64 `json:"cacheWritePrice,omitempty"` + // Deprecated: use maxPromptTokens. Prompt token budget for the long context tier. The total + // context window is this value plus the model's max_output_tokens. ContextMax *int64 `json:"contextMax,omitempty"` // AI Credits cost per billing batch of input tokens InputPrice *float64 `json:"inputPrice,omitempty"` + // Prompt token budget for the long context tier. The total context window is this value + // plus the model's max_output_tokens. + MaxPromptTokens *int64 `json:"maxPromptTokens,omitempty"` // AI Credits cost per billing batch of output tokens OutputPrice *float64 `json:"outputPrice,omitempty"` } @@ -5291,7 +5513,7 @@ type SandboxConfig struct { AddCurrentWorkingDirectory *bool `json:"addCurrentWorkingDirectory,omitempty"` // Raw `ContainerConfig` (per `@microsoft/mxc-sdk`) passed directly to // `spawnSandboxFromConfig`, bypassing policy merging. - Config map[string]any `json:"config,omitzero"` + Config any `json:"config,omitempty"` // Whether sandboxing is enabled for the session. Enabled bool `json:"enabled"` // User-managed sandbox policy fragment merged into the auto-discovered base policy. @@ -6219,6 +6441,11 @@ type SessionOpenOptions struct { DisabledInstructionSources []string `json:"disabledInstructionSources,omitzero"` // Skill IDs disabled for this session. DisabledSkills []string `json:"disabledSkills,omitzero"` + // Experimental: enable native model citations (Anthropic models today), normalized onto the + // `assistant.message` event. Off by default; may change or be removed while the citations + // surface is experimental. + // Experimental: EnableCitations is part of an experimental API and may change or be removed. + EnableCitations *bool `json:"enableCitations,omitempty"` // Whether on-demand custom instruction discovery is enabled. EnableOnDemandInstructionDiscovery *bool `json:"enableOnDemandInstructionDiscovery,omitempty"` // Whether shell-script safety heuristics are enabled. @@ -9262,6 +9489,24 @@ const ( InstructionSourceTypeVscode InstructionSourceType = "vscode" ) +// Transport the runtime would otherwise use for this request. `http` (the default when +// absent) covers plain HTTP and SSE responses; `websocket` indicates a full-duplex message +// channel where each body chunk maps to one WebSocket message and the `binary` flag +// distinguishes text from binary frames. The SDK consumer uses this to decide whether to +// service the request with an HTTP client or a WebSocket client. It is the one piece of +// request metadata the consumer cannot reliably infer from the URL or headers alone. +type LlmInferenceHTTPRequestStartTransport string + +const ( + // Plain HTTP or SSE response. Each body chunk is an opaque byte range; the response is a + // status line, headers, and a (possibly streamed) body. + LlmInferenceHTTPRequestStartTransportHTTP LlmInferenceHTTPRequestStartTransport = "http" + // Full-duplex WebSocket channel. Each body chunk maps to exactly one WebSocket message and + // the `binary` flag distinguishes text from binary frames; request and response chunks flow + // concurrently. + LlmInferenceHTTPRequestStartTransportWebsocket LlmInferenceHTTPRequestStartTransport = "websocket" +) + // Allowed values for the `McpAppsHostContextDetailsAvailableDisplayMode` enumeration. // Experimental: MCPAppsHostContextDetailsAvailableDisplayMode is part of an experimental // API and may change or be removed. @@ -9370,6 +9615,14 @@ const ( MCPAppsSetHostContextDetailsThemeLight MCPAppsSetHostContextDetailsTheme = "light" ) +// Kind discriminator for MCPOauthPendingRequestResponse. +type MCPOauthPendingRequestResponseKind string + +const ( + MCPOauthPendingRequestResponseKindCancelled MCPOauthPendingRequestResponseKind = "cancelled" + MCPOauthPendingRequestResponseKindToken MCPOauthPendingRequestResponseKind = "token" +) + // Outcome of the sampling inference. 'success' produced a response; 'failure' encountered // an error (including agent-side rejection by content filter or criteria); 'cancelled' the // caller cancelled this execution via cancelSamplingExecution. @@ -9531,6 +9784,19 @@ const ( ModelPolicyStateUnconfigured ModelPolicyState = "unconfigured" ) +// Why the binary data is absent: it exceeded the inline size limit, or its asset was +// unavailable +// Experimental: OmittedBinaryOmittedReason is part of an experimental API and may change or +// be removed. +type OmittedBinaryOmittedReason string + +const ( + // The referenced binary asset could not be found (e.g. a truncated log). + OmittedBinaryOmittedReasonAssetUnavailable OmittedBinaryOmittedReason = "asset_unavailable" + // Bytes exceeded the session's inline size limit. + OmittedBinaryOmittedReasonTooLarge OmittedBinaryOmittedReason = "too_large" +) + // Allowed values for the `OptionsUpdateAdditionalContentExclusionPolicyScope` enumeration. // Experimental: OptionsUpdateAdditionalContentExclusionPolicyScope is part of an // experimental API and may change or be removed. @@ -10660,6 +10926,71 @@ func (a *ServerInstructionsAPI) GetDiscoveryPaths(ctx context.Context, params *I return &result, nil } +// Experimental: ServerLlmInferenceAPI contains experimental APIs that may change or be +// removed. +type ServerLlmInferenceAPI serverAPI + +// HttpResponseChunk delivers a body byte range (or a terminal transport error) for an +// in-flight response, correlated by requestId. Set `end` true on the last chunk. When +// `error` is set the response terminates with a transport-level failure and the runtime +// raises an APIConnectionError. +// +// RPC method: llmInference.httpResponseChunk. +// +// Parameters: A response body chunk or terminal error. +// +// Returns: Whether the chunk was accepted. +func (a *ServerLlmInferenceAPI) HttpResponseChunk(ctx context.Context, params *LlmInferenceHTTPResponseChunkRequest) (*LlmInferenceHTTPResponseChunkResult, error) { + raw, err := a.client.Request(ctx, "llmInference.httpResponseChunk", params) + if err != nil { + return nil, err + } + var result LlmInferenceHTTPResponseChunkResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// HttpResponseStart delivers the response head (status + headers) for an in-flight request, +// correlated by the requestId the runtime supplied in httpRequestStart. Must be called +// exactly once per request before any httpResponseChunk frames. +// +// RPC method: llmInference.httpResponseStart. +// +// Parameters: Response head. +// +// Returns: Whether the start frame was accepted. +func (a *ServerLlmInferenceAPI) HttpResponseStart(ctx context.Context, params *LlmInferenceHTTPResponseStartRequest) (*LlmInferenceHTTPResponseStartResult, error) { + raw, err := a.client.Request(ctx, "llmInference.httpResponseStart", params) + if err != nil { + return nil, err + } + var result LlmInferenceHTTPResponseStartResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// SetProvider registers an SDK client as the LLM inference callback provider. +// +// RPC method: llmInference.setProvider. +// +// Returns: Indicates whether the calling client was registered as the LLM inference +// provider. +func (a *ServerLlmInferenceAPI) SetProvider(ctx context.Context) (*LlmInferenceSetProviderResult, error) { + raw, err := a.client.Request(ctx, "llmInference.setProvider", nil) + if err != nil { + return nil, err + } + var result LlmInferenceSetProviderResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + type ServerMCPAPI serverAPI // Discovers MCP servers from user, workspace, plugin, and builtin sources. @@ -11726,6 +12057,7 @@ type ServerRPC struct { AgentRegistry *ServerAgentRegistryAPI Agents *ServerAgentsAPI Instructions *ServerInstructionsAPI + LlmInference *ServerLlmInferenceAPI MCP *ServerMCPAPI Models *ServerModelsAPI Plugins *ServerPluginsAPI @@ -11765,6 +12097,7 @@ func NewServerRPC(client *jsonrpc2.Client) *ServerRPC { r.AgentRegistry = (*ServerAgentRegistryAPI)(&r.common) r.Agents = (*ServerAgentsAPI)(&r.common) r.Instructions = (*ServerInstructionsAPI)(&r.common) + r.LlmInference = (*ServerLlmInferenceAPI)(&r.common) r.MCP = (*ServerMCPAPI)(&r.common) r.Models = (*ServerModelsAPI)(&r.common) r.Plugins = (*ServerPluginsAPI)(&r.common) @@ -13236,6 +13569,32 @@ func (s *MCPAPI) Apps() *MCPAppsAPI { // Experimental: MCPOauthAPI contains experimental APIs that may change or be removed. type MCPOauthAPI sessionAPI +// HandlePendingRequest resolves a pending MCP OAuth request with a host-provided token or +// cancellation. The pending request is emitted as mcp.oauth_required with the data +// necessary to authorize the request. +// +// RPC method: session.mcp.oauth.handlePendingRequest. +// +// Parameters: Pending MCP OAuth request ID and host-provided token or cancellation response. +// +// Returns: Indicates whether the pending MCP OAuth response was accepted. +func (a *MCPOauthAPI) HandlePendingRequest(ctx context.Context, params *MCPOauthHandlePendingRequest) (*MCPOauthHandlePendingResult, error) { + req := map[string]any{"sessionId": a.sessionID} + if params != nil { + req["requestId"] = params.RequestID + req["result"] = params.Result + } + raw, err := a.client.Request(ctx, "session.mcp.oauth.handlePendingRequest", req) + if err != nil { + return nil, err + } + var result MCPOauthHandlePendingResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // Login starts OAuth authentication for a remote MCP server. // // RPC method: session.mcp.oauth.login. @@ -16250,10 +16609,10 @@ func (a *InternalMCPAPI) UnregisterExternalClient(ctx context.Context, params *M // removed. type InternalMCPOauthAPI internalSessionAPI -// Responds to a pending MCP OAuth provider request. Marked internal because the `provider` -// argument is an in-process OAuthClientProvider instance that cannot be carried over the -// wire; the public OAuth surface will route the response through a wire-clean handshake -// once the CLI moves on top of the SDK. +// Responds to a pending MCP OAuth request with an in-process provider. This internal +// CLI-only API accepts a live OAuthClientProvider instance and cannot be used over the SDK +// JSON-RPC boundary. Use session.mcp.oauth.handlePendingRequest instead for the public +// SDK-safe response path. // // RPC method: session.mcp.oauth.respond. // diff --git a/go/rpc/zrpc_encoding.go b/go/rpc/zrpc_encoding.go index 7503e5d9f..12a5e0a45 100644 --- a/go/rpc/zrpc_encoding.go +++ b/go/rpc/zrpc_encoding.go @@ -1080,6 +1080,89 @@ func (r *MCPConfigUpdateRequest) UnmarshalJSON(data []byte) error { return nil } +func unmarshalMCPOauthPendingRequestResponse(data []byte) (MCPOauthPendingRequestResponse, error) { + if string(data) == "null" { + return nil, nil + } + type rawUnion struct { + Kind MCPOauthPendingRequestResponseKind `json:"kind"` + } + var raw rawUnion + if err := json.Unmarshal(data, &raw); err != nil { + return nil, err + } + + switch raw.Kind { + case MCPOauthPendingRequestResponseKindCancelled: + var d MCPOauthPendingRequestResponseCancelled + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil + case MCPOauthPendingRequestResponseKindToken: + var d MCPOauthPendingRequestResponseToken + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil + default: + return &RawMCPOauthPendingRequestResponseData{Discriminator: raw.Kind, Raw: data}, nil + } +} + +func (r RawMCPOauthPendingRequestResponseData) MarshalJSON() ([]byte, error) { + if r.Raw != nil { + return r.Raw, nil + } + return json.Marshal(struct { + Kind MCPOauthPendingRequestResponseKind `json:"kind"` + }{ + Kind: r.Discriminator, + }) +} + +func (r MCPOauthPendingRequestResponseCancelled) MarshalJSON() ([]byte, error) { + type alias MCPOauthPendingRequestResponseCancelled + return json.Marshal(struct { + Kind MCPOauthPendingRequestResponseKind `json:"kind"` + alias + }{ + Kind: r.Kind(), + alias: alias(r), + }) +} + +func (r MCPOauthPendingRequestResponseToken) MarshalJSON() ([]byte, error) { + type alias MCPOauthPendingRequestResponseToken + return json.Marshal(struct { + Kind MCPOauthPendingRequestResponseKind `json:"kind"` + alias + }{ + Kind: r.Kind(), + alias: alias(r), + }) +} + +func (r *MCPOauthHandlePendingRequest) UnmarshalJSON(data []byte) error { + type rawMCPOauthHandlePendingRequest struct { + RequestID string `json:"requestId"` + Result json.RawMessage `json:"result"` + } + var raw rawMCPOauthHandlePendingRequest + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + r.RequestID = raw.RequestID + if raw.Result != nil { + value, err := unmarshalMCPOauthPendingRequestResponse(raw.Result) + if err != nil { + return err + } + r.Result = value + } + return nil +} + func unmarshalPermissionDecision(data []byte) (PermissionDecision, error) { if string(data) == "null" { return nil, nil @@ -2692,6 +2775,7 @@ func (r *SessionOpenOptions) UnmarshalJSON(data []byte) error { DetachedFromSpawningParentSessionID *string `json:"detachedFromSpawningParentSessionId,omitempty"` DisabledInstructionSources []string `json:"disabledInstructionSources,omitzero"` DisabledSkills []string `json:"disabledSkills,omitzero"` + EnableCitations *bool `json:"enableCitations,omitempty"` EnableOnDemandInstructionDiscovery *bool `json:"enableOnDemandInstructionDiscovery,omitempty"` EnableScriptSafety *bool `json:"enableScriptSafety,omitempty"` EnableStreaming *bool `json:"enableStreaming,omitempty"` @@ -2756,6 +2840,7 @@ func (r *SessionOpenOptions) UnmarshalJSON(data []byte) error { r.DetachedFromSpawningParentSessionID = raw.DetachedFromSpawningParentSessionID r.DisabledInstructionSources = raw.DisabledInstructionSources r.DisabledSkills = raw.DisabledSkills + r.EnableCitations = raw.EnableCitations r.EnableOnDemandInstructionDiscovery = raw.EnableOnDemandInstructionDiscovery r.EnableScriptSafety = raw.EnableScriptSafety r.EnableStreaming = raw.EnableStreaming diff --git a/go/rpc/zsession_encoding.go b/go/rpc/zsession_encoding.go index 1cc987402..3d9741f18 100644 --- a/go/rpc/zsession_encoding.go +++ b/go/rpc/zsession_encoding.go @@ -655,6 +655,110 @@ func (r *UserMessageData) UnmarshalJSON(data []byte) error { return nil } +func unmarshalCitationLocation(data []byte) (CitationLocation, error) { + if string(data) == "null" { + return nil, nil + } + type rawUnion struct { + Type CitationLocationType `json:"type"` + } + var raw rawUnion + if err := json.Unmarshal(data, &raw); err != nil { + return nil, err + } + + switch raw.Type { + case CitationLocationTypeBlock: + var d CitationLocationBlock + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil + case CitationLocationTypeChar: + var d CitationLocationChar + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil + case CitationLocationTypePage: + var d CitationLocationPage + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil + default: + return &RawCitationLocation{Discriminator: raw.Type, Raw: data}, nil + } +} + +func (r RawCitationLocation) MarshalJSON() ([]byte, error) { + if r.Raw != nil { + return r.Raw, nil + } + return json.Marshal(struct { + Type CitationLocationType `json:"type"` + }{ + Type: r.Discriminator, + }) +} + +func (r CitationLocationBlock) MarshalJSON() ([]byte, error) { + type alias CitationLocationBlock + return json.Marshal(struct { + Type CitationLocationType `json:"type"` + alias + }{ + Type: r.Type(), + alias: alias(r), + }) +} + +func (r CitationLocationChar) MarshalJSON() ([]byte, error) { + type alias CitationLocationChar + return json.Marshal(struct { + Type CitationLocationType `json:"type"` + alias + }{ + Type: r.Type(), + alias: alias(r), + }) +} + +func (r CitationLocationPage) MarshalJSON() ([]byte, error) { + type alias CitationLocationPage + return json.Marshal(struct { + Type CitationLocationType `json:"type"` + alias + }{ + Type: r.Type(), + alias: alias(r), + }) +} + +func (r *CitationReference) UnmarshalJSON(data []byte) error { + type rawCitationReference struct { + CitedText *string `json:"citedText,omitempty"` + Location json.RawMessage `json:"location,omitempty"` + ProviderMetadata any `json:"providerMetadata,omitempty"` + SourceID string `json:"sourceId"` + } + var raw rawCitationReference + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + r.CitedText = raw.CitedText + if raw.Location != nil { + value, err := unmarshalCitationLocation(raw.Location) + if err != nil { + return err + } + r.Location = value + } + r.ProviderMetadata = raw.ProviderMetadata + r.SourceID = raw.SourceID + return nil +} + func matchesBinaryAssetReference(data []byte) bool { var rawGroup0 struct { AssetID json.RawMessage `json:"assetId"` @@ -693,15 +797,6 @@ func matchesOmittedBinaryResult(data []byte) bool { if rawGroup0.OmittedReason == nil { return false } - var rawGroup0String string - if err := json.Unmarshal(rawGroup0.OmittedReason, &rawGroup0String); err != nil { - return false - } - switch rawGroup0String { - case "asset_unavailable", "too_large": - default: - return false - } if rawGroup0.AssetID != nil { return false } @@ -1034,6 +1129,7 @@ func (r ToolExecutionCompleteContentText) MarshalJSON() ([]byte, error) { func (r *ToolExecutionCompleteResult) UnmarshalJSON(data []byte) error { type rawToolExecutionCompleteResult struct { BinaryResultsForLlm []json.RawMessage `json:"binaryResultsForLlm,omitzero"` + CitableSources []CitableSource `json:"citableSources,omitzero"` Content string `json:"content"` Contents []json.RawMessage `json:"contents,omitzero"` DetailedContent *string `json:"detailedContent,omitempty"` @@ -1054,6 +1150,7 @@ func (r *ToolExecutionCompleteResult) UnmarshalJSON(data []byte) error { r.BinaryResultsForLlm = append(r.BinaryResultsForLlm, value) } } + r.CitableSources = raw.CitableSources r.Content = raw.Content if raw.Contents != nil { r.Contents = make([]ToolExecutionCompleteContent, 0, len(raw.Contents)) @@ -1909,124 +2006,6 @@ func (r *PermissionCompletedData) UnmarshalJSON(data []byte) error { return nil } -func unmarshalElicitationCompletedContent(data []byte) (ElicitationCompletedContent, error) { - if string(data) == "null" { - return nil, nil - } - { - var value string - if err := json.Unmarshal(data, &value); err == nil { - return ElicitationCompletedStringContent(value), nil - } - } - { - var value float64 - if err := json.Unmarshal(data, &value); err == nil { - return ElicitationCompletedNumberContent(value), nil - } - } - { - var value bool - if err := json.Unmarshal(data, &value); err == nil { - return ElicitationCompletedBooleanContent(value), nil - } - } - { - var value []string - if err := json.Unmarshal(data, &value); err == nil { - return ElicitationCompletedStringArrayContent(value), nil - } - } - return nil, errors.New("data did not match any union variant for ElicitationCompletedContent") -} - -func (r *ElicitationCompletedData) UnmarshalJSON(data []byte) error { - type rawElicitationCompletedData struct { - Action *ElicitationCompletedAction `json:"action,omitempty"` - Content map[string]json.RawMessage `json:"content,omitzero"` - RequestID string `json:"requestId"` - } - var raw rawElicitationCompletedData - if err := json.Unmarshal(data, &raw); err != nil { - return err - } - r.Action = raw.Action - if raw.Content != nil { - r.Content = make(map[string]ElicitationCompletedContent, len(raw.Content)) - for key, rawValue := range raw.Content { - value, err := unmarshalElicitationCompletedContent(rawValue) - if err != nil { - return err - } - r.Content[key] = value - } - } - r.RequestID = raw.RequestID - return nil -} - -func (r CustomNotificationPayload) MarshalJSON() ([]byte, error) { - if r.AnyArray != nil { - return json.Marshal(r.AnyArray) - } - if r.AnyMap != nil { - return json.Marshal(r.AnyMap) - } - if r.Bool != nil { - return json.Marshal(r.Bool) - } - if r.Double != nil { - return json.Marshal(r.Double) - } - if r.String != nil { - return json.Marshal(r.String) - } - return []byte("null"), nil -} - -func (r *CustomNotificationPayload) UnmarshalJSON(data []byte) error { - if string(data) == "null" { - *r = CustomNotificationPayload{} - return nil - } - { - var value []any - if err := json.Unmarshal(data, &value); err == nil { - *r = CustomNotificationPayload{AnyArray: value} - return nil - } - } - { - var value map[string]any - if err := json.Unmarshal(data, &value); err == nil { - *r = CustomNotificationPayload{AnyMap: value} - return nil - } - } - { - var value bool - if err := json.Unmarshal(data, &value); err == nil { - *r = CustomNotificationPayload{Bool: &value} - return nil - } - } - { - var value float64 - if err := json.Unmarshal(data, &value); err == nil { - *r = CustomNotificationPayload{Double: &value} - return nil - } - } - { - var value string - if err := json.Unmarshal(data, &value); err == nil { - *r = CustomNotificationPayload{String: &value} - return nil - } - } - return errors.New("data did not match any union variant for CustomNotificationPayload") -} - func (r *SessionExtensionsAttachmentsPushedData) UnmarshalJSON(data []byte) error { type rawSessionExtensionsAttachmentsPushedData struct { Attachments []json.RawMessage `json:"attachments"` diff --git a/go/rpc/zsession_events.go b/go/rpc/zsession_events.go index 8a9d20d37..2144faed0 100644 --- a/go/rpc/zsession_events.go +++ b/go/rpc/zsession_events.go @@ -183,6 +183,9 @@ func (*AssistantReasoningData) Type() SessionEventType { return SessionEventType type AssistantMessageData struct { // Provider's completion / response identifier; shared across all chunks of a single API call. Used to group multi-chunk assistant utterances. APICallID *string `json:"apiCallId,omitempty"` + // Provider-agnostic citations linking spans of this message's content to the sources that support them. Experimental; only populated when citation emission is enabled. + // Experimental: Citations is part of an experimental API and may change or be removed. + Citations *Citations `json:"citations,omitempty"` // The assistant's text response content Content string `json:"content"` // Encrypted reasoning content from OpenAI models. Session-bound and stripped on resume. @@ -403,7 +406,7 @@ 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,omitzero"` + Content map[string]any `json:"content,omitzero"` // Request ID of the resolved elicitation request; clients should dismiss any UI for this request RequestID string `json:"requestId"` } @@ -524,10 +527,16 @@ func (*ExternalToolRequestedData) Type() SessionEventType { type ModelCallFailureData struct { // Completion ID from the model provider (e.g., chatcmpl-abc123) APICallID *string `json:"apiCallId,omitempty"` + // For HTTP 400 failures only: whether the response carried a structured CAPI error envelope (structured_error, a deterministic validation failure) or no error body (bodyless, the transient gateway/proxy signature). Absent for non-400 failures. + BadRequestKind *ModelCallFailureBadRequestKind `json:"badRequestKind,omitempty"` // Duration of the failed API call in milliseconds DurationMs *int64 `json:"durationMs,omitempty"` + // For HTTP 400 failures only: the `code` from the CAPI error envelope (e.g. 'model_max_prompt_tokens_exceeded') identifying which deterministic validation failure occurred. Raw server-controlled string, emitted only through restricted telemetry. Absent for bodyless or non-400 failures. + ErrorCode *string `json:"errorCode,omitempty"` // Raw provider/runtime error message for restricted telemetry ErrorMessage *string `json:"errorMessage,omitempty"` + // For HTTP 400 failures only: the `type` from the CAPI error envelope (e.g. 'websocket_error'), a coarser companion to errorCode for envelopes that carry no code. Raw server-controlled string, emitted only through restricted telemetry. Absent for bodyless or non-400 failures. + ErrorType *string `json:"errorType,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 @@ -669,6 +678,8 @@ func (*MCPAppToolCallCompleteData) Type() SessionEventType { // MCP OAuth request completion notification type MCPOauthCompletedData struct { + // How the pending OAuth request was completed + Outcome MCPOauthCompletionOutcome `json:"outcome"` // Request ID of the resolved OAuth request RequestID string `json:"requestId"` } @@ -712,14 +723,18 @@ func (*SessionRemoteSteerableChangedData) Type() SessionEventType { // OAuth authentication request for an MCP server type MCPOauthRequiredData struct { - // Unique identifier for this OAuth request; used to respond via session.respondToMcpOAuth() + // Unique identifier for this OAuth request; used to respond via session.mcp.oauth.handlePendingRequest RequestID string `json:"requestId"` + // Raw OAuth protected-resource metadata document fetched for the MCP server, if available + ResourceMetadata *string `json:"resourceMetadata,omitempty"` // 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"` + // OAuth WWW-Authenticate parameters parsed from the auth challenge, if available + WwwAuthenticateParams *MCPOauthWwwAuthenticateParams `json:"wwwAuthenticateParams,omitempty"` } func (*MCPOauthRequiredData) sessionEventData() {} @@ -730,7 +745,7 @@ type SessionCustomNotificationData struct { // Source-defined custom notification name Name string `json:"name"` // Source-defined JSON payload for the custom notification - Payload CustomNotificationPayload `json:"payload"` + Payload any `json:"payload"` // Namespace for the custom notification producer Source string `json:"source"` // Optional source-defined string identifiers describing the payload subject @@ -1755,7 +1770,7 @@ type CanvasRegistryChangedCanvas struct { // Owning extension display name, when available ExtensionName *string `json:"extensionName,omitempty"` // JSON Schema for canvas open input - InputSchema map[string]any `json:"inputSchema,omitzero"` + InputSchema any `json:"inputSchema,omitempty"` } // Schema for the `CanvasRegistryChangedCanvasAction` type. @@ -1763,7 +1778,7 @@ type CanvasRegistryChangedCanvasAction struct { // Action description Description *string `json:"description,omitempty"` // JSON Schema for action input - InputSchema map[string]any `json:"inputSchema,omitzero"` + InputSchema any `json:"inputSchema,omitempty"` // Action name Name string `json:"name"` } @@ -1778,6 +1793,125 @@ type CapabilitiesChangedUI struct { MCPApps *bool `json:"mcpApps,omitempty"` } +// A source supplied by a tool that should be made available to the model as citable content. +// Experimental: CitableSource is part of an experimental API and may change or be removed. +type CitableSource struct { + // The source text made available to the model as citable content. + Content string `json:"content"` + // Stable identifier for this source within the tool result. Used for deduplication and may be used by future provider integrations to correlate response citations back to the originating source. + ID string `json:"id"` + // File path relative to the agent's workspace root, when the source is a file. + Path *string `json:"path,omitempty"` + // Human-readable title of the source. + Title *string `json:"title,omitempty"` + // URL of the source, when it is a web resource. + URL *string `json:"url,omitempty"` +} + +// Location within a cited source (character, page, or content-block range) that supports a span. +// Experimental: CitationLocation is part of an experimental API and may change or be removed. +type CitationLocation interface { + citationLocation() + Type() CitationLocationType +} + +type RawCitationLocation struct { + Discriminator CitationLocationType + Raw json.RawMessage +} + +func (RawCitationLocation) citationLocation() {} +func (r RawCitationLocation) Type() CitationLocationType { + return r.Discriminator +} + +// A content-block range within a structured source document. +type CitationLocationBlock struct { + // Index of the last content block of the cited range (zero-based, exclusive). + EndBlock int64 `json:"endBlock"` + // Index of the first content block of the cited range (zero-based, inclusive). + StartBlock int64 `json:"startBlock"` +} + +func (CitationLocationBlock) citationLocation() {} +func (CitationLocationBlock) Type() CitationLocationType { + return CitationLocationTypeBlock +} + +// A character range within the source's text content. +type CitationLocationChar struct { + // End character offset within the source text (zero-based, exclusive). + EndIndex int64 `json:"endIndex"` + // Start character offset within the source text (zero-based, inclusive). + StartIndex int64 `json:"startIndex"` +} + +func (CitationLocationChar) citationLocation() {} +func (CitationLocationChar) Type() CitationLocationType { + return CitationLocationTypeChar +} + +// A page range within a paginated source document. +type CitationLocationPage struct { + // Last page number of the cited range (inclusive). + EndPage int64 `json:"endPage"` + // First page number of the cited range. + StartPage int64 `json:"startPage"` +} + +func (CitationLocationPage) citationLocation() {} +func (CitationLocationPage) Type() CitationLocationType { + return CitationLocationTypePage +} + +// A single citation occurrence linking a span of generated text to a supporting source. +// Experimental: CitationReference is part of an experimental API and may change or be removed. +type CitationReference struct { + // The exact text from the source that supports the cited span, when provided by the model. + CitedText *string `json:"citedText,omitempty"` + // Location within the source that supports the cited span, when the provider reports one. + Location CitationLocation `json:"location,omitempty"` + // Provider-native citation correlation data (e.g. Anthropic search_result_index / document_index), passed through opaquely for debugging and forward compatibility. + ProviderMetadata any `json:"providerMetadata,omitempty"` + // Identifier of the CitationSource this reference points to (CitationSource.id). + SourceID string `json:"sourceId"` +} + +// Provider-agnostic citations linking spans of the assistant's response to their supporting sources. +// Experimental: Citations is part of an experimental API and may change or be removed. +type Citations struct { + // Deduplicated set of sources referenced by the citation spans. + Sources []CitationSource `json:"sources"` + // Spans of generated text annotated with the sources that support them. + Spans []CitationSpan `json:"spans"` +} + +// A source that backs one or more cited spans in the assistant's response. +// Experimental: CitationSource is part of an experimental API and may change or be removed. +type CitationSource struct { + // Stable, turn-scoped identifier for this source, referenced by CitationReference.sourceId. + ID string `json:"id"` + // File path relative to the agent's workspace root, when the source is a file. + Path *string `json:"path,omitempty"` + // The system that produced this citation. + Provider CitationProvider `json:"provider"` + // Human-readable title of the source. + Title *string `json:"title,omitempty"` + // URL of the source, when it is a web resource. + URL *string `json:"url,omitempty"` +} + +// A contiguous span of generated assistant text and the source references that support it. +// Experimental: CitationSpan is part of an experimental API and may change or be removed. +type CitationSpan struct { + // End offset of the cited span within the final assistant message content (UTF-16 code units, zero-based, exclusive). + EndIndex int64 `json:"endIndex"` + // The sources that support this span of generated text. + References []CitationReference `json:"references"` + // Start offset of the cited span within the final assistant message content (UTF-16 code units, zero-based, inclusive). + StartIndex int64 `json:"startIndex"` +} + // Schema for the `CommandsChangedCommand` type. type CommandsChangedCommand struct { // Optional human-readable command description. @@ -1847,36 +1981,6 @@ type CustomAgentsUpdatedAgent struct { UserInvocable bool `json:"userInvocable"` } -// Source-defined JSON payload for the custom notification -type CustomNotificationPayload struct { - AnyArray []any - AnyMap map[string]any - Bool *bool - Double *float64 - String *string -} - -// Schema for the `ElicitationCompletedContent` type. -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 @@ -1949,6 +2053,16 @@ type MCPOauthRequiredStaticClientConfig struct { PublicClient *bool `json:"publicClient,omitempty"` } +// OAuth WWW-Authenticate parameters parsed from an MCP auth challenge +type MCPOauthWwwAuthenticateParams struct { + // OAuth error from the WWW-Authenticate error parameter, if present + Error *string `json:"error,omitempty"` + // Protected resource metadata URL from the WWW-Authenticate resource_metadata parameter + ResourceMetadataURL string `json:"resourceMetadataUrl"` + // Requested OAuth scopes from the WWW-Authenticate scope parameter, if present + Scope *string `json:"scope,omitempty"` +} + // Schema for the `McpServersLoadedServer` type. type MCPServersLoadedServer struct { // Error message if the server failed to connect @@ -2071,7 +2185,7 @@ func (PermissionPromptRequestHook) Kind() PermissionPromptRequestKind { // MCP tool invocation permission prompt type PermissionPromptRequestMCP struct { // Arguments to pass to the MCP tool - Args *any `json:"args,omitempty"` + 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 @@ -2923,6 +3037,9 @@ type ToolExecutionCompleteResult struct { // Model-facing binary results (base64 inline or size-omitted markers) sent to the LLM for this tool call // Experimental: BinaryResultsForLlm is part of an experimental API and may change or be removed. BinaryResultsForLlm []PersistedBinaryResult `json:"binaryResultsForLlm,omitzero"` + // Provider-neutral source material this tool makes available to the model as citable content. Persisted so it survives session resume. Experimental. + // Experimental: CitableSources is part of an experimental API and may change or be removed. + CitableSources []CitableSource `json:"citableSources,omitzero"` // 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 @@ -3161,6 +3278,29 @@ const ( CanvasOpenedAvailabilityStale CanvasOpenedAvailability = "stale" ) +// Type discriminator for CitationLocation. +// Experimental: CitationLocationType is part of an experimental API and may change or be removed. +type CitationLocationType string + +const ( + CitationLocationTypeBlock CitationLocationType = "block" + CitationLocationTypeChar CitationLocationType = "char" + CitationLocationTypePage CitationLocationType = "page" +) + +// The system that produced a citation. +// Experimental: CitationProvider is part of an experimental API and may change or be removed. +type CitationProvider string + +const ( + // Citation produced by an Anthropic (Claude) model response. + CitationProviderAnthropic CitationProvider = "anthropic" + // Citation synthesized client-side by the runtime from tool output. + CitationProviderClient CitationProvider = "client" + // Citation produced by an OpenAI model response. + CitationProviderOpenai CitationProvider = "openai" +) + // The user action: "accept" (submitted form), "decline" (explicitly refused), or "cancel" (dismissed) type ElicitationCompletedAction string @@ -3242,6 +3382,16 @@ const ( HandoffSourceTypeRemote HandoffSourceType = "remote" ) +// How the pending MCP OAuth request was completed +type MCPOauthCompletionOutcome string + +const ( + // The request completed without an OAuth provider. + MCPOauthCompletionOutcomeCancelled MCPOauthCompletionOutcome = "cancelled" + // The request completed with a token-backed OAuth provider. + MCPOauthCompletionOutcomeToken MCPOauthCompletionOutcome = "token" +) + // 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 @@ -3263,6 +3413,16 @@ const ( MCPServerTransportStdio MCPServerTransport = "stdio" ) +// For HTTP 400 failures only: whether the response carried a structured CAPI error envelope (structured_error, a deterministic validation failure) or no error body (bodyless, the transient gateway/proxy signature). Absent for non-400 failures. +type ModelCallFailureBadRequestKind string + +const ( + // The 400 response carried no error body (transient gateway/proxy signature). + ModelCallFailureBadRequestKindBodyless ModelCallFailureBadRequestKind = "bodyless" + // The 400 response carried a structured CAPI error envelope (deterministic validation failure). + ModelCallFailureBadRequestKindStructuredError ModelCallFailureBadRequestKind = "structured_error" +) + // Where the failed model call originated type ModelCallFailureSource string @@ -3275,16 +3435,6 @@ const ( ModelCallFailureSourceTopLevel ModelCallFailureSource = "top_level" ) -// Why the binary data is absent: it exceeded the inline size limit, or its asset was unavailable -type OmittedBinaryOmittedReason string - -const ( - // The referenced binary asset could not be found (e.g. a truncated log). - OmittedBinaryOmittedReasonAssetUnavailable OmittedBinaryOmittedReason = "asset_unavailable" - // Bytes exceeded the session's inline size limit. - OmittedBinaryOmittedReasonTooLarge OmittedBinaryOmittedReason = "too_large" -) - // Binary result type discriminator. Use "image" for images and "resource" for other binary data. type OmittedBinaryType string diff --git a/go/test.sh b/go/test.sh index 15fc35c30..dfb7bac1d 100755 --- a/go/test.sh +++ b/go/test.sh @@ -15,10 +15,12 @@ fi # Determine COPILOT_CLI_PATH if [ -z "$COPILOT_CLI_PATH" ]; then - # Try to find it relative to the SDK + # Try to find it relative to the SDK. As of CLI 1.0.64-1 the @github/copilot + # package is a thin loader; the runnable index.js ships in the installed + # platform package (e.g. @github/copilot-linux-x64). Exactly one is installed. SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" - POTENTIAL_PATH="$SCRIPT_DIR/../nodejs/node_modules/@github/copilot/index.js" - if [ -f "$POTENTIAL_PATH" ]; then + POTENTIAL_PATH="$(ls "$SCRIPT_DIR"/../nodejs/node_modules/@github/copilot-*/index.js 2>/dev/null | head -n1)" + if [ -n "$POTENTIAL_PATH" ] && [ -f "$POTENTIAL_PATH" ]; then export COPILOT_CLI_PATH="$POTENTIAL_PATH" echo "📍 Auto-detected CLI path: $COPILOT_CLI_PATH" else diff --git a/go/zsession_events.go b/go/zsession_events.go index 944a84b91..5d76ec8e7 100644 --- a/go/zsession_events.go +++ b/go/zsession_events.go @@ -51,6 +51,17 @@ type ( CanvasRegistryChangedCanvasAction = rpc.CanvasRegistryChangedCanvasAction CapabilitiesChangedData = rpc.CapabilitiesChangedData CapabilitiesChangedUI = rpc.CapabilitiesChangedUI + CitableSource = rpc.CitableSource + CitationLocation = rpc.CitationLocation + CitationLocationBlock = rpc.CitationLocationBlock + CitationLocationChar = rpc.CitationLocationChar + CitationLocationPage = rpc.CitationLocationPage + CitationLocationType = rpc.CitationLocationType + CitationProvider = rpc.CitationProvider + CitationReference = rpc.CitationReference + Citations = rpc.Citations + CitationSource = rpc.CitationSource + CitationSpan = rpc.CitationSpan CommandCompletedData = rpc.CommandCompletedData CommandExecuteData = rpc.CommandExecuteData CommandQueuedData = rpc.CommandQueuedData @@ -60,14 +71,8 @@ type ( CompactionCompleteCompactionTokensUsedCopilotUsageTokenDetail = rpc.CompactionCompleteCompactionTokensUsedCopilotUsageTokenDetail ContextTier = rpc.ContextTier CustomAgentsUpdatedAgent = rpc.CustomAgentsUpdatedAgent - CustomNotificationPayload = rpc.CustomNotificationPayload 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 @@ -93,13 +98,16 @@ type ( MCPAppToolCallCompleteToolMeta = rpc.MCPAppToolCallCompleteToolMeta MCPAppToolCallCompleteToolMetaUI = rpc.MCPAppToolCallCompleteToolMetaUI MCPOauthCompletedData = rpc.MCPOauthCompletedData + MCPOauthCompletionOutcome = rpc.MCPOauthCompletionOutcome MCPOauthRequiredData = rpc.MCPOauthRequiredData MCPOauthRequiredStaticClientConfig = rpc.MCPOauthRequiredStaticClientConfig MCPOauthRequiredStaticClientConfigGrantType = rpc.MCPOauthRequiredStaticClientConfigGrantType + MCPOauthWwwAuthenticateParams = rpc.MCPOauthWwwAuthenticateParams MCPServersLoadedServer = rpc.MCPServersLoadedServer MCPServerSource = rpc.MCPServerSource MCPServerStatus = rpc.MCPServerStatus MCPServerTransport = rpc.MCPServerTransport + ModelCallFailureBadRequestKind = rpc.ModelCallFailureBadRequestKind ModelCallFailureData = rpc.ModelCallFailureData ModelCallFailureSource = rpc.ModelCallFailureSource OmittedBinaryOmittedReason = rpc.OmittedBinaryOmittedReason @@ -157,6 +165,7 @@ type ( PersistedBinaryResultType = rpc.PersistedBinaryResultType PlanChangedOperation = rpc.PlanChangedOperation PossibleURL = rpc.PossibleURL + RawCitationLocation = rpc.RawCitationLocation RawPermissionPromptRequest = rpc.RawPermissionPromptRequest RawPermissionRequest = rpc.RawPermissionRequest RawPermissionResult = rpc.RawPermissionResult @@ -329,6 +338,12 @@ const ( BinaryAssetTypeResource = rpc.BinaryAssetTypeResource CanvasOpenedAvailabilityReady = rpc.CanvasOpenedAvailabilityReady CanvasOpenedAvailabilityStale = rpc.CanvasOpenedAvailabilityStale + CitationLocationTypeBlock = rpc.CitationLocationTypeBlock + CitationLocationTypeChar = rpc.CitationLocationTypeChar + CitationLocationTypePage = rpc.CitationLocationTypePage + CitationProviderAnthropic = rpc.CitationProviderAnthropic + CitationProviderClient = rpc.CitationProviderClient + CitationProviderOpenai = rpc.CitationProviderOpenai ContextTierDefault = rpc.ContextTierDefault ContextTierLongContext = rpc.ContextTierLongContext ElicitationCompletedActionAccept = rpc.ElicitationCompletedActionAccept @@ -351,6 +366,8 @@ const ( ExtensionsLoadedExtensionStatusStarting = rpc.ExtensionsLoadedExtensionStatusStarting HandoffSourceTypeLocal = rpc.HandoffSourceTypeLocal HandoffSourceTypeRemote = rpc.HandoffSourceTypeRemote + MCPOauthCompletionOutcomeCancelled = rpc.MCPOauthCompletionOutcomeCancelled + MCPOauthCompletionOutcomeToken = rpc.MCPOauthCompletionOutcomeToken MCPOauthRequiredStaticClientConfigGrantTypeClientCredentials = rpc.MCPOauthRequiredStaticClientConfigGrantTypeClientCredentials MCPServerSourceBuiltin = rpc.MCPServerSourceBuiltin MCPServerSourcePlugin = rpc.MCPServerSourcePlugin @@ -366,6 +383,8 @@ const ( MCPServerTransportMemory = rpc.MCPServerTransportMemory MCPServerTransportSSE = rpc.MCPServerTransportSSE MCPServerTransportStdio = rpc.MCPServerTransportStdio + ModelCallFailureBadRequestKindBodyless = rpc.ModelCallFailureBadRequestKindBodyless + ModelCallFailureBadRequestKindStructuredError = rpc.ModelCallFailureBadRequestKindStructuredError ModelCallFailureSourceMCPSampling = rpc.ModelCallFailureSourceMCPSampling ModelCallFailureSourceSubagent = rpc.ModelCallFailureSourceSubagent ModelCallFailureSourceTopLevel = rpc.ModelCallFailureSourceTopLevel diff --git a/java/pom.xml b/java/pom.xml index 65257ffca..ac7b719b5 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -60,9 +60,9 @@ environment variable, so `mvn verify` is self-contained and the developer never has to set it manually. Override on the command line to point at a different CLI build, e.g.: - mvn verify -Dcopilot.cli.path=/some/other/copilot/index.js + mvn verify -Dcopilot.cli.path=/some/other/copilot/npm-loader.js --> - ${copilot.sdk.root}/nodejs/node_modules/@github/copilot/index.js + ${copilot.sdk.root}/nodejs/node_modules/@github/copilot/npm-loader.js false - ^1.0.64-0 + ^1.0.64-1 diff --git a/java/scripts/codegen/package-lock.json b/java/scripts/codegen/package-lock.json index d64a507c5..0bce4a4fc 100644 --- a/java/scripts/codegen/package-lock.json +++ b/java/scripts/codegen/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "copilot-sdk-java-codegen", "dependencies": { - "@github/copilot": "^1.0.64-0", + "@github/copilot": "^1.0.64-1", "json-schema": "^0.4.0", "tsx": "^4.22.4" } @@ -428,32 +428,31 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.64-0", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.64-0.tgz", - "integrity": "sha512-PlH7ByBHjmPLqLXS4CE2q8hN6CFEfkCMV6ScBEzW/u73+KYQB4fGNouo8Lr8okL6D5CW5rzPJbsXyISyJqVOZg==", + "version": "1.0.64-1", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.64-1.tgz", + "integrity": "sha512-lojV4Cb7oT4VJnYPEKBRH8KI3W43Q4Lh0Pc+V6sej+xjPJkoqwm68sNKn73/p3wXPBSTVTzPeCm9WhIisgf1Jw==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { - "detect-libc": "^2.1.2", - "os-theme": "^0.0.8" + "detect-libc": "^2.1.2" }, "bin": { "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.64-0", - "@github/copilot-darwin-x64": "1.0.64-0", - "@github/copilot-linux-arm64": "1.0.64-0", - "@github/copilot-linux-x64": "1.0.64-0", - "@github/copilot-linuxmusl-arm64": "1.0.64-0", - "@github/copilot-linuxmusl-x64": "1.0.64-0", - "@github/copilot-win32-arm64": "1.0.64-0", - "@github/copilot-win32-x64": "1.0.64-0" + "@github/copilot-darwin-arm64": "1.0.64-1", + "@github/copilot-darwin-x64": "1.0.64-1", + "@github/copilot-linux-arm64": "1.0.64-1", + "@github/copilot-linux-x64": "1.0.64-1", + "@github/copilot-linuxmusl-arm64": "1.0.64-1", + "@github/copilot-linuxmusl-x64": "1.0.64-1", + "@github/copilot-win32-arm64": "1.0.64-1", + "@github/copilot-win32-x64": "1.0.64-1" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.64-0", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.64-0.tgz", - "integrity": "sha512-97DUGiuYrkCYOlSSLWMmr+K0uGzAxz1JOL/GyO/7mNl6V/1xgs6Van1Jj+Dpj4ly96iHE8lUIW8cQNCG66644g==", + "version": "1.0.64-1", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.64-1.tgz", + "integrity": "sha512-MQHZT9LhmCiq+ogO1E8cPCWrurZ6x+r9lPJfYUSnOyMO+EHbREpiJwOOChxtLHgL2/tKJSZdId2pg3tDgUlcsw==", "cpu": [ "arm64" ], @@ -467,9 +466,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.64-0", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.64-0.tgz", - "integrity": "sha512-2PXY4mSFtIjFdRaAt8PakegRgGtf6Sz9z6U/dIgVygNfctVNzaL5FH65PNPm8Y80jaDvEcz1/XY5MiQtxnlzZQ==", + "version": "1.0.64-1", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.64-1.tgz", + "integrity": "sha512-kOQY7CvI7He0eO3ObQAHePWdkNLWAOegCSzUqUmdcpa1SNVqbZ3GBMsQ7uAZQip2cQxnGZ7pS1v6tKQ0HJdkYw==", "cpu": [ "x64" ], @@ -483,9 +482,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.64-0", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.64-0.tgz", - "integrity": "sha512-PLP+vR508fOTlCr9CSZiXi9geicHKXuX9jLGdwNqK2TMZO5TqCLz8wP7dBEmkdkeXcFKovMb8nQVB1Toc6xutw==", + "version": "1.0.64-1", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.64-1.tgz", + "integrity": "sha512-hIfuO7Q+pWs0SKfIRYqT+CjMaupudnhp4RMS6XoJ5s/e33rvpj2tkTkXYlHJo1PMDI823vvbqgpEdr+KeewMwg==", "cpu": [ "arm64" ], @@ -499,9 +498,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.64-0", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.64-0.tgz", - "integrity": "sha512-NvVjQ69zr390ijzo2f75+v0DHm6xnvPbi67ugnKDk7ZPbx8P3vSxVdAnrzrrL4T3T8ng3pJANcC4p+eGbx+UDw==", + "version": "1.0.64-1", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.64-1.tgz", + "integrity": "sha512-VHaE62pha0rDDvuNN3bd97gf0EZ+EJebstM1ejHsMYoPT1IOUkYEXlNfGGHY+GfUGYxAiy/+Uew4xw5mJyy/Sw==", "cpu": [ "x64" ], @@ -515,9 +514,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.64-0", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.64-0.tgz", - "integrity": "sha512-qCnVF5vIcTO74CukAENZo8e5nqXm4QUshuKN69aiZb5GOhVvyyIKsf5Jo7ikZt54jJBHycAMUKlTA8L3/nK+KA==", + "version": "1.0.64-1", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.64-1.tgz", + "integrity": "sha512-L/YrZPotRujAP0QERq+DlkR1SLr7abbTSz/56JqKKOqEdjKZPdQW1bUlhL/w1CZg1gXlTNUsNVyKz/fUfrEBgw==", "cpu": [ "arm64" ], @@ -531,9 +530,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.64-0", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.64-0.tgz", - "integrity": "sha512-WDBEmkBk1RulTfdLK5IuttNBadjLOBpvQyonGQ/aLeaetRNNdapoygrSjFU7q1QBSenmCyanXH6D+TS7tP3Qsw==", + "version": "1.0.64-1", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.64-1.tgz", + "integrity": "sha512-AGMjXqR128oyjiJhoI6Gd7JP5ddWkib+P4YH/JoHm05iNn23ZYl4tSc0XihHzeyMI1ix7Aacn8UINYB7lGOGOA==", "cpu": [ "x64" ], @@ -547,9 +546,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.64-0", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.64-0.tgz", - "integrity": "sha512-PC7yuUKcVbhli4bpzWFVT3juxj+v/iONazetNe3tMpHWza3W7MeFRifzAseSErKQCt2fHJth3m8bQAwFN2jfrA==", + "version": "1.0.64-1", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.64-1.tgz", + "integrity": "sha512-vvv+gnemi9WKaxF41zz7Xmq6a493n8Yjps5UFaOY6a3WR222kKXZXfOpeRvIYsDgnIPHGBHIj1TBOmnHQT4V4w==", "cpu": [ "arm64" ], @@ -563,9 +562,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.64-0", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.64-0.tgz", - "integrity": "sha512-d2fnUTIlqNxCqS2PuV+FD99ZOYBaX72OLtAmphbKyz36KyZ6D4ssiu8M4vHVTKWWdyc3TWiLsnIB+ryWdv1gGw==", + "version": "1.0.64-1", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.64-1.tgz", + "integrity": "sha512-mcHvD0fjGDuqr/YXzy8mKuDmah1F+qjPujxoFuGmabmTJZ33cSIJ3nq7RRvxZNIdp8YJ57NkbcW30WvIcOeJ3w==", "cpu": [ "x64" ], @@ -578,45 +577,6 @@ "copilot-win32-x64": "copilot.exe" } }, - "node_modules/@os-theme/darwin-arm64": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/@os-theme/darwin-arm64/-/darwin-arm64-0.0.8.tgz", - "integrity": "sha512-gMsOs+8Ju396a5yyMWigkbA0dMTxD78U3HzG3mlpiAyn6hfd5dbyI4VGP+sfTB82KGgWLzIhWWTFX5UYY6iX0A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@os-theme/linux-x64": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/@os-theme/linux-x64/-/linux-x64-0.0.8.tgz", - "integrity": "sha512-zvjmBUiSQPjM1RbhpsfCDYMJxW4eLlGmkFPnpteC/03X2lz6CjiX2hfbN2EWLxXjNnIje3Jqaen8IsqEnWrRBg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@os-theme/win32-x64": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/@os-theme/win32-x64/-/win32-x64-0.0.8.tgz", - "integrity": "sha512-N3yxKNbVl2IBa/ncDuq55QhwqwUjnYLJxDKMEmYeJbLIV950qZNojPw3scXA6PbfxPZfIiRa8iz1pzNg9XxP8w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -687,20 +647,6 @@ "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", "license": "(AFL-2.1 OR BSD-3-Clause)" }, - "node_modules/os-theme": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/os-theme/-/os-theme-0.0.8.tgz", - "integrity": "sha512-u1q3bLSv5uMHNIiPItkfDrHXu6ZFs2juwqxWREFM/uVBa+7Kkhy2v49LmJev2JcinGwqiEccElB/XsH9gwasuA==", - "license": "MIT", - "optionalDependencies": { - "@os-theme/darwin-arm64": "0.0.8", - "@os-theme/linux-x64": "0.0.8", - "@os-theme/win32-x64": "0.0.8" - }, - "peerDependencies": { - "typescript": "^5" - } - }, "node_modules/tsx": { "version": "4.22.4", "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.4.tgz", @@ -718,20 +664,6 @@ "optionalDependencies": { "fsevents": "~2.3.3" } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "license": "Apache-2.0", - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } } } } diff --git a/java/scripts/codegen/package.json b/java/scripts/codegen/package.json index 696a20623..ec0ade02a 100644 --- a/java/scripts/codegen/package.json +++ b/java/scripts/codegen/package.json @@ -7,7 +7,7 @@ "generate:java": "tsx java.ts" }, "dependencies": { - "@github/copilot": "^1.0.64-0", + "@github/copilot": "^1.0.64-1", "json-schema": "^0.4.0", "tsx": "^4.22.4" } diff --git a/java/src/generated/java/com/github/copilot/generated/AssistantMessageEvent.java b/java/src/generated/java/com/github/copilot/generated/AssistantMessageEvent.java index afa9fe8d8..e4680a54c 100644 --- a/java/src/generated/java/com/github/copilot/generated/AssistantMessageEvent.java +++ b/java/src/generated/java/com/github/copilot/generated/AssistantMessageEvent.java @@ -66,7 +66,9 @@ public record AssistantMessageEventData( /** Identifier for the agent loop turn that produced this message, matching the corresponding assistant.turn_start event */ @JsonProperty("turnId") String turnId, /** Tool call ID of the parent tool invocation when this event originates from a sub-agent */ - @JsonProperty("parentToolCallId") String parentToolCallId + @JsonProperty("parentToolCallId") String parentToolCallId, + /** Provider-agnostic citations linking spans of this message's content to the sources that support them. Experimental; only populated when citation emission is enabled. */ + @JsonProperty("citations") Citations citations ) { } } diff --git a/java/src/generated/java/com/github/copilot/generated/CanvasRegistryChangedCanvas.java b/java/src/generated/java/com/github/copilot/generated/CanvasRegistryChangedCanvas.java index 17d1477c1..b4805b0bb 100644 --- a/java/src/generated/java/com/github/copilot/generated/CanvasRegistryChangedCanvas.java +++ b/java/src/generated/java/com/github/copilot/generated/CanvasRegistryChangedCanvas.java @@ -11,7 +11,6 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; -import java.util.Map; import javax.annotation.processing.Generated; /** @@ -34,7 +33,7 @@ public record CanvasRegistryChangedCanvas( /** Short, single-sentence description shown to the agent in canvas catalogs. */ @JsonProperty("description") String description, /** JSON Schema for canvas open input */ - @JsonProperty("inputSchema") Map inputSchema, + @JsonProperty("inputSchema") Object inputSchema, /** Actions the agent or host may invoke */ @JsonProperty("actions") List actions ) { diff --git a/java/src/generated/java/com/github/copilot/generated/CanvasRegistryChangedCanvasAction.java b/java/src/generated/java/com/github/copilot/generated/CanvasRegistryChangedCanvasAction.java index 34e30d3f2..b8e474e62 100644 --- a/java/src/generated/java/com/github/copilot/generated/CanvasRegistryChangedCanvasAction.java +++ b/java/src/generated/java/com/github/copilot/generated/CanvasRegistryChangedCanvasAction.java @@ -10,7 +10,6 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.Map; import javax.annotation.processing.Generated; /** @@ -27,6 +26,6 @@ public record CanvasRegistryChangedCanvasAction( /** Action description */ @JsonProperty("description") String description, /** JSON Schema for action input */ - @JsonProperty("inputSchema") Map inputSchema + @JsonProperty("inputSchema") Object inputSchema ) { } diff --git a/java/src/generated/java/com/github/copilot/generated/CitableSource.java b/java/src/generated/java/com/github/copilot/generated/CitableSource.java new file mode 100644 index 000000000..c66809fc1 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/CitableSource.java @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * A source supplied by a tool that should be made available to the model as citable content. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record CitableSource( + /** Stable identifier for this source within the tool result. Used for deduplication and may be used by future provider integrations to correlate response citations back to the originating source. */ + @JsonProperty("id") String id, + /** Human-readable title of the source. */ + @JsonProperty("title") String title, + /** The source text made available to the model as citable content. */ + @JsonProperty("content") String content, + /** URL of the source, when it is a web resource. */ + @JsonProperty("url") String url, + /** File path relative to the agent's workspace root, when the source is a file. */ + @JsonProperty("path") String path +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/CitationProvider.java b/java/src/generated/java/com/github/copilot/generated/CitationProvider.java new file mode 100644 index 000000000..46a02b256 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/CitationProvider.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import javax.annotation.processing.Generated; + +/** + * The system that produced a citation. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum CitationProvider { + /** The {@code anthropic} variant. */ + ANTHROPIC("anthropic"), + /** The {@code openai} variant. */ + OPENAI("openai"), + /** The {@code client} variant. */ + CLIENT("client"); + + private final String value; + CitationProvider(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static CitationProvider fromValue(String value) { + for (CitationProvider v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown CitationProvider value: " + value); + } +} diff --git a/java/src/generated/java/com/github/copilot/generated/CitationReference.java b/java/src/generated/java/com/github/copilot/generated/CitationReference.java new file mode 100644 index 000000000..e9ad222c1 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/CitationReference.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * A single citation occurrence linking a span of generated text to a supporting source. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record CitationReference( + /** Identifier of the CitationSource this reference points to (CitationSource.id). */ + @JsonProperty("sourceId") String sourceId, + /** The exact text from the source that supports the cited span, when provided by the model. */ + @JsonProperty("citedText") String citedText, + /** Location within the source that supports the cited span, when the provider reports one. */ + @JsonProperty("location") Object location, + /** Provider-native citation correlation data (e.g. Anthropic search_result_index / document_index), passed through opaquely for debugging and forward compatibility. */ + @JsonProperty("providerMetadata") Object providerMetadata +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/CitationSource.java b/java/src/generated/java/com/github/copilot/generated/CitationSource.java new file mode 100644 index 000000000..561c5eced --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/CitationSource.java @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * A source that backs one or more cited spans in the assistant's response. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record CitationSource( + /** Stable, turn-scoped identifier for this source, referenced by CitationReference.sourceId. */ + @JsonProperty("id") String id, + /** The system that produced this citation. */ + @JsonProperty("provider") CitationProvider provider, + /** Human-readable title of the source. */ + @JsonProperty("title") String title, + /** URL of the source, when it is a web resource. */ + @JsonProperty("url") String url, + /** File path relative to the agent's workspace root, when the source is a file. */ + @JsonProperty("path") String path +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/CitationSpan.java b/java/src/generated/java/com/github/copilot/generated/CitationSpan.java new file mode 100644 index 000000000..aaa8647a6 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/CitationSpan.java @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * A contiguous span of generated assistant text and the source references that support it. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record CitationSpan( + /** Start offset of the cited span within the final assistant message content (UTF-16 code units, zero-based, inclusive). */ + @JsonProperty("startIndex") Long startIndex, + /** End offset of the cited span within the final assistant message content (UTF-16 code units, zero-based, exclusive). */ + @JsonProperty("endIndex") Long endIndex, + /** The sources that support this span of generated text. */ + @JsonProperty("references") List references +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/Citations.java b/java/src/generated/java/com/github/copilot/generated/Citations.java new file mode 100644 index 000000000..c153c39a7 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/Citations.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Provider-agnostic citations linking spans of the assistant's response to their supporting sources. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record Citations( + /** Deduplicated set of sources referenced by the citation spans. */ + @JsonProperty("sources") List sources, + /** Spans of generated text annotated with the sources that support them. */ + @JsonProperty("spans") List spans +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/McpOauthCompletedEvent.java b/java/src/generated/java/com/github/copilot/generated/McpOauthCompletedEvent.java index 635751b43..0cbe1b0a8 100644 --- a/java/src/generated/java/com/github/copilot/generated/McpOauthCompletedEvent.java +++ b/java/src/generated/java/com/github/copilot/generated/McpOauthCompletedEvent.java @@ -35,7 +35,9 @@ public final class McpOauthCompletedEvent extends SessionEvent { @JsonInclude(JsonInclude.Include.NON_NULL) public record McpOauthCompletedEventData( /** Request ID of the resolved OAuth request */ - @JsonProperty("requestId") String requestId + @JsonProperty("requestId") String requestId, + /** How the pending OAuth request was completed */ + @JsonProperty("outcome") McpOauthCompletionOutcome outcome ) { } } diff --git a/java/src/generated/java/com/github/copilot/generated/McpOauthCompletionOutcome.java b/java/src/generated/java/com/github/copilot/generated/McpOauthCompletionOutcome.java new file mode 100644 index 000000000..6352224da --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/McpOauthCompletionOutcome.java @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import javax.annotation.processing.Generated; + +/** + * How the pending MCP OAuth request was completed + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum McpOauthCompletionOutcome { + /** The {@code token} variant. */ + TOKEN("token"), + /** The {@code cancelled} variant. */ + CANCELLED("cancelled"); + + private final String value; + McpOauthCompletionOutcome(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static McpOauthCompletionOutcome fromValue(String value) { + for (McpOauthCompletionOutcome v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown McpOauthCompletionOutcome value: " + value); + } +} diff --git a/java/src/generated/java/com/github/copilot/generated/McpOauthRequiredEvent.java b/java/src/generated/java/com/github/copilot/generated/McpOauthRequiredEvent.java index 02e67a35f..3f6ef8ef6 100644 --- a/java/src/generated/java/com/github/copilot/generated/McpOauthRequiredEvent.java +++ b/java/src/generated/java/com/github/copilot/generated/McpOauthRequiredEvent.java @@ -34,14 +34,18 @@ public final class McpOauthRequiredEvent extends SessionEvent { @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record McpOauthRequiredEventData( - /** Unique identifier for this OAuth request; used to respond via session.respondToMcpOAuth() */ + /** Unique identifier for this OAuth request; used to respond via session.mcp.oauth.handlePendingRequest */ @JsonProperty("requestId") String requestId, /** Display name of the MCP server that requires OAuth */ @JsonProperty("serverName") String serverName, /** URL of the MCP server that requires OAuth */ @JsonProperty("serverUrl") String serverUrl, /** Static OAuth client configuration, if the server specifies one */ - @JsonProperty("staticClientConfig") McpOauthRequiredStaticClientConfig staticClientConfig + @JsonProperty("staticClientConfig") McpOauthRequiredStaticClientConfig staticClientConfig, + /** OAuth WWW-Authenticate parameters parsed from the auth challenge, if available */ + @JsonProperty("wwwAuthenticateParams") McpOauthWWWAuthenticateParams wwwAuthenticateParams, + /** Raw OAuth protected-resource metadata document fetched for the MCP server, if available */ + @JsonProperty("resourceMetadata") String resourceMetadata ) { } } diff --git a/java/src/generated/java/com/github/copilot/generated/McpOauthWWWAuthenticateParams.java b/java/src/generated/java/com/github/copilot/generated/McpOauthWWWAuthenticateParams.java new file mode 100644 index 000000000..faa08e1e8 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/McpOauthWWWAuthenticateParams.java @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * OAuth WWW-Authenticate parameters parsed from an MCP auth challenge + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record McpOauthWWWAuthenticateParams( + /** Protected resource metadata URL from the WWW-Authenticate resource_metadata parameter */ + @JsonProperty("resourceMetadataUrl") String resourceMetadataUrl, + /** Requested OAuth scopes from the WWW-Authenticate scope parameter, if present */ + @JsonProperty("scope") String scope, + /** OAuth error from the WWW-Authenticate error parameter, if present */ + @JsonProperty("error") String error +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/ModelCallFailureBadRequestKind.java b/java/src/generated/java/com/github/copilot/generated/ModelCallFailureBadRequestKind.java new file mode 100644 index 000000000..1f17ed5e9 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/ModelCallFailureBadRequestKind.java @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import javax.annotation.processing.Generated; + +/** + * For HTTP 400 failures only: whether the response carried a structured CAPI error envelope (structured_error, a deterministic validation failure) or no error body (bodyless, the transient gateway/proxy signature). Absent for non-400 failures. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ModelCallFailureBadRequestKind { + /** The {@code bodyless} variant. */ + BODYLESS("bodyless"), + /** The {@code structured_error} variant. */ + STRUCTURED_ERROR("structured_error"); + + private final String value; + ModelCallFailureBadRequestKind(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ModelCallFailureBadRequestKind fromValue(String value) { + for (ModelCallFailureBadRequestKind v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ModelCallFailureBadRequestKind value: " + value); + } +} diff --git a/java/src/generated/java/com/github/copilot/generated/ModelCallFailureEvent.java b/java/src/generated/java/com/github/copilot/generated/ModelCallFailureEvent.java index 4b8a6cebb..40dc4c54e 100644 --- a/java/src/generated/java/com/github/copilot/generated/ModelCallFailureEvent.java +++ b/java/src/generated/java/com/github/copilot/generated/ModelCallFailureEvent.java @@ -51,7 +51,13 @@ public record ModelCallFailureEventData( /** Where the failed model call originated */ @JsonProperty("source") ModelCallFailureSource source, /** Raw provider/runtime error message for restricted telemetry */ - @JsonProperty("errorMessage") String errorMessage + @JsonProperty("errorMessage") String errorMessage, + /** For HTTP 400 failures only: whether the response carried a structured CAPI error envelope (structured_error, a deterministic validation failure) or no error body (bodyless, the transient gateway/proxy signature). Absent for non-400 failures. */ + @JsonProperty("badRequestKind") ModelCallFailureBadRequestKind badRequestKind, + /** For HTTP 400 failures only: the `code` from the CAPI error envelope (e.g. 'model_max_prompt_tokens_exceeded') identifying which deterministic validation failure occurred. Raw server-controlled string, emitted only through restricted telemetry. Absent for bodyless or non-400 failures. */ + @JsonProperty("errorCode") String errorCode, + /** For HTTP 400 failures only: the `type` from the CAPI error envelope (e.g. 'websocket_error'), a coarser companion to errorCode for envelopes that carry no code. Raw server-controlled string, emitted only through restricted telemetry. Absent for bodyless or non-400 failures. */ + @JsonProperty("errorType") String errorType ) { } } diff --git a/java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteResult.java b/java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteResult.java index d44f3e398..7459d5517 100644 --- a/java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteResult.java +++ b/java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteResult.java @@ -33,6 +33,8 @@ public record ToolExecutionCompleteResult( /** MCP Apps UI resource content for rendering in a sandboxed iframe */ @JsonProperty("uiResource") ToolExecutionCompleteUIResource uiResource, /** Structured content (arbitrary JSON) returned verbatim by the MCP tool */ - @JsonProperty("structuredContent") Object structuredContent + @JsonProperty("structuredContent") Object structuredContent, + /** Provider-neutral source material this tool makes available to the model as citable content. Persisted so it survives session resume. Experimental. */ + @JsonProperty("citableSources") List citableSources ) { } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseChunkError.java b/java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseChunkError.java new file mode 100644 index 000000000..551c534a1 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseChunkError.java @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Set to terminate the response with a transport-level failure. Implies end-of-stream; any further chunks for this requestId are ignored. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record LlmInferenceHttpResponseChunkError( + /** Human-readable failure description. */ + @JsonProperty("message") String message, + /** Optional machine-readable error code. */ + @JsonProperty("code") String code +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseChunkParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseChunkParams.java new file mode 100644 index 000000000..2a381d827 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseChunkParams.java @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * A response body chunk or terminal error. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record LlmInferenceHttpResponseChunkParams( + /** Matches the requestId from the originating httpRequestStart frame. */ + @JsonProperty("requestId") String requestId, + /** Body byte range. UTF-8 text when `binary` is absent or false; base64-encoded bytes when `binary` is true. May be empty (e.g. when the response body is empty: send a single chunk with empty data and end=true). */ + @JsonProperty("data") String data, + /** When true, `data` is base64-encoded bytes. When absent or false, `data` is UTF-8 text. */ + @JsonProperty("binary") Boolean binary, + /** When true, this is the final body chunk for the response. The runtime treats the response body as complete after receiving an end-marked chunk. */ + @JsonProperty("end") Boolean end, + /** Set to terminate the response with a transport-level failure. Implies end-of-stream; any further chunks for this requestId are ignored. */ + @JsonProperty("error") LlmInferenceHttpResponseChunkError error +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseChunkResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseChunkResult.java new file mode 100644 index 000000000..2ffddc1d3 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseChunkResult.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Whether the chunk was accepted. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record LlmInferenceHttpResponseChunkResult( + /** True when the chunk was matched to a pending request; false when unknown. */ + @JsonProperty("accepted") Boolean accepted +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseStartParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseStartParams.java new file mode 100644 index 000000000..69c26221b --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseStartParams.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import java.util.Map; +import javax.annotation.processing.Generated; + +/** + * Response head. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record LlmInferenceHttpResponseStartParams( + /** Matches the requestId from the originating httpRequestStart frame. */ + @JsonProperty("requestId") String requestId, + /** HTTP status code. */ + @JsonProperty("status") Long status, + /** Optional HTTP status reason phrase. */ + @JsonProperty("statusText") String statusText, + @JsonProperty("headers") Map> headers +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseStartResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseStartResult.java new file mode 100644 index 000000000..05692013a --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseStartResult.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Whether the start frame was accepted. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record LlmInferenceHttpResponseStartResult( + /** True when the response start was matched to a pending request; false when unknown. */ + @JsonProperty("accepted") Boolean accepted +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceSetProviderResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceSetProviderResult.java new file mode 100644 index 000000000..33c8fb722 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceSetProviderResult.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Indicates whether the calling client was registered as the LLM inference provider. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record LlmInferenceSetProviderResult( + /** Whether the provider was set successfully */ + @JsonProperty("success") Boolean success +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ModelBillingTokenPrices.java b/java/src/generated/java/com/github/copilot/generated/rpc/ModelBillingTokenPrices.java index 5cbf0e99a..246d4e585 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/ModelBillingTokenPrices.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ModelBillingTokenPrices.java @@ -25,12 +25,18 @@ public record ModelBillingTokenPrices( @JsonProperty("inputPrice") Double inputPrice, /** AI Credits cost per billing batch of output tokens */ @JsonProperty("outputPrice") Double outputPrice, - /** AI Credits cost per billing batch of cached tokens */ + /** Deprecated: use cacheReadPrice. AI Credits cost per billing batch of cached tokens */ @JsonProperty("cachePrice") Double cachePrice, + /** AI Credits cost per billing batch of cached (read) tokens */ + @JsonProperty("cacheReadPrice") Double cacheReadPrice, + /** AI Credits cost per billing batch of cache-write (cache creation) tokens. */ + @JsonProperty("cacheWritePrice") Double cacheWritePrice, /** Number of tokens per standard billing batch */ @JsonProperty("batchSize") Long batchSize, - /** Prompt token budget (max_prompt_tokens) for the default tier. The total context window is this value plus the model's max_output_tokens. */ + /** Deprecated: use maxPromptTokens. Prompt token budget for the default tier. The total context window is this value plus the model's max_output_tokens. */ @JsonProperty("contextMax") Long contextMax, + /** Prompt token budget for the default tier. The total context window is this value plus the model's max_output_tokens. */ + @JsonProperty("maxPromptTokens") Long maxPromptTokens, /** Long context tier pricing (available for models with extended context windows) */ @JsonProperty("longContext") ModelBillingTokenPricesLongContext longContext ) { diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ModelBillingTokenPricesLongContext.java b/java/src/generated/java/com/github/copilot/generated/rpc/ModelBillingTokenPricesLongContext.java index 5c2cfe98c..95ca05631 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/ModelBillingTokenPricesLongContext.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ModelBillingTokenPricesLongContext.java @@ -25,9 +25,15 @@ public record ModelBillingTokenPricesLongContext( @JsonProperty("inputPrice") Double inputPrice, /** AI Credits cost per billing batch of output tokens */ @JsonProperty("outputPrice") Double outputPrice, - /** AI Credits cost per billing batch of cached tokens */ + /** Deprecated: use cacheReadPrice. AI Credits cost per billing batch of cached tokens */ @JsonProperty("cachePrice") Double cachePrice, - /** Prompt token budget (max_prompt_tokens) for the long context tier. The total context window is this value plus the model's max_output_tokens. */ - @JsonProperty("contextMax") Long contextMax + /** AI Credits cost per billing batch of cached (read) tokens */ + @JsonProperty("cacheReadPrice") Double cacheReadPrice, + /** AI Credits cost per billing batch of cache-write (cache creation) tokens. */ + @JsonProperty("cacheWritePrice") Double cacheWritePrice, + /** Deprecated: use maxPromptTokens. Prompt token budget for the long context tier. The total context window is this value plus the model's max_output_tokens. */ + @JsonProperty("contextMax") Long contextMax, + /** Prompt token budget for the long context tier. The total context window is this value plus the model's max_output_tokens. */ + @JsonProperty("maxPromptTokens") Long maxPromptTokens ) { } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfig.java b/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfig.java index 29c25a5a6..a3d3f4f9a 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfig.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfig.java @@ -10,7 +10,6 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.Map; import javax.annotation.processing.Generated; /** @@ -27,7 +26,7 @@ public record SandboxConfig( /** User-managed sandbox policy fragment merged into the auto-discovered base policy. */ @JsonProperty("userPolicy") SandboxConfigUserPolicy userPolicy, /** Raw `ContainerConfig` (per `@microsoft/mxc-sdk`) passed directly to `spawnSandboxFromConfig`, bypassing policy merging. */ - @JsonProperty("config") Map config, + @JsonProperty("config") Object config, /** Whether to auto-add the current working directory to readwritePaths. Default: true. */ @JsonProperty("addCurrentWorkingDirectory") Boolean addCurrentWorkingDirectory ) { diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerLlmInferenceApi.java b/java/src/generated/java/com/github/copilot/generated/rpc/ServerLlmInferenceApi.java new file mode 100644 index 000000000..4b7663f1f --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ServerLlmInferenceApi.java @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.github.copilot.CopilotExperimental; +import java.util.concurrent.CompletableFuture; +import javax.annotation.processing.Generated; + +/** + * API methods for the {@code llmInference} namespace. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class ServerLlmInferenceApi { + + private final RpcCaller caller; + + /** @param caller the RPC transport function */ + ServerLlmInferenceApi(RpcCaller caller) { + this.caller = caller; + } + + /** + * Indicates whether the calling client was registered as the LLM inference provider. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture setProvider() { + return caller.invoke("llmInference.setProvider", java.util.Map.of(), LlmInferenceSetProviderResult.class); + } + + /** + * Response head. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture httpResponseStart(LlmInferenceHttpResponseStartParams params) { + return caller.invoke("llmInference.httpResponseStart", params, LlmInferenceHttpResponseStartResult.class); + } + + /** + * A response body chunk or terminal error. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture httpResponseChunk(LlmInferenceHttpResponseChunkParams params) { + return caller.invoke("llmInference.httpResponseChunk", params, LlmInferenceHttpResponseChunkResult.class); + } + +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java b/java/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java index f96c87386..9b90cea4a 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java @@ -48,6 +48,8 @@ public final class ServerRpc { public final ServerRuntimeApi runtime; /** API methods for the {@code sessionFs} namespace. */ public final ServerSessionFsApi sessionFs; + /** API methods for the {@code llmInference} namespace. */ + public final ServerLlmInferenceApi llmInference; /** API methods for the {@code sessions} namespace. */ public final ServerSessionsApi sessions; /** API methods for the {@code agentRegistry} namespace. */ @@ -72,6 +74,7 @@ public ServerRpc(RpcCaller caller) { this.user = new ServerUserApi(caller); this.runtime = new ServerRuntimeApi(caller); this.sessionFs = new ServerSessionFsApi(caller); + this.llmInference = new ServerLlmInferenceApi(caller); this.sessions = new ServerSessionsApi(caller); this.agentRegistry = new ServerAgentRegistryApi(caller); } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthApi.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthApi.java index 59c4e45a1..95a081206 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthApi.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthApi.java @@ -46,6 +46,22 @@ public CompletableFuture respond(SessionMcpOauthRespondParams params) { return caller.invoke("session.mcp.oauth.respond", _p, Void.class); } + /** + * Pending MCP OAuth request ID and host-provided token or cancellation response. + *

+ * Note: the {@code sessionId} field in the params record is overridden + * by the session-scoped wrapper; any value provided is ignored. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture handlePendingRequest(SessionMcpOauthHandlePendingRequestParams params) { + com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); + _p.put("sessionId", this.sessionId); + return caller.invoke("session.mcp.oauth.handlePendingRequest", _p, SessionMcpOauthHandlePendingRequestResult.class); + } + /** * Remote MCP server name and optional overrides controlling reauthentication, OAuth client display name, and the callback success-page copy. *

diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthHandlePendingRequestParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthHandlePendingRequestParams.java new file mode 100644 index 000000000..403bd548a --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthHandlePendingRequestParams.java @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Pending MCP OAuth request ID and host-provided token or cancellation response. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SessionMcpOauthHandlePendingRequestParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId, + /** OAuth request identifier from the mcp.oauth_required event */ + @JsonProperty("requestId") String requestId, + /** Host response to the pending OAuth request. */ + @JsonProperty("result") Object result +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthHandlePendingRequestResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthHandlePendingRequestResult.java new file mode 100644 index 000000000..a7bca646e --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthHandlePendingRequestResult.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Indicates whether the pending MCP OAuth response was accepted. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SessionMcpOauthHandlePendingRequestResult( + /** Whether the response was accepted. False if the request was unknown, timed out, or already resolved. */ + @JsonProperty("success") Boolean success +) { +} diff --git a/java/src/test/java/com/github/copilot/CapiProxy.java b/java/src/test/java/com/github/copilot/CapiProxy.java index 90c2dd0a7..6484f0581 100644 --- a/java/src/test/java/com/github/copilot/CapiProxy.java +++ b/java/src/test/java/com/github/copilot/CapiProxy.java @@ -290,6 +290,53 @@ public void setCopilotUserByToken(String token, String login, String copilotPlan } } + /** + * Registers a raw Copilot user response for a given token on the + * {@code /copilot_internal/user} endpoint. + * + *

+ * Unlike + * {@link #setCopilotUserByToken(String, String, String, String, String, String)}, + * this posts the response object verbatim, so callers control the exact field + * names the proxy returns to the CLI. This matters because the CLI reads + * snake_case fields (e.g. {@code copilot_plan}, {@code is_mcp_enabled}) from + * the raw user JSON to gate MCP enablement. Use this to register the default + * e2e user with the same snake_case shape the Go, Node, Python, and .NET + * harnesses post, keeping MCP behavior hermetic and consistent across SDKs. + *

+ * + * @param token + * the GitHub token to configure + * @param response + * the raw user response object to return for the token (field names + * are sent verbatim) + * @throws IOException + * if the request fails + * @throws InterruptedException + * if the request is interrupted + */ + public void setCopilotUserByToken(String token, Map response) + throws IOException, InterruptedException { + if (proxyUrl == null) { + throw new IllegalStateException("Proxy not started"); + } + + Map payload = new java.util.HashMap<>(); + payload.put("token", token); + payload.put("response", response); + + String body = MAPPER.writeValueAsString(payload); + + HttpRequest request = HttpRequest.newBuilder().uri(URI.create(proxyUrl + "/copilot-user-config")) + .header("Content-Type", "application/json").POST(HttpRequest.BodyPublishers.ofString(body)).build(); + + HttpResponse response2 = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + if (response2.statusCode() != 200) { + throw new IOException( + "Failed to set copilot user config: " + response2.statusCode() + ": " + response2.body()); + } + } + /** * Stops the proxy server gracefully. * diff --git a/java/src/test/java/com/github/copilot/E2ETestContext.java b/java/src/test/java/com/github/copilot/E2ETestContext.java index 2bc139d94..4089e10ff 100644 --- a/java/src/test/java/com/github/copilot/E2ETestContext.java +++ b/java/src/test/java/com/github/copilot/E2ETestContext.java @@ -55,6 +55,12 @@ public class E2ETestContext implements AutoCloseable { private static final Logger LOG = Logger.getLogger(E2ETestContext.class.getName()); + + /** + * The default GitHub token used by the CLI in e2e tests. The proxy resolves + * this token to the default Copilot user registered at context creation. + */ + private static final String DEFAULT_GITHUB_TOKEN = "fake-token-for-e2e-tests"; private static final Pattern SNAKE_CASE = Pattern.compile("[^a-zA-Z0-9]"); private static final Pattern USER_CONTENT_PATTERN = Pattern .compile("^\\s+-\\s+role:\\s+user\\s*$\\s+content:\\s*(.+?)$", Pattern.MULTILINE); @@ -97,6 +103,23 @@ public static E2ETestContext create() throws IOException, InterruptedException { CapiProxy proxy = new CapiProxy(); String proxyUrl = proxy.start(); + // Register a default Copilot user for the CLI's default token so the proxy's + // /copilot_internal/user endpoint returns a valid user (HTTP 200) instead of + // 401 "Bad credentials". CLI 1.0.64-1 gates MCP enablement on this user: + // `is_mcp_enabled` (added by the proxy) is the global gate, and snake_case + // `copilot_plan` makes the third-party MCP policy resolver early-return + // allow-all for non-org plans (anything other than business/enterprise), + // avoiding a /copilot/mcp_registry network call the proxy does not serve. + // Without this, MCP servers never reach CONNECTED. This mirrors the Go, + // Node, Python, and .NET harnesses, which all register the same default + // individual_pro user at context creation. + Map defaultUser = new HashMap<>(); + defaultUser.put("login", "e2e-test-user"); + defaultUser.put("copilot_plan", "individual_pro"); + defaultUser.put("endpoints", Map.of("api", proxyUrl, "telemetry", "https://localhost:1/telemetry")); + defaultUser.put("analytics_tracking_id", "e2e-test-tracking-id"); + proxy.setCopilotUserByToken(DEFAULT_GITHUB_TOKEN, defaultUser); + return new E2ETestContext(cliPath, homeDir, workDir, proxyUrl, proxy, repoRoot); } @@ -256,6 +279,11 @@ public List> getExchanges() throws IOException, InterruptedE public Map getEnvironment() { Map env = new HashMap<>(System.getenv()); env.put("COPILOT_API_URL", proxyUrl); + // Route GitHub API calls (e.g. the MCP registry policy check) to the + // replay proxy so MCP enablement stays hermetic. Without this the CLI + // reaches the real api.github.com, which is slow/unreachable on macOS + // CI runners and makes MCP servers time out before reaching connected. + env.put("COPILOT_DEBUG_GITHUB_API_URL", proxyUrl); env.put("COPILOT_HOME", homeDir.toString()); env.put("GH_CONFIG_DIR", homeDir.toString()); env.put("XDG_CONFIG_HOME", homeDir.toString()); @@ -277,8 +305,8 @@ public Map getEnvironment() { env.put("REQUESTS_CA_BUNDLE", caFile); env.put("CURL_CA_BUNDLE", caFile); env.put("GIT_SSL_CAINFO", caFile); - env.put("GH_TOKEN", "fake-token-for-e2e-tests"); - env.put("GITHUB_TOKEN", "fake-token-for-e2e-tests"); + env.put("GH_TOKEN", DEFAULT_GITHUB_TOKEN); + env.put("GITHUB_TOKEN", DEFAULT_GITHUB_TOKEN); env.put("GH_ENTERPRISE_TOKEN", ""); env.put("GITHUB_ENTERPRISE_TOKEN", ""); } @@ -293,7 +321,7 @@ public Map getEnvironment() { */ public CopilotClient createClient() { CopilotClientOptions options = new CopilotClientOptions().setCliPath(cliPath).setCwd(workDir.toString()) - .setEnvironment(getEnvironment()).setGitHubToken("fake-token-for-e2e-tests"); + .setEnvironment(getEnvironment()).setGitHubToken(DEFAULT_GITHUB_TOKEN); return new CopilotClient(options); } @@ -318,7 +346,7 @@ public CopilotClient createClient(CopilotClientOptions options) { options.setEnvironment(getEnvironment()); } if (options.getGitHubToken() == null) { - options.setGitHubToken("fake-token-for-e2e-tests"); + options.setGitHubToken(DEFAULT_GITHUB_TOKEN); } return new CopilotClient(options); @@ -438,10 +466,21 @@ private static String getCliPath(Path repoRoot) throws IOException { return harnessCliPath.toString(); } - // Try nodejs installation - Path cliPath = repoRoot.resolve("nodejs/node_modules/@github/copilot/index.js"); - if (Files.exists(cliPath)) { - return cliPath.toString(); + // Try nodejs installation. As of CLI 1.0.64-1 the @github/copilot package + // is a thin loader; the runnable index.js ships in the installed + // platform-specific package (e.g. @github/copilot-linux-x64). Exactly one + // is installed. Running index.js under Node.js is the documented preferred + // entry point and matches the Go, Python, Rust, and .NET test harnesses. + Path githubModules = repoRoot.resolve("nodejs/node_modules/@github"); + if (Files.isDirectory(githubModules)) { + try (var modules = Files.newDirectoryStream(githubModules, "copilot-*")) { + for (Path module : modules) { + Path indexJs = module.resolve("index.js"); + if (Files.exists(indexJs)) { + return indexJs.toString(); + } + } + } } // Fallback: try to find 'copilot' in PATH diff --git a/java/src/test/java/com/github/copilot/PerSessionAuthTest.java b/java/src/test/java/com/github/copilot/PerSessionAuthTest.java index 000d36e4b..1b0c419c3 100644 --- a/java/src/test/java/com/github/copilot/PerSessionAuthTest.java +++ b/java/src/test/java/com/github/copilot/PerSessionAuthTest.java @@ -111,16 +111,29 @@ void shouldIsolateAuthBetweenSessions() throws Exception { @Test void shouldBeUnauthenticatedWithoutToken() throws Exception { - try (CopilotClient client = createAuthTestClient()) { + Map env = new HashMap<>(ctx.getEnvironment()); + env.put("COPILOT_DEBUG_GITHUB_API_URL", ctx.getProxyUrl()); + // Strip global auth tokens so there is no global identity to fall back to, + // mirroring the Go/Node per-session-auth "without token" tests. Otherwise the + // process-level fake token resolves to the default e2e user registered on the + // proxy and the session reports a login. + env.put("GH_TOKEN", ""); + env.put("GITHUB_TOKEN", ""); + env.put("COPILOT_SDK_AUTH_TOKEN", ""); + + // Build the client directly (not via ctx.createClient) so the context's + // default GitHub token is not auto-injected and useLoggedInUser is disabled. + CopilotClientOptions options = new CopilotClientOptions().setCliPath(ctx.getCliPath()) + .setCwd(ctx.getWorkDir().toString()).setEnvironment(env).setUseLoggedInUser(false); + + try (CopilotClient client = new CopilotClient(options)) { CopilotSession session = client .createSession(new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get(); try { SessionAuthGetStatusResult authStatus = session.getRpc().auth.getStatus().get(); - // Without a per-session token, there is no per-session identity. - // In CI the process-level fake token may still authenticate globally, - // so we check login rather than isAuthenticated. + // With no global or per-session token, there is no identity at all. assertNull(authStatus.login(), "Expected no login without per-session token"); } finally { session.close(); diff --git a/java/src/test/java/com/github/copilot/SessionEventHandlingTest.java b/java/src/test/java/com/github/copilot/SessionEventHandlingTest.java index 17c2c59cf..3ca56b817 100644 --- a/java/src/test/java/com/github/copilot/SessionEventHandlingTest.java +++ b/java/src/test/java/com/github/copilot/SessionEventHandlingTest.java @@ -865,7 +865,7 @@ private SessionStartEvent createSessionStartEvent(String sessionId) { private AssistantMessageEvent createAssistantMessageEvent(String content) { var event = new AssistantMessageEvent(); var data = new AssistantMessageEvent.AssistantMessageEventData(null, null, content, null, null, null, null, - null, null, null, null, null, null, null, null, null); + null, null, null, null, null, null, null, null, null, null); event.setData(data); return event; } diff --git a/java/src/test/java/com/github/copilot/TestUtil.java b/java/src/test/java/com/github/copilot/TestUtil.java index 42e6c3182..23bb53e49 100644 --- a/java/src/test/java/com/github/copilot/TestUtil.java +++ b/java/src/test/java/com/github/copilot/TestUtil.java @@ -40,7 +40,7 @@ public static String tempPath(String filename) { *
  • Otherwise search the system PATH using {@code where.exe} (Windows) or * {@code which} (Linux/macOS).
  • *
  • Walk parent directories looking for - * {@code nodejs/node_modules/@github/copilot/index.js}.
  • + * {@code nodejs/node_modules/@github/copilot/npm-loader.js}. * * *

    @@ -88,8 +88,9 @@ static String findCliPath() { return npmLoader.toString(); } - // nodejs installation - Path cliPath = current.resolve("nodejs/node_modules/@github/copilot/index.js"); + // nodejs installation (thin loader; resolves the platform-specific + // CLI package internally) + Path cliPath = current.resolve("nodejs/node_modules/@github/copilot/npm-loader.js"); if (cliPath.toFile().exists()) { return cliPath.toString(); } diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index 5e1ae09ee..08cd933c4 100644 --- a/nodejs/package-lock.json +++ b/nodejs/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0-dev", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.64-0", + "@github/copilot": "^1.0.64-1", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" }, @@ -697,32 +697,31 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.64-0", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.64-0.tgz", - "integrity": "sha512-PlH7ByBHjmPLqLXS4CE2q8hN6CFEfkCMV6ScBEzW/u73+KYQB4fGNouo8Lr8okL6D5CW5rzPJbsXyISyJqVOZg==", + "version": "1.0.64-1", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.64-1.tgz", + "integrity": "sha512-lojV4Cb7oT4VJnYPEKBRH8KI3W43Q4Lh0Pc+V6sej+xjPJkoqwm68sNKn73/p3wXPBSTVTzPeCm9WhIisgf1Jw==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { - "detect-libc": "^2.1.2", - "os-theme": "^0.0.8" + "detect-libc": "^2.1.2" }, "bin": { "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.64-0", - "@github/copilot-darwin-x64": "1.0.64-0", - "@github/copilot-linux-arm64": "1.0.64-0", - "@github/copilot-linux-x64": "1.0.64-0", - "@github/copilot-linuxmusl-arm64": "1.0.64-0", - "@github/copilot-linuxmusl-x64": "1.0.64-0", - "@github/copilot-win32-arm64": "1.0.64-0", - "@github/copilot-win32-x64": "1.0.64-0" + "@github/copilot-darwin-arm64": "1.0.64-1", + "@github/copilot-darwin-x64": "1.0.64-1", + "@github/copilot-linux-arm64": "1.0.64-1", + "@github/copilot-linux-x64": "1.0.64-1", + "@github/copilot-linuxmusl-arm64": "1.0.64-1", + "@github/copilot-linuxmusl-x64": "1.0.64-1", + "@github/copilot-win32-arm64": "1.0.64-1", + "@github/copilot-win32-x64": "1.0.64-1" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.64-0", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.64-0.tgz", - "integrity": "sha512-97DUGiuYrkCYOlSSLWMmr+K0uGzAxz1JOL/GyO/7mNl6V/1xgs6Van1Jj+Dpj4ly96iHE8lUIW8cQNCG66644g==", + "version": "1.0.64-1", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.64-1.tgz", + "integrity": "sha512-MQHZT9LhmCiq+ogO1E8cPCWrurZ6x+r9lPJfYUSnOyMO+EHbREpiJwOOChxtLHgL2/tKJSZdId2pg3tDgUlcsw==", "cpu": [ "arm64" ], @@ -736,9 +735,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.64-0", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.64-0.tgz", - "integrity": "sha512-2PXY4mSFtIjFdRaAt8PakegRgGtf6Sz9z6U/dIgVygNfctVNzaL5FH65PNPm8Y80jaDvEcz1/XY5MiQtxnlzZQ==", + "version": "1.0.64-1", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.64-1.tgz", + "integrity": "sha512-kOQY7CvI7He0eO3ObQAHePWdkNLWAOegCSzUqUmdcpa1SNVqbZ3GBMsQ7uAZQip2cQxnGZ7pS1v6tKQ0HJdkYw==", "cpu": [ "x64" ], @@ -752,9 +751,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.64-0", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.64-0.tgz", - "integrity": "sha512-PLP+vR508fOTlCr9CSZiXi9geicHKXuX9jLGdwNqK2TMZO5TqCLz8wP7dBEmkdkeXcFKovMb8nQVB1Toc6xutw==", + "version": "1.0.64-1", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.64-1.tgz", + "integrity": "sha512-hIfuO7Q+pWs0SKfIRYqT+CjMaupudnhp4RMS6XoJ5s/e33rvpj2tkTkXYlHJo1PMDI823vvbqgpEdr+KeewMwg==", "cpu": [ "arm64" ], @@ -768,9 +767,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.64-0", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.64-0.tgz", - "integrity": "sha512-NvVjQ69zr390ijzo2f75+v0DHm6xnvPbi67ugnKDk7ZPbx8P3vSxVdAnrzrrL4T3T8ng3pJANcC4p+eGbx+UDw==", + "version": "1.0.64-1", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.64-1.tgz", + "integrity": "sha512-VHaE62pha0rDDvuNN3bd97gf0EZ+EJebstM1ejHsMYoPT1IOUkYEXlNfGGHY+GfUGYxAiy/+Uew4xw5mJyy/Sw==", "cpu": [ "x64" ], @@ -784,9 +783,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.64-0", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.64-0.tgz", - "integrity": "sha512-qCnVF5vIcTO74CukAENZo8e5nqXm4QUshuKN69aiZb5GOhVvyyIKsf5Jo7ikZt54jJBHycAMUKlTA8L3/nK+KA==", + "version": "1.0.64-1", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.64-1.tgz", + "integrity": "sha512-L/YrZPotRujAP0QERq+DlkR1SLr7abbTSz/56JqKKOqEdjKZPdQW1bUlhL/w1CZg1gXlTNUsNVyKz/fUfrEBgw==", "cpu": [ "arm64" ], @@ -800,9 +799,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.64-0", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.64-0.tgz", - "integrity": "sha512-WDBEmkBk1RulTfdLK5IuttNBadjLOBpvQyonGQ/aLeaetRNNdapoygrSjFU7q1QBSenmCyanXH6D+TS7tP3Qsw==", + "version": "1.0.64-1", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.64-1.tgz", + "integrity": "sha512-AGMjXqR128oyjiJhoI6Gd7JP5ddWkib+P4YH/JoHm05iNn23ZYl4tSc0XihHzeyMI1ix7Aacn8UINYB7lGOGOA==", "cpu": [ "x64" ], @@ -816,9 +815,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.64-0", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.64-0.tgz", - "integrity": "sha512-PC7yuUKcVbhli4bpzWFVT3juxj+v/iONazetNe3tMpHWza3W7MeFRifzAseSErKQCt2fHJth3m8bQAwFN2jfrA==", + "version": "1.0.64-1", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.64-1.tgz", + "integrity": "sha512-vvv+gnemi9WKaxF41zz7Xmq6a493n8Yjps5UFaOY6a3WR222kKXZXfOpeRvIYsDgnIPHGBHIj1TBOmnHQT4V4w==", "cpu": [ "arm64" ], @@ -832,9 +831,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.64-0", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.64-0.tgz", - "integrity": "sha512-d2fnUTIlqNxCqS2PuV+FD99ZOYBaX72OLtAmphbKyz36KyZ6D4ssiu8M4vHVTKWWdyc3TWiLsnIB+ryWdv1gGw==", + "version": "1.0.64-1", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.64-1.tgz", + "integrity": "sha512-mcHvD0fjGDuqr/YXzy8mKuDmah1F+qjPujxoFuGmabmTJZ33cSIJ3nq7RRvxZNIdp8YJ57NkbcW30WvIcOeJ3w==", "cpu": [ "x64" ], @@ -939,45 +938,6 @@ "@emnapi/runtime": "^1.7.1" } }, - "node_modules/@os-theme/darwin-arm64": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/@os-theme/darwin-arm64/-/darwin-arm64-0.0.8.tgz", - "integrity": "sha512-gMsOs+8Ju396a5yyMWigkbA0dMTxD78U3HzG3mlpiAyn6hfd5dbyI4VGP+sfTB82KGgWLzIhWWTFX5UYY6iX0A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@os-theme/linux-x64": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/@os-theme/linux-x64/-/linux-x64-0.0.8.tgz", - "integrity": "sha512-zvjmBUiSQPjM1RbhpsfCDYMJxW4eLlGmkFPnpteC/03X2lz6CjiX2hfbN2EWLxXjNnIje3Jqaen8IsqEnWrRBg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@os-theme/win32-x64": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/@os-theme/win32-x64/-/win32-x64-0.0.8.tgz", - "integrity": "sha512-N3yxKNbVl2IBa/ncDuq55QhwqwUjnYLJxDKMEmYeJbLIV950qZNojPw3scXA6PbfxPZfIiRa8iz1pzNg9XxP8w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@oxc-project/types": { "version": "0.133.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", @@ -3087,20 +3047,6 @@ "node": ">= 0.8.0" } }, - "node_modules/os-theme": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/os-theme/-/os-theme-0.0.8.tgz", - "integrity": "sha512-u1q3bLSv5uMHNIiPItkfDrHXu6ZFs2juwqxWREFM/uVBa+7Kkhy2v49LmJev2JcinGwqiEccElB/XsH9gwasuA==", - "license": "MIT", - "optionalDependencies": { - "@os-theme/darwin-arm64": "0.0.8", - "@os-theme/linux-x64": "0.0.8", - "@os-theme/win32-x64": "0.0.8" - }, - "peerDependencies": { - "typescript": "^5" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -3652,6 +3598,7 @@ "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", diff --git a/nodejs/package.json b/nodejs/package.json index 11dc978bc..140a50fd4 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -56,7 +56,7 @@ "author": "GitHub", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.64-0", + "@github/copilot": "^1.0.64-1", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" }, diff --git a/nodejs/samples/package-lock.json b/nodejs/samples/package-lock.json index a824b4c47..1589ceb67 100644 --- a/nodejs/samples/package-lock.json +++ b/nodejs/samples/package-lock.json @@ -18,7 +18,7 @@ "version": "0.0.0-dev", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.64-0", + "@github/copilot": "^1.0.64-1", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" }, diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index 6b4aca13e..a6efb061a 100644 --- a/nodejs/src/client.ts +++ b/nodejs/src/client.ts @@ -277,36 +277,65 @@ function getNodeExecPath(): string { } /** - * Gets the path to the bundled CLI from the @github/copilot package. - * Uses index.js directly rather than npm-loader.js (which spawns the native binary). + * Computes the candidate platform-specific CLI package names for the current + * platform/arch, mirroring @github/copilot's npm-loader. As of CLI 1.0.64-1 the + * @github/copilot package is a thin loader and the actual CLI ships in a + * platform package (e.g. @github/copilot-darwin-arm64). For Linux we try both + * the glibc and musl variants since only the matching one is installed. + */ +function getCliPlatformPackageNames(): string[] { + const arch = process.arch; + const variants = process.platform === "linux" ? ["linux", "linuxmusl"] : [process.platform]; + return variants.map((variant) => `@github/copilot-${variant}-${arch}`); +} + +/** + * Gets the path to the bundled CLI from the platform-specific @github/copilot-* + * package. Uses index.js directly rather than the native binary so the CLI runs + * under the current Node.js runtime. * * In ESM, uses import.meta.resolve directly. In CJS (e.g., VS Code extensions * bundled with esbuild format:"cjs"), import.meta is empty so we fall back to * walking node_modules to find the package. */ function getBundledCliPath(): string { + const packageNames = getCliPlatformPackageNames(); + if (typeof import.meta.resolve === "function") { // ESM: resolve via import.meta.resolve - const sdkUrl = import.meta.resolve("@github/copilot/sdk"); - const sdkPath = fileURLToPath(sdkUrl); - // sdkPath is like .../node_modules/@github/copilot/sdk/index.js - // Go up two levels to get the package root, then append index.js - return join(dirname(dirname(sdkPath)), "index.js"); + for (const packageName of packageNames) { + try { + const sdkUrl = import.meta.resolve(`${packageName}/sdk`); + const sdkPath = fileURLToPath(sdkUrl); + // sdkPath is like .../node_modules/@github/copilot-/sdk/index.js + // Go up two levels to get the package root, then append index.js + return join(dirname(dirname(sdkPath)), "index.js"); + } catch { + // Try the next candidate platform package. + } + } + throw new Error( + `Could not resolve a @github/copilot platform package (tried ${packageNames.join(", ")}). ` + + `Ensure @github/copilot is installed, or pass cliPath/cliUrl to CopilotClient.` + ); } - // CJS fallback: the @github/copilot package has ESM-only exports so - // require.resolve cannot reach it. Walk the module search paths instead. + // CJS fallback: the platform packages have ESM-only exports so + // require.resolve cannot reach them. Walk the module search paths instead. const req = createRequire(__filename); const searchPaths = req.resolve.paths("@github/copilot") ?? []; for (const base of searchPaths) { - const candidate = join(base, "@github", "copilot", "index.js"); - if (existsSync(candidate)) { - return candidate; + for (const packageName of packageNames) { + const candidate = join(base, ...packageName.split("/"), "index.js"); + if (existsSync(candidate)) { + return candidate; + } } } throw new Error( - `Could not find @github/copilot package. Searched ${searchPaths.length} paths. ` + - `Ensure it is installed, or pass cliPath/cliUrl to CopilotClient.` + `Could not find a @github/copilot platform package (tried ${packageNames.join(", ")}). ` + + `Searched ${searchPaths.length} paths. ` + + `Ensure @github/copilot is installed, or pass cliPath/cliUrl to CopilotClient.` ); } diff --git a/nodejs/src/generated/rpc.ts b/nodejs/src/generated/rpc.ts index ba27a2d52..6303f9db2 100644 --- a/nodejs/src/generated/rpc.ts +++ b/nodejs/src/generated/rpc.ts @@ -501,6 +501,17 @@ export type InstructionSourceLocation = | "working-directory" /** Instructions live in plugin-provided configuration. */ | "plugin"; +/** + * Transport the runtime would otherwise use for this request. `http` (the default when absent) covers plain HTTP and SSE responses; `websocket` indicates a full-duplex message channel where each body chunk maps to one WebSocket message and the `binary` flag distinguishes text from binary frames. The SDK consumer uses this to decide whether to service the request with an HTTP client or a WebSocket client. It is the one piece of request metadata the consumer cannot reliably infer from the URL or headers alone. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "LlmInferenceHttpRequestStartTransport". + */ +export type LlmInferenceHttpRequestStartTransport = + /** Plain HTTP or SSE response. Each body chunk is an opaque byte range; the response is a status line, headers, and a (possibly streamed) body. */ + | "http" + /** Full-duplex WebSocket channel. Each body chunk maps to exactly one WebSocket message and the `binary` flag distinguishes text from binary frames; request and response chunks flow concurrently. */ + | "websocket"; /** * Repository host type * @@ -682,6 +693,36 @@ export type McpServerConfigHttpOauthGrantType = | "authorization_code" /** Headless client credentials flow using the configured OAuth client. */ | "client_credentials"; +/** + * Host response to the pending OAuth request. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "McpOauthPendingRequestResponse". + */ +/** @experimental */ +export type McpOauthPendingRequestResponse = + | { + /** + * Access token acquired by the SDK host + */ + accessToken: string; + /** + * OAuth token type. Defaults to Bearer when omitted. + */ + tokenType?: string; + /** + * Refresh token supplied by the host, if available. + */ + refreshToken?: string; + /** + * Token lifetime in seconds, if known. + */ + expiresIn?: number; + kind: "token"; + } + | { + kind: "cancelled"; + }; /** * Outcome of the sampling inference. 'success' produced a response; 'failure' encountered an error (including agent-side rejection by content filter or criteria); 'cancelled' the caller cancelled this execution via cancelSamplingExecution. * @@ -3648,7 +3689,6 @@ export interface ExternalToolTextResultForLlm { * Structured content blocks from the tool */ contents?: ExternalToolTextResultForLlmContent[]; - [k: string]: unknown | undefined; } /** * Binary result returned by a tool for the model @@ -4303,6 +4343,192 @@ export interface InstructionSource { */ projectPath?: string; } +/** + * HTTP headers as a map from lowercased header name to a list of values. Multi-valued headers (e.g. Set-Cookie) preserve all values. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "LlmInferenceHeaders". + */ +/** @experimental */ +export interface LlmInferenceHeaders { + [k: string]: string[] | undefined; +} +/** + * A request body chunk or cancellation signal. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "LlmInferenceHttpRequestChunkRequest". + */ +export interface LlmInferenceHttpRequestChunkRequest { + /** + * Matches the requestId from the originating httpRequestStart frame. + */ + requestId: string; + /** + * Body byte range. UTF-8 text when `binary` is absent or false; base64-encoded bytes when `binary` is true. May be empty. + */ + data: string; + /** + * When true, `data` is base64-encoded bytes. When absent or false, `data` is UTF-8 text. + */ + binary?: boolean; + /** + * When true, this is the final body chunk for the request. The SDK may rely on having received an end-marked chunk before treating the request body as complete. + */ + end?: boolean; + /** + * When true, the runtime is cancelling the in-flight request (e.g. upstream consumer aborted). `data` is ignored. Implies end-of-request. + */ + cancel?: boolean; + /** + * Optional human-readable reason for the cancellation, propagated for logging. + */ + cancelReason?: string; +} +/** + * Acknowledgement. The SDK is free to ignore the ack and treat chunk delivery as fire-and-forget. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "LlmInferenceHttpRequestChunkResult". + */ +export interface LlmInferenceHttpRequestChunkResult {} +/** + * The head of an outbound model-layer HTTP request. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "LlmInferenceHttpRequestStartRequest". + */ +export interface LlmInferenceHttpRequestStartRequest { + /** + * Opaque runtime-minted id, unique per in-flight request. The SDK uses this to correlate httpRequestChunk frames and to address its httpResponseStart / httpResponseChunk replies back to the runtime. + */ + requestId: string; + /** + * Id of the runtime session that triggered this request, when one is in scope. Absent for requests issued outside any session (e.g. startup model-catalog or capability resolution). This is a payload field — not a dispatch key — because the client-global API is registered process-wide rather than per session. + */ + sessionId?: string; + /** + * HTTP method, e.g. GET, POST. + */ + method: string; + /** + * Absolute request URL. + */ + url: string; + headers: LlmInferenceHeaders; + transport?: LlmInferenceHttpRequestStartTransport; +} +/** + * Acknowledgement. Returning successfully simply means the SDK accepted the start frame; it does not imply the request will succeed. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "LlmInferenceHttpRequestStartResult". + */ +export interface LlmInferenceHttpRequestStartResult {} +/** + * Set to terminate the response with a transport-level failure. Implies end-of-stream; any further chunks for this requestId are ignored. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "LlmInferenceHttpResponseChunkError". + */ +/** @experimental */ +export interface LlmInferenceHttpResponseChunkError { + /** + * Human-readable failure description. + */ + message: string; + /** + * Optional machine-readable error code. + */ + code?: string; +} +/** + * A response body chunk or terminal error. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "LlmInferenceHttpResponseChunkRequest". + */ +/** @experimental */ +export interface LlmInferenceHttpResponseChunkRequest { + /** + * Matches the requestId from the originating httpRequestStart frame. + */ + requestId: string; + /** + * Body byte range. UTF-8 text when `binary` is absent or false; base64-encoded bytes when `binary` is true. May be empty (e.g. when the response body is empty: send a single chunk with empty data and end=true). + */ + data: string; + /** + * When true, `data` is base64-encoded bytes. When absent or false, `data` is UTF-8 text. + */ + binary?: boolean; + /** + * When true, this is the final body chunk for the response. The runtime treats the response body as complete after receiving an end-marked chunk. + */ + end?: boolean; + error?: LlmInferenceHttpResponseChunkError; +} +/** + * Whether the chunk was accepted. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "LlmInferenceHttpResponseChunkResult". + */ +/** @experimental */ +export interface LlmInferenceHttpResponseChunkResult { + /** + * True when the chunk was matched to a pending request; false when unknown. + */ + accepted: boolean; +} +/** + * Response head. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "LlmInferenceHttpResponseStartRequest". + */ +/** @experimental */ +export interface LlmInferenceHttpResponseStartRequest { + /** + * Matches the requestId from the originating httpRequestStart frame. + */ + requestId: string; + /** + * HTTP status code. + */ + status: number; + /** + * Optional HTTP status reason phrase. + */ + statusText?: string; + headers: LlmInferenceHeaders; +} +/** + * Whether the start frame was accepted. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "LlmInferenceHttpResponseStartResult". + */ +/** @experimental */ +export interface LlmInferenceHttpResponseStartResult { + /** + * True when the response start was matched to a pending request; false when unknown. + */ + accepted: boolean; +} +/** + * Indicates whether the calling client was registered as the LLM inference provider. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "LlmInferenceSetProviderResult". + */ +/** @experimental */ +export interface LlmInferenceSetProviderResult { + /** + * Whether the provider was set successfully + */ + success: boolean; +} /** * Schema for the `LocalSessionMetadataValue` type. * @@ -4944,7 +5170,6 @@ export interface McpServerAuthConfigRedirectPort { * Fixed port for the OAuth redirect callback server. */ redirectPort?: number; - [k: string]: unknown | undefined; } /** * Remote MCP server configuration accessed over HTTP or SSE. @@ -5153,7 +5378,9 @@ export interface McpExecuteSamplingParams { /** * The original MCP JSON-RPC request ID (string or number). Used by the runtime to correlate the inference with the originating MCP request for telemetry; this is distinct from `requestId` (which is the schema-level cancellation handle). */ - mcpRequestId: string | number; + mcpRequestId: { + [k: string]: unknown | undefined; + }; request: McpExecuteSamplingRequest; } /** @@ -5341,6 +5568,33 @@ export interface McpTools { */ description?: string; } +/** + * Pending MCP OAuth request ID and host-provided token or cancellation response. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "McpOauthHandlePendingRequest". + */ +/** @experimental */ +export interface McpOauthHandlePendingRequest { + /** + * OAuth request identifier from the mcp.oauth_required event + */ + requestId: string; + result: McpOauthPendingRequestResponse; +} +/** + * Indicates whether the pending MCP OAuth response was accepted. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "McpOauthHandlePendingResult". + */ +/** @experimental */ +export interface McpOauthHandlePendingResult { + /** + * Whether the response was accepted. False if the request was unknown, timed out, or already resolved. + */ + success: boolean; +} /** * Remote MCP server name and optional overrides controlling reauthentication, OAuth client display name, and the callback success-page copy. * @@ -5996,17 +6250,29 @@ export interface ModelBillingTokenPrices { */ outputPrice?: number; /** - * AI Credits cost per billing batch of cached tokens + * Deprecated: use cacheReadPrice. AI Credits cost per billing batch of cached tokens */ cachePrice?: number; + /** + * AI Credits cost per billing batch of cached (read) tokens + */ + cacheReadPrice?: number; + /** + * AI Credits cost per billing batch of cache-write (cache creation) tokens. + */ + cacheWritePrice?: number; /** * Number of tokens per standard billing batch */ batchSize?: number; /** - * Prompt token budget (max_prompt_tokens) for the default tier. The total context window is this value plus the model's max_output_tokens. + * Deprecated: use maxPromptTokens. Prompt token budget for the default tier. The total context window is this value plus the model's max_output_tokens. */ contextMax?: number; + /** + * Prompt token budget for the default tier. The total context window is this value plus the model's max_output_tokens. + */ + maxPromptTokens?: number; longContext?: ModelBillingTokenPricesLongContext; } /** @@ -6025,13 +6291,25 @@ export interface ModelBillingTokenPricesLongContext { */ outputPrice?: number; /** - * AI Credits cost per billing batch of cached tokens + * Deprecated: use cacheReadPrice. AI Credits cost per billing batch of cached tokens */ cachePrice?: number; /** - * Prompt token budget (max_prompt_tokens) for the long context tier. The total context window is this value plus the model's max_output_tokens. + * AI Credits cost per billing batch of cached (read) tokens + */ + cacheReadPrice?: number; + /** + * AI Credits cost per billing batch of cache-write (cache creation) tokens. + */ + cacheWritePrice?: number; + /** + * Deprecated: use maxPromptTokens. Prompt token budget for the long context tier. The total context window is this value plus the model's max_output_tokens. */ contextMax?: number; + /** + * Prompt token budget for the long context tier. The total context window is this value plus the model's max_output_tokens. + */ + maxPromptTokens?: number; } /** * Optional capability overrides (vision, tool_calls, reasoning, etc.). @@ -6313,9 +6591,8 @@ export interface NameSetRequest { /** @experimental */ export interface OptionsUpdateAdditionalContentExclusionPolicy { rules: OptionsUpdateAdditionalContentExclusionPolicyRule[]; - last_updated_at: string | number; + last_updated_at: unknown; scope: OptionsUpdateAdditionalContentExclusionPolicyScope; - [k: string]: unknown | undefined; } /** * Schema for the `OptionsUpdateAdditionalContentExclusionPolicyRule` type. @@ -6329,7 +6606,6 @@ export interface OptionsUpdateAdditionalContentExclusionPolicyRule { ifAnyMatch?: string[]; ifNoneMatch?: string[]; source: OptionsUpdateAdditionalContentExclusionPolicyRuleSource; - [k: string]: unknown | undefined; } /** * Schema for the `OptionsUpdateAdditionalContentExclusionPolicyRuleSource` type. @@ -7325,9 +7601,8 @@ export interface PermissionRulesSet { /** @experimental */ export interface PermissionsConfigureAdditionalContentExclusionPolicy { rules: PermissionsConfigureAdditionalContentExclusionPolicyRule[]; - last_updated_at: string | number; + last_updated_at: unknown; scope: PermissionsConfigureAdditionalContentExclusionPolicyScope; - [k: string]: unknown | undefined; } /** * Schema for the `PermissionsConfigureAdditionalContentExclusionPolicyRule` type. @@ -7341,7 +7616,6 @@ export interface PermissionsConfigureAdditionalContentExclusionPolicyRule { ifAnyMatch?: string[]; ifNoneMatch?: string[]; source: PermissionsConfigureAdditionalContentExclusionPolicyRuleSource; - [k: string]: unknown | undefined; } /** * Schema for the `PermissionsConfigureAdditionalContentExclusionPolicyRuleSource` type. @@ -9687,7 +9961,7 @@ export interface SessionFsSqliteQueryRequest { * Optional named bind parameters */ params?: { - [k: string]: (string | number | null) | undefined; + [k: string]: unknown | undefined; }; } /** @@ -10163,6 +10437,12 @@ export interface SessionOpenOptions { * Whether model responses stream as delta events. */ enableStreaming?: boolean; + /** + * Experimental: enable native model citations (Anthropic models today), normalized onto the `assistant.message` event. Off by default; may change or be removed while the citations surface is experimental. + * + * @experimental + */ + enableCitations?: boolean; /** * Override URL for the Copilot API endpoint. */ @@ -10221,9 +10501,8 @@ export interface SessionOpenOptions { /** @experimental */ export interface SessionOpenOptionsAdditionalContentExclusionPolicy { rules: SessionOpenOptionsAdditionalContentExclusionPolicyRule[]; - last_updated_at: string | number; + last_updated_at: unknown; scope: SessionOpenOptionsAdditionalContentExclusionPolicyScope; - [k: string]: unknown | undefined; } /** * Schema for the `SessionOpenOptionsAdditionalContentExclusionPolicyRule` type. @@ -10237,7 +10516,6 @@ export interface SessionOpenOptionsAdditionalContentExclusionPolicyRule { ifAnyMatch?: string[]; ifNoneMatch?: string[]; source: SessionOpenOptionsAdditionalContentExclusionPolicyRuleSource; - [k: string]: unknown | undefined; } /** * Schema for the `SessionOpenOptionsAdditionalContentExclusionPolicyRuleSource` type. @@ -13573,6 +13851,34 @@ export function createServerRpc(connection: MessageConnection) { connection.sendRequest("sessionFs.setProvider", params), }, /** @experimental */ + llmInference: { + /** + * Registers an SDK client as the LLM inference callback provider. + * + * @returns Indicates whether the calling client was registered as the LLM inference provider. + */ + setProvider: async (): Promise => + connection.sendRequest("llmInference.setProvider", {}), + /** + * Delivers the response head (status + headers) for an in-flight request, correlated by the requestId the runtime supplied in httpRequestStart. Must be called exactly once per request before any httpResponseChunk frames. + * + * @param params Response head. + * + * @returns Whether the start frame was accepted. + */ + httpResponseStart: async (params: LlmInferenceHttpResponseStartRequest): Promise => + connection.sendRequest("llmInference.httpResponseStart", params), + /** + * Delivers a body byte range (or a terminal transport error) for an in-flight response, correlated by requestId. Set `end` true on the last chunk. When `error` is set the response terminates with a transport-level failure and the runtime raises an APIConnectionError. + * + * @param params A response body chunk or terminal error. + * + * @returns Whether the chunk was accepted. + */ + httpResponseChunk: async (params: LlmInferenceHttpResponseChunkRequest): Promise => + connection.sendRequest("llmInference.httpResponseChunk", params), + }, + /** @experimental */ sessions: { /** * Creates or resumes a local session and returns the opened session ID. @@ -14439,6 +14745,15 @@ export function createSessionRpc(connection: MessageConnection, sessionId: strin connection.sendRequest("session.mcp.isServerRunning", { sessionId, ...params }), /** @experimental */ oauth: { + /** + * Resolves a pending MCP OAuth request with a host-provided token or cancellation. The pending request is emitted as mcp.oauth_required with the data necessary to authorize the request. + * + * @param params Pending MCP OAuth request ID and host-provided token or cancellation response. + * + * @returns Indicates whether the pending MCP OAuth response was accepted. + */ + handlePendingRequest: async (params: McpOauthHandlePendingRequest): Promise => + connection.sendRequest("session.mcp.oauth.handlePendingRequest", { sessionId, ...params }), /** * Starts OAuth authentication for a remote MCP server. * @@ -15304,7 +15619,7 @@ export function createInternalSessionRpc(connection: MessageConnection, sessionI /** @experimental */ oauth: { /** - * Responds to a pending MCP OAuth provider request. Marked internal because the `provider` argument is an in-process OAuthClientProvider instance that cannot be carried over the wire; the public OAuth surface will route the response through a wire-clean handshake once the CLI moves on top of the SDK. + * Responds to a pending MCP OAuth request with an in-process provider. This internal CLI-only API accepts a live OAuthClientProvider instance and cannot be used over the SDK JSON-RPC boundary. Use session.mcp.oauth.handlePendingRequest instead for the public SDK-safe response path. * * @param params MCP OAuth request id and optional provider response. * diff --git a/nodejs/src/generated/session-events.ts b/nodejs/src/generated/session-events.ts index 96f871783..cd74a7b48 100644 --- a/nodejs/src/generated/session-events.ts +++ b/nodejs/src/generated/session-events.ts @@ -212,6 +212,14 @@ export type Attachment = | AttachmentGitHubReference | AttachmentBlob | AttachmentExtensionContext; +/** + * Why the binary data is absent: it exceeded the inline size limit, or its asset was unavailable + */ +export type OmittedBinaryOmittedReason = + /** Bytes exceeded the session's inline size limit. */ + | "too_large" + /** The referenced binary asset could not be found (e.g. a truncated log). */ + | "asset_unavailable"; /** * Type of GitHub reference */ @@ -222,6 +230,22 @@ export type AttachmentGitHubReferenceType = | "pr" /** GitHub discussion reference. */ | "discussion"; +/** + * The system that produced a citation. + */ +/** @experimental */ +export type CitationProvider = + /** Citation produced by an Anthropic (Claude) model response. */ + | "anthropic" + /** Citation produced by an OpenAI model response. */ + | "openai" + /** Citation synthesized client-side by the runtime from tool output. */ + | "client"; +/** + * Location within a cited source (character, page, or content-block range) that supports a span. + */ +/** @experimental */ +export type CitationLocation = CitationLocationChar | CitationLocationPage | CitationLocationBlock; /** * Tool call type: "function" for standard tool calls, "custom" for grammar-based tool calls. Defaults to "function" when absent. */ @@ -242,6 +266,14 @@ export type AssistantUsageApiEndpoint = | "/responses" /** WebSocket Responses API endpoint. */ | "ws:/responses"; +/** + * For HTTP 400 failures only: whether the response carried a structured CAPI error envelope (structured_error, a deterministic validation failure) or no error body (bodyless, the transient gateway/proxy signature). Absent for non-400 failures. + */ +export type ModelCallFailureBadRequestKind = + /** The 400 response carried no error body (transient gateway/proxy signature). */ + | "bodyless" + /** The 400 response carried a structured CAPI error envelope (deterministic validation failure). */ + | "structured_error"; /** * Where the failed model call originated */ @@ -283,14 +315,6 @@ export type PersistedBinaryImageType = | "image" /** Other binary resource data. */ | "resource"; -/** - * Why the binary data is absent: it exceeded the inline size limit, or its asset was unavailable - */ -export type OmittedBinaryOmittedReason = - /** Bytes exceeded the session's inline size limit. */ - | "too_large" - /** The referenced binary asset could not be found (e.g. a truncated log). */ - | "asset_unavailable"; /** * Binary result type discriminator. Use "image" for images and "resource" for other binary data. */ @@ -480,21 +504,13 @@ export type ElicitationCompletedAction = /** The user dismissed the request. */ | "cancel"; /** - * Schema for the `ElicitationCompletedContent` type. - */ -export type ElicitationCompletedContent = (string | number | boolean | string[]) | undefined; -/** - * Source-defined JSON payload for the custom notification + * How the pending MCP OAuth request was completed */ -export type CustomNotificationPayload = - | string - | number - | boolean - | null - | unknown[] - | { - [k: string]: unknown | undefined; - }; +export type McpOauthCompletionOutcome = + /** The request completed with a token-backed OAuth provider. */ + | "token" + /** The request completed without an OAuth provider. */ + | "cancelled"; /** * The user's auto-mode-switch choice */ @@ -2292,11 +2308,24 @@ export interface UserMessageData { * File attachment */ export interface AttachmentFile { + /** + * Internal: content-addressed id of the session.binary_asset event holding this attachment's model-facing bytes (e.g. "sha256:..."). Absent externally. + */ + assetId?: string; + /** + * Internal: decoded byte length of the attachment's model-facing bytes. Absent externally. + */ + byteLength?: number; /** * User-facing display name for the attachment */ displayName: string; lineRange?: AttachmentFileLineRange; + /** + * Internal: MIME type of the file's model-facing bytes (post-resize for images). Set when the file's bytes are interned to an asset. Absent externally. + */ + mimeType?: string; + omittedReason?: OmittedBinaryOmittedReason; /** * Absolute file path */ @@ -2422,9 +2451,17 @@ export interface AttachmentGitHubReference { */ export interface AttachmentBlob { /** - * Base64-encoded content + * Internal: content-addressed id of the session.binary_asset event holding this attachment's model-facing bytes (e.g. "sha256:..."). Absent externally. */ - data: string; + assetId?: string; + /** + * Internal: decoded byte length of the attachment's model-facing bytes. Absent externally. + */ + byteLength?: number; + /** + * Base64-encoded content. Present on input and for external consumers; replaced by an internal `assetId` reference in persisted events when interned to a content-addressed asset. + */ + data?: string; /** * User-facing display name for the attachment */ @@ -2433,6 +2470,7 @@ export interface AttachmentBlob { * MIME type of the inline data */ mimeType: string; + omittedReason?: OmittedBinaryOmittedReason; /** * Attachment type discriminator */ @@ -2752,6 +2790,12 @@ export interface AssistantMessageData { * Provider's completion / response identifier; shared across all chunks of a single API call. Used to group multi-chunk assistant utterances. */ apiCallId?: string; + /** + * Provider-agnostic citations linking spans of this message's content to the sources that support them. Experimental; only populated when citation emission is enabled. + * + * @experimental + */ + citations?: Citations; /** * The assistant's text response content */ @@ -2811,6 +2855,136 @@ export interface AssistantMessageData { */ turnId?: string; } +/** + * Provider-agnostic citations linking spans of the assistant's response to their supporting sources. + */ +/** @experimental */ +export interface Citations { + /** + * Deduplicated set of sources referenced by the citation spans. + */ + sources: CitationSource[]; + /** + * Spans of generated text annotated with the sources that support them. + */ + spans: CitationSpan[]; +} +/** + * A source that backs one or more cited spans in the assistant's response. + */ +/** @experimental */ +export interface CitationSource { + /** + * Stable, turn-scoped identifier for this source, referenced by CitationReference.sourceId. + */ + id: string; + /** + * File path relative to the agent's workspace root, when the source is a file. + */ + path?: string; + provider: CitationProvider; + /** + * Human-readable title of the source. + */ + title?: string; + /** + * URL of the source, when it is a web resource. + */ + url?: string; +} +/** + * A contiguous span of generated assistant text and the source references that support it. + */ +/** @experimental */ +export interface CitationSpan { + /** + * End offset of the cited span within the final assistant message content (UTF-16 code units, zero-based, exclusive). + */ + endIndex: number; + /** + * The sources that support this span of generated text. + */ + references: CitationReference[]; + /** + * Start offset of the cited span within the final assistant message content (UTF-16 code units, zero-based, inclusive). + */ + startIndex: number; +} +/** + * A single citation occurrence linking a span of generated text to a supporting source. + */ +/** @experimental */ +export interface CitationReference { + /** + * The exact text from the source that supports the cited span, when provided by the model. + */ + citedText?: string; + location?: CitationLocation; + /** + * Provider-native citation correlation data (e.g. Anthropic search_result_index / document_index), passed through opaquely for debugging and forward compatibility. + */ + providerMetadata?: { + [k: string]: unknown | undefined; + }; + /** + * Identifier of the CitationSource this reference points to (CitationSource.id). + */ + sourceId: string; +} +/** + * A character range within the source's text content. + */ +/** @experimental */ +export interface CitationLocationChar { + /** + * End character offset within the source text (zero-based, exclusive). + */ + endIndex: number; + /** + * Start character offset within the source text (zero-based, inclusive). + */ + startIndex: number; + /** + * Citation location type discriminator + */ + type: "char"; +} +/** + * A page range within a paginated source document. + */ +/** @experimental */ +export interface CitationLocationPage { + /** + * Last page number of the cited range (inclusive). + */ + endPage: number; + /** + * First page number of the cited range. + */ + startPage: number; + /** + * Citation location type discriminator + */ + type: "page"; +} +/** + * A content-block range within a structured source document. + */ +/** @experimental */ +export interface CitationLocationBlock { + /** + * Index of the last content block of the cited range (zero-based, exclusive). + */ + endBlock: number; + /** + * Index of the first content block of the cited range (zero-based, inclusive). + */ + startBlock: number; + /** + * Citation location type discriminator + */ + type: "block"; +} /** * Neutral provider-tagged server-side tool-use payload (tool search, advisor) for verbatim round-tripping */ @@ -3238,14 +3412,23 @@ export interface ModelCallFailureData { * Completion ID from the model provider (e.g., chatcmpl-abc123) */ apiCallId?: string; + badRequestKind?: ModelCallFailureBadRequestKind; /** * Duration of the failed API call in milliseconds */ durationMs?: number; + /** + * For HTTP 400 failures only: the `code` from the CAPI error envelope (e.g. 'model_max_prompt_tokens_exceeded') identifying which deterministic validation failure occurred. Raw server-controlled string, emitted only through restricted telemetry. Absent for bodyless or non-400 failures. + */ + errorCode?: string; /** * Raw provider/runtime error message for restricted telemetry */ errorMessage?: string; + /** + * For HTTP 400 failures only: the `type` from the CAPI error envelope (e.g. 'websocket_error'), a coarser companion to errorCode for envelopes that carry no code. Raw server-controlled string, emitted only through restricted telemetry. Absent for bodyless or non-400 failures. + */ + errorType?: string; /** * What initiated this API call (e.g., "sub-agent", "mcp-sampling"); absent for user-initiated calls */ @@ -3647,6 +3830,12 @@ export interface ToolExecutionCompleteResult { * @experimental */ binaryResultsForLlm?: PersistedBinaryResult[]; + /** + * Provider-neutral source material this tool makes available to the model as citable content. Persisted so it survives session resume. Experimental. + * + * @experimental + */ + citableSources?: CitableSource[]; /** * Concise tool result text sent to the LLM for chat completion, potentially truncated for token efficiency */ @@ -3746,6 +3935,32 @@ export interface BinaryAssetReference { mimeType: string; type: BinaryAssetReferenceType; } +/** + * A source supplied by a tool that should be made available to the model as citable content. + */ +/** @experimental */ +export interface CitableSource { + /** + * The source text made available to the model as citable content. + */ + content: string; + /** + * Stable identifier for this source within the tool result. Used for deduplication and may be used by future provider integrations to correlate response citations back to the originating source. + */ + id: string; + /** + * File path relative to the agent's workspace root, when the source is a file. + */ + path?: string; + /** + * Human-readable title of the source. + */ + title?: string; + /** + * URL of the source, when it is a web resource. + */ + url?: string; +} /** * Plain text content block */ @@ -3971,27 +4186,19 @@ export interface ToolExecutionCompleteUIResourceMetaUIPermissions { /** * Schema for the `ToolExecutionCompleteUIResourceMetaUIPermissionsCamera` type. */ -export interface ToolExecutionCompleteUIResourceMetaUIPermissionsCamera { - [k: string]: unknown | undefined; -} +export interface ToolExecutionCompleteUIResourceMetaUIPermissionsCamera {} /** * Schema for the `ToolExecutionCompleteUIResourceMetaUIPermissionsClipboardWrite` type. */ -export interface ToolExecutionCompleteUIResourceMetaUIPermissionsClipboardWrite { - [k: string]: unknown | undefined; -} +export interface ToolExecutionCompleteUIResourceMetaUIPermissionsClipboardWrite {} /** * Schema for the `ToolExecutionCompleteUIResourceMetaUIPermissionsGeolocation` type. */ -export interface ToolExecutionCompleteUIResourceMetaUIPermissionsGeolocation { - [k: string]: unknown | undefined; -} +export interface ToolExecutionCompleteUIResourceMetaUIPermissionsGeolocation {} /** * Schema for the `ToolExecutionCompleteUIResourceMetaUIPermissionsMicrophone` type. */ -export interface ToolExecutionCompleteUIResourceMetaUIPermissionsMicrophone { - [k: string]: unknown | undefined; -} +export interface ToolExecutionCompleteUIResourceMetaUIPermissionsMicrophone {} /** * Tool definition metadata, present for MCP tools with MCP Apps support */ @@ -5260,7 +5467,12 @@ export interface PermissionPromptRequestRead { * MCP tool invocation permission prompt */ export interface PermissionPromptRequestMcp { - args?: unknown; + /** + * Arguments to pass to the MCP tool + */ + args?: { + [k: string]: unknown | undefined; + }; /** * Prompt kind discriminator */ @@ -5878,7 +6090,6 @@ export interface ElicitationRequestedData { * URL to open in the user's browser (url mode only) */ url?: string; - [k: string]: unknown | undefined; } /** * JSON Schema describing the form fields to present to the user (form mode only) @@ -5945,6 +6156,12 @@ export interface ElicitationCompletedData { */ requestId: string; } +/** + * Schema for the `ElicitationCompletedContent` type. + */ +export interface ElicitationCompletedContent { + [k: string]: unknown | undefined; +} /** * Session event "sampling.requested". Sampling request from an MCP server; contains the server name and a requestId for correlation */ @@ -5982,7 +6199,9 @@ export interface SamplingRequestedData { /** * The JSON-RPC request ID from the MCP protocol */ - mcpRequestId: string | number; + mcpRequestId: { + [k: string]: unknown | undefined; + }; /** * Unique identifier for this sampling request; used to respond via session.respondToSampling() */ @@ -5991,7 +6210,6 @@ export interface SamplingRequestedData { * Name of the MCP server that initiated the sampling request */ serverName: string; - [k: string]: unknown | undefined; } /** * Session event "sampling.completed". Sampling request completion notification signaling UI dismissal @@ -6067,9 +6285,13 @@ export interface McpOauthRequiredEvent { */ export interface McpOauthRequiredData { /** - * Unique identifier for this OAuth request; used to respond via session.respondToMcpOAuth() + * Unique identifier for this OAuth request; used to respond via session.mcp.oauth.handlePendingRequest */ requestId: string; + /** + * Raw OAuth protected-resource metadata document fetched for the MCP server, if available + */ + resourceMetadata?: string; /** * Display name of the MCP server that requires OAuth */ @@ -6079,6 +6301,7 @@ export interface McpOauthRequiredData { */ serverUrl: string; staticClientConfig?: McpOauthRequiredStaticClientConfig; + wwwAuthenticateParams?: McpOauthWWWAuthenticateParams; } /** * Static OAuth client configuration, if the server specifies one @@ -6097,6 +6320,23 @@ export interface McpOauthRequiredStaticClientConfig { */ publicClient?: boolean; } +/** + * OAuth WWW-Authenticate parameters parsed from an MCP auth challenge + */ +export interface McpOauthWWWAuthenticateParams { + /** + * OAuth error from the WWW-Authenticate error parameter, if present + */ + error?: string; + /** + * Protected resource metadata URL from the WWW-Authenticate resource_metadata parameter + */ + resourceMetadataUrl: string; + /** + * Requested OAuth scopes from the WWW-Authenticate scope parameter, if present + */ + scope?: string; +} /** * Session event "mcp.oauth_completed". MCP OAuth request completion notification */ @@ -6131,6 +6371,7 @@ export interface McpOauthCompletedEvent { * MCP OAuth request completion notification */ export interface McpOauthCompletedData { + outcome: McpOauthCompletionOutcome; /** * Request ID of the resolved OAuth request */ @@ -6185,6 +6426,12 @@ export interface CustomNotificationData { */ version?: number; } +/** + * Source-defined JSON payload for the custom notification + */ +export interface CustomNotificationPayload { + [k: string]: unknown | undefined; +} /** * Optional source-defined string identifiers describing the payload subject */ @@ -7443,7 +7690,6 @@ export interface McpAppToolCallCompleteError { */ export interface McpAppToolCallCompleteToolMeta { ui?: McpAppToolCallCompleteToolMetaUI; - [k: string]: unknown | undefined; } /** * Schema for the `McpAppToolCallCompleteToolMetaUI` type. @@ -7457,5 +7703,4 @@ export interface McpAppToolCallCompleteToolMetaUI { * Tool visibility per SEP-1865 (typically a subset of `["model","app"]`) */ visibility?: string[]; - [k: string]: unknown | undefined; } diff --git a/nodejs/src/sessionFsProvider.ts b/nodejs/src/sessionFsProvider.ts index 7e959849e..0bc198750 100644 --- a/nodejs/src/sessionFsProvider.ts +++ b/nodejs/src/sessionFsProvider.ts @@ -96,7 +96,7 @@ export interface SessionFsProvider { } function normalizeSqliteParams( - params?: Record + params?: Record ): Record | undefined { if (!params) { return undefined; @@ -105,7 +105,7 @@ function normalizeSqliteParams( const normalized: Record = {}; for (const [key, value] of Object.entries(params)) { if (value !== undefined) { - normalized[key] = value; + normalized[key] = value as string | number | null; } } return normalized; diff --git a/nodejs/test/e2e/harness/sdkTestContext.ts b/nodejs/test/e2e/harness/sdkTestContext.ts index d7eff3d59..cd6494cad 100644 --- a/nodejs/test/e2e/harness/sdkTestContext.ts +++ b/nodejs/test/e2e/harness/sdkTestContext.ts @@ -53,6 +53,11 @@ export async function createSdkTestContext({ ...process.env, ...openAiEndpoint.getProxyEnv(), COPILOT_API_URL: proxyUrl, + // Route GitHub API calls (e.g. the MCP registry policy check) to the + // replay proxy so MCP enablement stays hermetic. Without this the CLI + // reaches the real api.github.com, which is slow/unreachable on macOS + // CI runners and makes MCP servers time out before reaching connected. + COPILOT_DEBUG_GITHUB_API_URL: proxyUrl, COPILOT_HOME: copilotHomeDir, COPILOT_SDK_AUTH_TOKEN: "", GH_CONFIG_DIR: homeDir, diff --git a/nodejs/test/e2e/permissions.e2e.test.ts b/nodejs/test/e2e/permissions.e2e.test.ts index 96a470aee..5ef9206d1 100644 --- a/nodejs/test/e2e/permissions.e2e.test.ts +++ b/nodejs/test/e2e/permissions.e2e.test.ts @@ -564,12 +564,10 @@ describe("Permission callbacks", async () => { ).success ).toBe(true); expect( - (await session.rpc.permissions.urls.setUnrestrictedMode({ unrestricted: true })) - .success + (await session.rpc.permissions.urls.setUnrestrictedMode({ enabled: true })).success ).toBe(true); expect( - (await session.rpc.permissions.urls.setUnrestrictedMode({ unrestricted: false })) - .success + (await session.rpc.permissions.urls.setUnrestrictedMode({ enabled: false })).success ).toBe(true); } finally { await session.disconnect(); diff --git a/python/copilot/generated/rpc.py b/python/copilot/generated/rpc.py index 6822fd7ff..de42808b4 100644 --- a/python/copilot/generated/rpc.py +++ b/python/copilot/generated/rpc.py @@ -1864,6 +1864,212 @@ def to_dict(self) -> dict: result["projectPaths"] = from_union([lambda x: from_list(from_str, x), from_none], self.project_paths) return result +@dataclass +class LlmInferenceHTTPRequestChunkRequest: + """A request body chunk or cancellation signal.""" + + data: str + """Body byte range. UTF-8 text when `binary` is absent or false; base64-encoded bytes when + `binary` is true. May be empty. + """ + request_id: str + """Matches the requestId from the originating httpRequestStart frame.""" + + binary: bool | None = None + """When true, `data` is base64-encoded bytes. When absent or false, `data` is UTF-8 text.""" + + cancel: bool | None = None + """When true, the runtime is cancelling the in-flight request (e.g. upstream consumer + aborted). `data` is ignored. Implies end-of-request. + """ + cancel_reason: str | None = None + """Optional human-readable reason for the cancellation, propagated for logging.""" + + end: bool | None = None + """When true, this is the final body chunk for the request. The SDK may rely on having + received an end-marked chunk before treating the request body as complete. + """ + + @staticmethod + def from_dict(obj: Any) -> 'LlmInferenceHTTPRequestChunkRequest': + assert isinstance(obj, dict) + data = from_str(obj.get("data")) + request_id = from_str(obj.get("requestId")) + binary = from_union([from_bool, from_none], obj.get("binary")) + cancel = from_union([from_bool, from_none], obj.get("cancel")) + cancel_reason = from_union([from_str, from_none], obj.get("cancelReason")) + end = from_union([from_bool, from_none], obj.get("end")) + return LlmInferenceHTTPRequestChunkRequest(data, request_id, binary, cancel, cancel_reason, end) + + def to_dict(self) -> dict: + result: dict = {} + result["data"] = from_str(self.data) + result["requestId"] = from_str(self.request_id) + if self.binary is not None: + result["binary"] = from_union([from_bool, from_none], self.binary) + if self.cancel is not None: + result["cancel"] = from_union([from_bool, from_none], self.cancel) + if self.cancel_reason is not None: + result["cancelReason"] = from_union([from_str, from_none], self.cancel_reason) + if self.end is not None: + result["end"] = from_union([from_bool, from_none], self.end) + return result + +@dataclass +class LlmInferenceHTTPRequestChunkResult: + """Acknowledgement. The SDK is free to ignore the ack and treat chunk delivery as + fire-and-forget. + """ + @staticmethod + def from_dict(obj: Any) -> 'LlmInferenceHTTPRequestChunkResult': + assert isinstance(obj, dict) + return LlmInferenceHTTPRequestChunkResult() + + def to_dict(self) -> dict: + result: dict = {} + return result + +class LlmInferenceHTTPRequestStartTransport(Enum): + """Transport the runtime would otherwise use for this request. `http` (the default when + absent) covers plain HTTP and SSE responses; `websocket` indicates a full-duplex message + channel where each body chunk maps to one WebSocket message and the `binary` flag + distinguishes text from binary frames. The SDK consumer uses this to decide whether to + service the request with an HTTP client or a WebSocket client. It is the one piece of + request metadata the consumer cannot reliably infer from the URL or headers alone. + """ + HTTP = "http" + WEBSOCKET = "websocket" + +@dataclass +class LlmInferenceHTTPRequestStartResult: + """Acknowledgement. Returning successfully simply means the SDK accepted the start frame; it + does not imply the request will succeed. + """ + @staticmethod + def from_dict(obj: Any) -> 'LlmInferenceHTTPRequestStartResult': + assert isinstance(obj, dict) + return LlmInferenceHTTPRequestStartResult() + + def to_dict(self) -> dict: + result: dict = {} + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class LlmInferenceHTTPResponseChunkError: + """Set to terminate the response with a transport-level failure. Implies end-of-stream; any + further chunks for this requestId are ignored. + """ + message: str + """Human-readable failure description.""" + + code: str | None = None + """Optional machine-readable error code.""" + + @staticmethod + def from_dict(obj: Any) -> 'LlmInferenceHTTPResponseChunkError': + assert isinstance(obj, dict) + message = from_str(obj.get("message")) + code = from_union([from_str, from_none], obj.get("code")) + return LlmInferenceHTTPResponseChunkError(message, code) + + def to_dict(self) -> dict: + result: dict = {} + result["message"] = from_str(self.message) + if self.code is not None: + result["code"] = from_union([from_str, from_none], self.code) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class LlmInferenceHTTPResponseChunkResult: + """Whether the chunk was accepted.""" + + accepted: bool + """True when the chunk was matched to a pending request; false when unknown.""" + + @staticmethod + def from_dict(obj: Any) -> 'LlmInferenceHTTPResponseChunkResult': + assert isinstance(obj, dict) + accepted = from_bool(obj.get("accepted")) + return LlmInferenceHTTPResponseChunkResult(accepted) + + def to_dict(self) -> dict: + result: dict = {} + result["accepted"] = from_bool(self.accepted) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class LlmInferenceHTTPResponseStartRequest: + """Response head.""" + + headers: dict[str, list[str]] + request_id: str + """Matches the requestId from the originating httpRequestStart frame.""" + + status: int + """HTTP status code.""" + + status_text: str | None = None + """Optional HTTP status reason phrase.""" + + @staticmethod + def from_dict(obj: Any) -> 'LlmInferenceHTTPResponseStartRequest': + assert isinstance(obj, dict) + headers = from_dict(lambda x: from_list(from_str, x), obj.get("headers")) + request_id = from_str(obj.get("requestId")) + status = from_int(obj.get("status")) + status_text = from_union([from_str, from_none], obj.get("statusText")) + return LlmInferenceHTTPResponseStartRequest(headers, request_id, status, status_text) + + def to_dict(self) -> dict: + result: dict = {} + result["headers"] = from_dict(lambda x: from_list(from_str, x), self.headers) + result["requestId"] = from_str(self.request_id) + result["status"] = from_int(self.status) + if self.status_text is not None: + result["statusText"] = from_union([from_str, from_none], self.status_text) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class LlmInferenceHTTPResponseStartResult: + """Whether the start frame was accepted.""" + + accepted: bool + """True when the response start was matched to a pending request; false when unknown.""" + + @staticmethod + def from_dict(obj: Any) -> 'LlmInferenceHTTPResponseStartResult': + assert isinstance(obj, dict) + accepted = from_bool(obj.get("accepted")) + return LlmInferenceHTTPResponseStartResult(accepted) + + def to_dict(self) -> dict: + result: dict = {} + result["accepted"] = from_bool(self.accepted) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class LlmInferenceSetProviderResult: + """Indicates whether the calling client was registered as the LLM inference provider.""" + + success: bool + """Whether the provider was set successfully""" + + @staticmethod + def from_dict(obj: Any) -> 'LlmInferenceSetProviderResult': + assert isinstance(obj, dict) + success = from_bool(obj.get("success")) + return LlmInferenceSetProviderResult(success) + + def to_dict(self) -> dict: + result: dict = {} + result["success"] = from_bool(self.success) + return result + # Experimental: this type is part of an experimental API and may change or be removed. class HostType(Enum): """Repository host type @@ -2661,6 +2867,31 @@ def to_dict(self) -> dict: result["serverName"] = from_str(self.server_name) return result +class MCPOauthPendingRequestResponseKind(Enum): + CANCELLED = "cancelled" + TOKEN = "token" + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class MCPOauthHandlePendingResult: + """Indicates whether the pending MCP OAuth response was accepted.""" + + success: bool + """Whether the response was accepted. False if the request was unknown, timed out, or + already resolved. + """ + + @staticmethod + def from_dict(obj: Any) -> 'MCPOauthHandlePendingResult': + assert isinstance(obj, dict) + success = from_bool(obj.get("success")) + return MCPOauthHandlePendingResult(success) + + def to_dict(self) -> dict: + result: dict = {} + result["success"] = from_bool(self.success) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class MCPOauthLoginRequest: @@ -3136,15 +3367,25 @@ class ModelBillingTokenPricesLongContext: """Long context tier pricing (available for models with extended context windows)""" cache_price: float | None = None - """AI Credits cost per billing batch of cached tokens""" + """Deprecated: use cacheReadPrice. AI Credits cost per billing batch of cached tokens""" + + cache_read_price: float | None = None + """AI Credits cost per billing batch of cached (read) tokens""" + + cache_write_price: float | None = None + """AI Credits cost per billing batch of cache-write (cache creation) tokens.""" context_max: int | None = None - """Prompt token budget (max_prompt_tokens) for the long context tier. The total context - window is this value plus the model's max_output_tokens. + """Deprecated: use maxPromptTokens. Prompt token budget for the long context tier. The total + context window is this value plus the model's max_output_tokens. """ input_price: float | None = None """AI Credits cost per billing batch of input tokens""" + max_prompt_tokens: int | None = None + """Prompt token budget for the long context tier. The total context window is this value + plus the model's max_output_tokens. + """ output_price: float | None = None """AI Credits cost per billing batch of output tokens""" @@ -3152,19 +3393,28 @@ class ModelBillingTokenPricesLongContext: def from_dict(obj: Any) -> 'ModelBillingTokenPricesLongContext': assert isinstance(obj, dict) cache_price = from_union([from_float, from_none], obj.get("cachePrice")) + cache_read_price = from_union([from_float, from_none], obj.get("cacheReadPrice")) + cache_write_price = from_union([from_float, from_none], obj.get("cacheWritePrice")) context_max = from_union([from_int, from_none], obj.get("contextMax")) input_price = from_union([from_float, from_none], obj.get("inputPrice")) + max_prompt_tokens = from_union([from_int, from_none], obj.get("maxPromptTokens")) output_price = from_union([from_float, from_none], obj.get("outputPrice")) - return ModelBillingTokenPricesLongContext(cache_price, context_max, input_price, output_price) + return ModelBillingTokenPricesLongContext(cache_price, cache_read_price, cache_write_price, context_max, input_price, max_prompt_tokens, output_price) def to_dict(self) -> dict: result: dict = {} if self.cache_price is not None: result["cachePrice"] = from_union([to_float, from_none], self.cache_price) + if self.cache_read_price is not None: + result["cacheReadPrice"] = from_union([to_float, from_none], self.cache_read_price) + if self.cache_write_price is not None: + result["cacheWritePrice"] = from_union([to_float, from_none], self.cache_write_price) if self.context_max is not None: result["contextMax"] = from_union([from_int, from_none], self.context_max) if self.input_price is not None: result["inputPrice"] = from_union([to_float, from_none], self.input_price) + if self.max_prompt_tokens is not None: + result["maxPromptTokens"] = from_union([from_int, from_none], self.max_prompt_tokens) if self.output_price is not None: result["outputPrice"] = from_union([to_float, from_none], self.output_price) return result @@ -10002,6 +10252,107 @@ def to_dict(self) -> dict: result["projectPath"] = from_union([from_str, from_none], self.project_path) return result +@dataclass +class LlmInferenceHTTPRequestStartRequest: + """The head of an outbound model-layer HTTP request.""" + + headers: dict[str, list[str]] + method: str + """HTTP method, e.g. GET, POST.""" + + request_id: str + """Opaque runtime-minted id, unique per in-flight request. The SDK uses this to correlate + httpRequestChunk frames and to address its httpResponseStart / httpResponseChunk replies + back to the runtime. + """ + url: str + """Absolute request URL.""" + + session_id: str | None = None + """Id of the runtime session that triggered this request, when one is in scope. Absent for + requests issued outside any session (e.g. startup model-catalog or capability + resolution). This is a payload field — not a dispatch key — because the client-global API + is registered process-wide rather than per session. + """ + transport: LlmInferenceHTTPRequestStartTransport | None = None + """Transport the runtime would otherwise use for this request. `http` (the default when + absent) covers plain HTTP and SSE responses; `websocket` indicates a full-duplex message + channel where each body chunk maps to one WebSocket message and the `binary` flag + distinguishes text from binary frames. The SDK consumer uses this to decide whether to + service the request with an HTTP client or a WebSocket client. It is the one piece of + request metadata the consumer cannot reliably infer from the URL or headers alone. + """ + + @staticmethod + def from_dict(obj: Any) -> 'LlmInferenceHTTPRequestStartRequest': + assert isinstance(obj, dict) + headers = from_dict(lambda x: from_list(from_str, x), obj.get("headers")) + method = from_str(obj.get("method")) + request_id = from_str(obj.get("requestId")) + url = from_str(obj.get("url")) + session_id = from_union([from_str, from_none], obj.get("sessionId")) + transport = from_union([LlmInferenceHTTPRequestStartTransport, from_none], obj.get("transport")) + return LlmInferenceHTTPRequestStartRequest(headers, method, request_id, url, session_id, transport) + + def to_dict(self) -> dict: + result: dict = {} + result["headers"] = from_dict(lambda x: from_list(from_str, x), self.headers) + result["method"] = from_str(self.method) + result["requestId"] = from_str(self.request_id) + result["url"] = from_str(self.url) + if self.session_id is not None: + result["sessionId"] = from_union([from_str, from_none], self.session_id) + if self.transport is not None: + result["transport"] = from_union([lambda x: to_enum(LlmInferenceHTTPRequestStartTransport, x), from_none], self.transport) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class LlmInferenceHTTPResponseChunkRequest: + """A response body chunk or terminal error.""" + + data: str + """Body byte range. UTF-8 text when `binary` is absent or false; base64-encoded bytes when + `binary` is true. May be empty (e.g. when the response body is empty: send a single chunk + with empty data and end=true). + """ + request_id: str + """Matches the requestId from the originating httpRequestStart frame.""" + + binary: bool | None = None + """When true, `data` is base64-encoded bytes. When absent or false, `data` is UTF-8 text.""" + + end: bool | None = None + """When true, this is the final body chunk for the response. The runtime treats the response + body as complete after receiving an end-marked chunk. + """ + error: LlmInferenceHTTPResponseChunkError | None = None + """Set to terminate the response with a transport-level failure. Implies end-of-stream; any + further chunks for this requestId are ignored. + """ + + @staticmethod + def from_dict(obj: Any) -> 'LlmInferenceHTTPResponseChunkRequest': + assert isinstance(obj, dict) + data = from_str(obj.get("data")) + request_id = from_str(obj.get("requestId")) + binary = from_union([from_bool, from_none], obj.get("binary")) + end = from_union([from_bool, from_none], obj.get("end")) + error = from_union([LlmInferenceHTTPResponseChunkError.from_dict, from_none], obj.get("error")) + return LlmInferenceHTTPResponseChunkRequest(data, request_id, binary, end, error) + + def to_dict(self) -> dict: + result: dict = {} + result["data"] = from_str(self.data) + result["requestId"] = from_str(self.request_id) + if self.binary is not None: + result["binary"] = from_union([from_bool, from_none], self.binary) + if self.end is not None: + result["end"] = from_union([from_bool, from_none], self.end) + if self.error is not None: + result["error"] = from_union([lambda x: to_class(LlmInferenceHTTPResponseChunkError, x), from_none], self.error) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class SessionContext: @@ -10811,6 +11162,47 @@ def to_dict(self) -> dict: result["pendingConnections"] = from_list(from_str, self.pending_connections) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class MCPOauthPendingRequestResponse: + """Host response to the pending OAuth request.""" + + kind: MCPOauthPendingRequestResponseKind + access_token: str | None = None + """Access token acquired by the SDK host""" + + expires_in: int | None = None + """Token lifetime in seconds, if known.""" + + refresh_token: str | None = None + """Refresh token supplied by the host, if available.""" + + token_type: str | None = None + """OAuth token type. Defaults to Bearer when omitted.""" + + @staticmethod + def from_dict(obj: Any) -> 'MCPOauthPendingRequestResponse': + assert isinstance(obj, dict) + kind = MCPOauthPendingRequestResponseKind(obj.get("kind")) + access_token = from_union([from_str, from_none], obj.get("accessToken")) + expires_in = from_union([from_int, from_none], obj.get("expiresIn")) + refresh_token = from_union([from_str, from_none], obj.get("refreshToken")) + token_type = from_union([from_str, from_none], obj.get("tokenType")) + return MCPOauthPendingRequestResponse(kind, access_token, expires_in, refresh_token, token_type) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = to_enum(MCPOauthPendingRequestResponseKind, self.kind) + if self.access_token is not None: + result["accessToken"] = from_union([from_str, from_none], self.access_token) + if self.expires_in is not None: + result["expiresIn"] = from_union([from_int, from_none], self.expires_in) + if self.refresh_token is not None: + result["refreshToken"] = from_union([from_str, from_none], self.refresh_token) + if self.token_type is not None: + result["tokenType"] = from_union([from_str, from_none], self.token_type) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class MCPSamplingExecutionResult: @@ -10961,11 +11353,17 @@ class ModelBillingTokenPrices: """Number of tokens per standard billing batch""" cache_price: float | None = None - """AI Credits cost per billing batch of cached tokens""" + """Deprecated: use cacheReadPrice. AI Credits cost per billing batch of cached tokens""" + + cache_read_price: float | None = None + """AI Credits cost per billing batch of cached (read) tokens""" + + cache_write_price: float | None = None + """AI Credits cost per billing batch of cache-write (cache creation) tokens.""" context_max: int | None = None - """Prompt token budget (max_prompt_tokens) for the default tier. The total context window is - this value plus the model's max_output_tokens. + """Deprecated: use maxPromptTokens. Prompt token budget for the default tier. The total + context window is this value plus the model's max_output_tokens. """ input_price: float | None = None """AI Credits cost per billing batch of input tokens""" @@ -10973,6 +11371,10 @@ class ModelBillingTokenPrices: long_context: ModelBillingTokenPricesLongContext | None = None """Long context tier pricing (available for models with extended context windows)""" + max_prompt_tokens: int | None = None + """Prompt token budget for the default tier. The total context window is this value plus the + model's max_output_tokens. + """ output_price: float | None = None """AI Credits cost per billing batch of output tokens""" @@ -10981,11 +11383,14 @@ def from_dict(obj: Any) -> 'ModelBillingTokenPrices': assert isinstance(obj, dict) batch_size = from_union([from_int, from_none], obj.get("batchSize")) cache_price = from_union([from_float, from_none], obj.get("cachePrice")) + cache_read_price = from_union([from_float, from_none], obj.get("cacheReadPrice")) + cache_write_price = from_union([from_float, from_none], obj.get("cacheWritePrice")) context_max = from_union([from_int, from_none], obj.get("contextMax")) input_price = from_union([from_float, from_none], obj.get("inputPrice")) long_context = from_union([ModelBillingTokenPricesLongContext.from_dict, from_none], obj.get("longContext")) + max_prompt_tokens = from_union([from_int, from_none], obj.get("maxPromptTokens")) output_price = from_union([from_float, from_none], obj.get("outputPrice")) - return ModelBillingTokenPrices(batch_size, cache_price, context_max, input_price, long_context, output_price) + return ModelBillingTokenPrices(batch_size, cache_price, cache_read_price, cache_write_price, context_max, input_price, long_context, max_prompt_tokens, output_price) def to_dict(self) -> dict: result: dict = {} @@ -10993,12 +11398,18 @@ def to_dict(self) -> dict: result["batchSize"] = from_union([from_int, from_none], self.batch_size) if self.cache_price is not None: result["cachePrice"] = from_union([to_float, from_none], self.cache_price) + if self.cache_read_price is not None: + result["cacheReadPrice"] = from_union([to_float, from_none], self.cache_read_price) + if self.cache_write_price is not None: + result["cacheWritePrice"] = from_union([to_float, from_none], self.cache_write_price) if self.context_max is not None: result["contextMax"] = from_union([from_int, from_none], self.context_max) if self.input_price is not None: result["inputPrice"] = from_union([to_float, from_none], self.input_price) if self.long_context is not None: result["longContext"] = from_union([lambda x: to_class(ModelBillingTokenPricesLongContext, x), from_none], self.long_context) + if self.max_prompt_tokens is not None: + result["maxPromptTokens"] = from_union([from_int, from_none], self.max_prompt_tokens) if self.output_price is not None: result["outputPrice"] = from_union([to_float, from_none], self.output_price) return result @@ -13439,7 +13850,7 @@ class SessionFSSqliteQueryRequest: session_id: str """Target session identifier""" - params: dict[str, float | str | None] | None = None + params: dict[str, Any] | None = None """Optional named bind parameters""" @staticmethod @@ -13448,7 +13859,7 @@ def from_dict(obj: Any) -> 'SessionFSSqliteQueryRequest': query = from_str(obj.get("query")) query_type = SessionFSSqliteQueryType(obj.get("queryType")) session_id = from_str(obj.get("sessionId")) - params = from_union([lambda x: from_dict(lambda x: from_union([from_none, from_float, from_str], x), x), from_none], obj.get("params")) + params = from_union([lambda x: from_dict(lambda x: x, x), from_none], obj.get("params")) return SessionFSSqliteQueryRequest(query, query_type, session_id, params) def to_dict(self) -> dict: @@ -13457,7 +13868,7 @@ def to_dict(self) -> dict: result["queryType"] = to_enum(SessionFSSqliteQueryType, self.query_type) result["sessionId"] = from_str(self.session_id) if self.params is not None: - result["params"] = from_union([lambda x: from_dict(lambda x: from_union([from_none, to_float, from_str], x), x), from_none], self.params) + result["params"] = from_union([lambda x: from_dict(lambda x: x, x), from_none], self.params) return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -16060,6 +16471,30 @@ def to_dict(self) -> dict: result["serverName"] = from_str(self.server_name) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class MCPOauthHandlePendingRequest: + """Pending MCP OAuth request ID and host-provided token or cancellation response.""" + + request_id: str + """OAuth request identifier from the mcp.oauth_required event""" + + result: MCPOauthPendingRequestResponse + """Host response to the pending OAuth request.""" + + @staticmethod + def from_dict(obj: Any) -> 'MCPOauthHandlePendingRequest': + assert isinstance(obj, dict) + request_id = from_str(obj.get("requestId")) + result = MCPOauthPendingRequestResponse.from_dict(obj.get("result")) + return MCPOauthHandlePendingRequest(request_id, result) + + def to_dict(self) -> dict: + result: dict = {} + result["requestId"] = from_str(self.request_id) + result["result"] = to_class(MCPOauthPendingRequestResponse, self.result) + return result + @dataclass class ModelBilling: """Billing information""" @@ -16122,7 +16557,7 @@ def to_dict(self) -> dict: class OptionsUpdateAdditionalContentExclusionPolicy: """Schema for the `OptionsUpdateAdditionalContentExclusionPolicy` type.""" - last_updated_at: float | str + last_updated_at: Any rules: list[OptionsUpdateAdditionalContentExclusionPolicyRule] scope: AdditionalContentExclusionPolicyScope """Allowed values for the `OptionsUpdateAdditionalContentExclusionPolicyScope` enumeration.""" @@ -16130,14 +16565,14 @@ class OptionsUpdateAdditionalContentExclusionPolicy: @staticmethod def from_dict(obj: Any) -> 'OptionsUpdateAdditionalContentExclusionPolicy': assert isinstance(obj, dict) - last_updated_at = from_union([from_float, from_str], obj.get("last_updated_at")) + last_updated_at = obj.get("last_updated_at") rules = from_list(OptionsUpdateAdditionalContentExclusionPolicyRule.from_dict, obj.get("rules")) scope = AdditionalContentExclusionPolicyScope(obj.get("scope")) return OptionsUpdateAdditionalContentExclusionPolicy(last_updated_at, rules, scope) def to_dict(self) -> dict: result: dict = {} - result["last_updated_at"] = from_union([to_float, from_str], self.last_updated_at) + result["last_updated_at"] = self.last_updated_at result["rules"] = from_list(lambda x: to_class(OptionsUpdateAdditionalContentExclusionPolicyRule, x), self.rules) result["scope"] = to_enum(AdditionalContentExclusionPolicyScope, self.scope) return result @@ -16147,7 +16582,7 @@ def to_dict(self) -> dict: class PermissionsConfigureAdditionalContentExclusionPolicy: """Schema for the `PermissionsConfigureAdditionalContentExclusionPolicy` type.""" - last_updated_at: float | str + last_updated_at: Any rules: list[PermissionsConfigureAdditionalContentExclusionPolicyRule] scope: AdditionalContentExclusionPolicyScope """Allowed values for the `PermissionsConfigureAdditionalContentExclusionPolicyScope` @@ -16157,14 +16592,14 @@ class PermissionsConfigureAdditionalContentExclusionPolicy: @staticmethod def from_dict(obj: Any) -> 'PermissionsConfigureAdditionalContentExclusionPolicy': assert isinstance(obj, dict) - last_updated_at = from_union([from_float, from_str], obj.get("last_updated_at")) + last_updated_at = obj.get("last_updated_at") rules = from_list(PermissionsConfigureAdditionalContentExclusionPolicyRule.from_dict, obj.get("rules")) scope = AdditionalContentExclusionPolicyScope(obj.get("scope")) return PermissionsConfigureAdditionalContentExclusionPolicy(last_updated_at, rules, scope) def to_dict(self) -> dict: result: dict = {} - result["last_updated_at"] = from_union([to_float, from_str], self.last_updated_at) + result["last_updated_at"] = self.last_updated_at result["rules"] = from_list(lambda x: to_class(PermissionsConfigureAdditionalContentExclusionPolicyRule, x), self.rules) result["scope"] = to_enum(AdditionalContentExclusionPolicyScope, self.scope) return result @@ -16595,7 +17030,7 @@ def to_dict(self) -> dict: class SessionOpenOptionsAdditionalContentExclusionPolicy: """Schema for the `SessionOpenOptionsAdditionalContentExclusionPolicy` type.""" - last_updated_at: float | str + last_updated_at: Any rules: list[SessionOpenOptionsAdditionalContentExclusionPolicyRule] scope: AdditionalContentExclusionPolicyScope """Allowed values for the `SessionOpenOptionsAdditionalContentExclusionPolicyScope` @@ -16605,14 +17040,14 @@ class SessionOpenOptionsAdditionalContentExclusionPolicy: @staticmethod def from_dict(obj: Any) -> 'SessionOpenOptionsAdditionalContentExclusionPolicy': assert isinstance(obj, dict) - last_updated_at = from_union([from_float, from_str], obj.get("last_updated_at")) + last_updated_at = obj.get("last_updated_at") rules = from_list(SessionOpenOptionsAdditionalContentExclusionPolicyRule.from_dict, obj.get("rules")) scope = AdditionalContentExclusionPolicyScope(obj.get("scope")) return SessionOpenOptionsAdditionalContentExclusionPolicy(last_updated_at, rules, scope) def to_dict(self) -> dict: result: dict = {} - result["last_updated_at"] = from_union([to_float, from_str], self.last_updated_at) + result["last_updated_at"] = self.last_updated_at result["rules"] = from_list(lambda x: to_class(SessionOpenOptionsAdditionalContentExclusionPolicyRule, x), self.rules) result["scope"] = to_enum(AdditionalContentExclusionPolicyScope, self.scope) return result @@ -18240,7 +18675,7 @@ class SandboxConfig: add_current_working_directory: bool | None = None """Whether to auto-add the current working directory to readwritePaths. Default: true.""" - config: dict[str, Any] | None = None + config: Any = None """Raw `ContainerConfig` (per `@microsoft/mxc-sdk`) passed directly to `spawnSandboxFromConfig`, bypassing policy merging. """ @@ -18252,7 +18687,7 @@ def from_dict(obj: Any) -> 'SandboxConfig': assert isinstance(obj, dict) enabled = from_bool(obj.get("enabled")) add_current_working_directory = from_union([from_bool, from_none], obj.get("addCurrentWorkingDirectory")) - config = from_union([lambda x: from_dict(lambda x: x, x), from_none], obj.get("config")) + config = obj.get("config") user_policy = from_union([SandboxConfigUserPolicy.from_dict, from_none], obj.get("userPolicy")) return SandboxConfig(enabled, add_current_working_directory, config, user_policy) @@ -18262,7 +18697,7 @@ def to_dict(self) -> dict: if self.add_current_working_directory is not None: result["addCurrentWorkingDirectory"] = from_union([from_bool, from_none], self.add_current_working_directory) if self.config is not None: - result["config"] = from_union([lambda x: from_dict(lambda x: x, x), from_none], self.config) + result["config"] = self.config if self.user_policy is not None: result["userPolicy"] = from_union([lambda x: to_class(SandboxConfigUserPolicy, x), from_none], self.user_policy) return result @@ -18401,6 +18836,11 @@ class SessionOpenOptions: disabled_skills: list[str] | None = None """Skill IDs disabled for this session.""" + enable_citations: bool | None = None + """Experimental: enable native model citations (Anthropic models today), normalized onto the + `assistant.message` event. Off by default; may change or be removed while the citations + surface is experimental. + """ enable_on_demand_instruction_discovery: bool | None = None """Whether on-demand custom instruction discovery is enabled.""" @@ -18538,6 +18978,7 @@ def from_dict(obj: Any) -> 'SessionOpenOptions': detached_from_spawning_parent_session_id = from_union([from_str, from_none], obj.get("detachedFromSpawningParentSessionId")) disabled_instruction_sources = from_union([lambda x: from_list(from_str, x), from_none], obj.get("disabledInstructionSources")) disabled_skills = from_union([lambda x: from_list(from_str, x), from_none], obj.get("disabledSkills")) + enable_citations = from_union([from_bool, from_none], obj.get("enableCitations")) enable_on_demand_instruction_discovery = from_union([from_bool, from_none], obj.get("enableOnDemandInstructionDiscovery")) enable_script_safety = from_union([from_bool, from_none], obj.get("enableScriptSafety")) enable_streaming = from_union([from_bool, from_none], obj.get("enableStreaming")) @@ -18575,7 +19016,7 @@ def from_dict(obj: Any) -> 'SessionOpenOptions': trajectory_file = from_union([from_str, from_none], obj.get("trajectoryFile")) working_directory = from_union([from_str, from_none], obj.get("workingDirectory")) working_directory_context = from_union([SessionContext.from_dict, from_none], obj.get("workingDirectoryContext")) - return SessionOpenOptions(additional_content_exclusion_policies, agent_context, ask_user_disabled, auth_info, available_tools, client_kind, client_name, coauthor_enabled, config_dir, continue_on_auto_mode, copilot_url, custom_agents_local_only, detached_from_spawning_parent_engagement_id, detached_from_spawning_parent_session_id, disabled_instruction_sources, disabled_skills, enable_on_demand_instruction_discovery, enable_script_safety, enable_streaming, env_value_mode, events_log_directory, excluded_tools, exp_assignments, feature_flags, installed_plugins, integration_id, is_experimental_mode, log_interactive_shells, lsp_client_name, max_inline_binary_bytes, memory, model, model_capabilities_overrides, models, name, provider, providers, reasoning_effort, reasoning_summary, remote_defaulted_on, remote_exporting, remote_steerable, running_in_interactive_mode, sandbox_config, session_capabilities, session_id, shell_init_profile, shell_process_flags, skill_directories, skip_custom_instructions, trajectory_file, working_directory, working_directory_context) + return SessionOpenOptions(additional_content_exclusion_policies, agent_context, ask_user_disabled, auth_info, available_tools, client_kind, client_name, coauthor_enabled, config_dir, continue_on_auto_mode, copilot_url, custom_agents_local_only, detached_from_spawning_parent_engagement_id, detached_from_spawning_parent_session_id, disabled_instruction_sources, disabled_skills, enable_citations, enable_on_demand_instruction_discovery, enable_script_safety, enable_streaming, env_value_mode, events_log_directory, excluded_tools, exp_assignments, feature_flags, installed_plugins, integration_id, is_experimental_mode, log_interactive_shells, lsp_client_name, max_inline_binary_bytes, memory, model, model_capabilities_overrides, models, name, provider, providers, reasoning_effort, reasoning_summary, remote_defaulted_on, remote_exporting, remote_steerable, running_in_interactive_mode, sandbox_config, session_capabilities, session_id, shell_init_profile, shell_process_flags, skill_directories, skip_custom_instructions, trajectory_file, working_directory, working_directory_context) def to_dict(self) -> dict: result: dict = {} @@ -18611,6 +19052,8 @@ def to_dict(self) -> dict: result["disabledInstructionSources"] = from_union([lambda x: from_list(from_str, x), from_none], self.disabled_instruction_sources) if self.disabled_skills is not None: result["disabledSkills"] = from_union([lambda x: from_list(from_str, x), from_none], self.disabled_skills) + if self.enable_citations is not None: + result["enableCitations"] = from_union([from_bool, from_none], self.enable_citations) if self.enable_on_demand_instruction_discovery is not None: result["enableOnDemandInstructionDiscovery"] = from_union([from_bool, from_none], self.enable_on_demand_instruction_discovery) if self.enable_script_safety is not None: @@ -19460,11 +19903,6 @@ def to_dict(self) -> dict: class MCPExecuteSamplingParams: """Identifiers and raw MCP CreateMessageRequest params used to run a sampling inference.""" - mcp_request_id: float | str - """The original MCP JSON-RPC request ID (string or number). Used by the runtime to correlate - the inference with the originating MCP request for telemetry; this is distinct from - `requestId` (which is the schema-level cancellation handle). - """ request: dict[str, Any] """Raw MCP CreateMessageRequest params, as received in the `sampling.requested` event. Treated as opaque at the schema layer; the runtime converts the embedded MCP messages @@ -19478,10 +19916,15 @@ class MCPExecuteSamplingParams: server_name: str """Name of the MCP server that initiated the sampling request""" + mcp_request_id: Any = None + """The original MCP JSON-RPC request ID (string or number). Used by the runtime to correlate + the inference with the originating MCP request for telemetry; this is distinct from + `requestId` (which is the schema-level cancellation handle). + """ @staticmethod def from_dict(obj: Any) -> 'MCPExecuteSamplingParams': assert isinstance(obj, dict) - mcp_request_id = from_union([from_float, from_str], obj.get("mcpRequestId")) + mcp_request_id = obj.get("mcpRequestId") request = from_dict(lambda x: x, obj.get("request")) request_id = from_str(obj.get("requestId")) server_name = from_str(obj.get("serverName")) @@ -19489,7 +19932,7 @@ def from_dict(obj: Any) -> 'MCPExecuteSamplingParams': def to_dict(self) -> dict: result: dict = {} - result["mcpRequestId"] = from_union([to_float, from_str], self.mcp_request_id) + result["mcpRequestId"] = self.mcp_request_id result["request"] = from_dict(lambda x: x, self.request) result["requestId"] = from_str(self.request_id) result["serverName"] = from_str(self.server_name) @@ -20264,6 +20707,18 @@ class RPC: instruction_source: InstructionSource instruction_source_location: InstructionLocation instruction_source_type: InstructionSourceType + llm_inference_headers: dict[str, list[str]] + llm_inference_http_request_chunk_request: LlmInferenceHTTPRequestChunkRequest + llm_inference_http_request_chunk_result: LlmInferenceHTTPRequestChunkResult + llm_inference_http_request_start_request: LlmInferenceHTTPRequestStartRequest + llm_inference_http_request_start_result: LlmInferenceHTTPRequestStartResult + llm_inference_http_request_start_transport: LlmInferenceHTTPRequestStartTransport + llm_inference_http_response_chunk_error: LlmInferenceHTTPResponseChunkError + llm_inference_http_response_chunk_request: LlmInferenceHTTPResponseChunkRequest + llm_inference_http_response_chunk_result: LlmInferenceHTTPResponseChunkResult + llm_inference_http_response_start_request: LlmInferenceHTTPResponseStartRequest + llm_inference_http_response_start_result: LlmInferenceHTTPResponseStartResult + llm_inference_set_provider_result: LlmInferenceSetProviderResult local_session_metadata_value: LocalSessionMetadataValue log_request: LogRequest log_result: LogResult @@ -20322,8 +20777,11 @@ class RPC: mcp_is_server_running_result: MCPIsServerRunningResult mcp_list_tools_request: MCPListToolsRequest mcp_list_tools_result: MCPListToolsResult + mcp_oauth_handle_pending_request: MCPOauthHandlePendingRequest + mcp_oauth_handle_pending_result: MCPOauthHandlePendingResult mcp_oauth_login_request: MCPOauthLoginRequest mcp_oauth_login_result: MCPOauthLoginResult + mcp_oauth_pending_request_response: MCPOauthPendingRequestResponse mcp_oauth_respond_request: MCPOauthRespondRequest mcp_oauth_respond_result: MCPOauthRespondResult mcp_register_external_client_request: MCPRegisterExternalClientRequest @@ -21006,6 +21464,18 @@ def from_dict(obj: Any) -> 'RPC': instruction_source = InstructionSource.from_dict(obj.get("InstructionSource")) instruction_source_location = InstructionLocation(obj.get("InstructionSourceLocation")) instruction_source_type = InstructionSourceType(obj.get("InstructionSourceType")) + llm_inference_headers = from_dict(lambda x: from_list(from_str, x), obj.get("LlmInferenceHeaders")) + llm_inference_http_request_chunk_request = LlmInferenceHTTPRequestChunkRequest.from_dict(obj.get("LlmInferenceHttpRequestChunkRequest")) + llm_inference_http_request_chunk_result = LlmInferenceHTTPRequestChunkResult.from_dict(obj.get("LlmInferenceHttpRequestChunkResult")) + llm_inference_http_request_start_request = LlmInferenceHTTPRequestStartRequest.from_dict(obj.get("LlmInferenceHttpRequestStartRequest")) + llm_inference_http_request_start_result = LlmInferenceHTTPRequestStartResult.from_dict(obj.get("LlmInferenceHttpRequestStartResult")) + llm_inference_http_request_start_transport = LlmInferenceHTTPRequestStartTransport(obj.get("LlmInferenceHttpRequestStartTransport")) + llm_inference_http_response_chunk_error = LlmInferenceHTTPResponseChunkError.from_dict(obj.get("LlmInferenceHttpResponseChunkError")) + llm_inference_http_response_chunk_request = LlmInferenceHTTPResponseChunkRequest.from_dict(obj.get("LlmInferenceHttpResponseChunkRequest")) + llm_inference_http_response_chunk_result = LlmInferenceHTTPResponseChunkResult.from_dict(obj.get("LlmInferenceHttpResponseChunkResult")) + llm_inference_http_response_start_request = LlmInferenceHTTPResponseStartRequest.from_dict(obj.get("LlmInferenceHttpResponseStartRequest")) + llm_inference_http_response_start_result = LlmInferenceHTTPResponseStartResult.from_dict(obj.get("LlmInferenceHttpResponseStartResult")) + llm_inference_set_provider_result = LlmInferenceSetProviderResult.from_dict(obj.get("LlmInferenceSetProviderResult")) local_session_metadata_value = LocalSessionMetadataValue.from_dict(obj.get("LocalSessionMetadataValue")) log_request = LogRequest.from_dict(obj.get("LogRequest")) log_result = LogResult.from_dict(obj.get("LogResult")) @@ -21064,8 +21534,11 @@ def from_dict(obj: Any) -> 'RPC': mcp_is_server_running_result = MCPIsServerRunningResult.from_dict(obj.get("McpIsServerRunningResult")) mcp_list_tools_request = MCPListToolsRequest.from_dict(obj.get("McpListToolsRequest")) mcp_list_tools_result = MCPListToolsResult.from_dict(obj.get("McpListToolsResult")) + mcp_oauth_handle_pending_request = MCPOauthHandlePendingRequest.from_dict(obj.get("McpOauthHandlePendingRequest")) + mcp_oauth_handle_pending_result = MCPOauthHandlePendingResult.from_dict(obj.get("McpOauthHandlePendingResult")) mcp_oauth_login_request = MCPOauthLoginRequest.from_dict(obj.get("McpOauthLoginRequest")) mcp_oauth_login_result = MCPOauthLoginResult.from_dict(obj.get("McpOauthLoginResult")) + mcp_oauth_pending_request_response = MCPOauthPendingRequestResponse.from_dict(obj.get("McpOauthPendingRequestResponse")) mcp_oauth_respond_request = MCPOauthRespondRequest.from_dict(obj.get("McpOauthRespondRequest")) mcp_oauth_respond_result = MCPOauthRespondResult.from_dict(obj.get("McpOauthRespondResult")) mcp_register_external_client_request = MCPRegisterExternalClientRequest.from_dict(obj.get("McpRegisterExternalClientRequest")) @@ -21595,7 +22068,7 @@ def from_dict(obj: Any) -> 'RPC': subagent_settings = from_union([SubagentSettings.from_dict, from_none], obj.get("SubagentSettings")) task_progress = from_union([TaskProgress.from_dict, from_none], obj.get("TaskProgress")) workspace_summary = from_union([WorkspaceSummary.from_dict, from_none], obj.get("WorkspaceSummary")) - return RPC(abort_request, abort_result, account_get_quota_request, account_get_quota_result, account_quota_snapshot, agent_discovery_path, agent_discovery_path_list, agent_discovery_path_scope, agent_get_current_result, agent_info, agent_info_source, agent_list, agent_registry_live_target_entry, agent_registry_live_target_entry_attention_kind, agent_registry_live_target_entry_kind, agent_registry_live_target_entry_last_terminal_event, agent_registry_live_target_entry_status, agent_registry_log_capture, agent_registry_log_capture_open_error_reason, agent_registry_spawn_error, agent_registry_spawn_permission_mode, agent_registry_spawn_registry_timeout, agent_registry_spawn_request, agent_registry_spawn_result, agent_registry_spawn_spawned, agent_registry_spawn_validation_error, agent_registry_spawn_validation_error_field, agent_registry_spawn_validation_error_reason, agent_reload_result, agents_discover_request, agent_select_request, agent_select_result, agents_get_discovery_paths_request, allow_all_permission_set_result, allow_all_permission_state, api_key_auth_info, auth_info, auth_info_type, cancel_user_requested_shell_command_result, canvas_action, canvas_action_invoke_request, canvas_action_invoke_result, canvas_close_request, canvas_host_context, canvas_host_context_capabilities, canvas_instance_availability, canvas_json_schema, canvas_list, canvas_list_open_result, canvas_open_request, canvas_provider_close_request, canvas_provider_invoke_action_request, canvas_provider_open_request, canvas_provider_open_result, canvas_session_context, command_list, commands_handle_pending_command_request, commands_handle_pending_command_result, commands_invoke_request, commands_list_request, commands_respond_to_queued_command_request, commands_respond_to_queued_command_result, configure_session_extensions_params, connected_remote_session_metadata, connected_remote_session_metadata_kind, connected_remote_session_metadata_repository, connect_remote_session_params, connect_request, connect_result, content_filter_mode, copilot_api_token_auth_info, copilot_user_response, copilot_user_response_endpoints, copilot_user_response_quota_snapshots, copilot_user_response_quota_snapshots_chat, copilot_user_response_quota_snapshots_completions, copilot_user_response_quota_snapshots_premium_interactions, current_model, current_tool_metadata, discovered_canvas, discovered_mcp_server, discovered_mcp_server_type, enqueue_command_params, enqueue_command_result, env_auth_info, event_log_read_request, event_log_release_interest_result, event_log_tail_result, event_log_types, events_agent_scope, events_cursor_status, events_read_result, execute_command_params, execute_command_result, extension, extension_context_push_input, extension_list, extensions_disable_request, extensions_enable_request, extension_source, extension_status, external_tool_result, external_tool_text_result_for_llm, external_tool_text_result_for_llm_binary_results_for_llm, external_tool_text_result_for_llm_binary_results_for_llm_type, external_tool_text_result_for_llm_content, external_tool_text_result_for_llm_content_audio, external_tool_text_result_for_llm_content_image, external_tool_text_result_for_llm_content_resource, external_tool_text_result_for_llm_content_resource_details, external_tool_text_result_for_llm_content_resource_link, external_tool_text_result_for_llm_content_resource_link_icon, external_tool_text_result_for_llm_content_resource_link_icon_theme, external_tool_text_result_for_llm_content_terminal, external_tool_text_result_for_llm_content_text, filter_mapping, fleet_start_request, fleet_start_result, folder_trust_add_params, folder_trust_check_params, folder_trust_check_result, gh_cli_auth_info, handle_pending_tool_call_request, handle_pending_tool_call_result, history_abort_manual_compaction_result, history_cancel_background_compaction_result, history_compact_context_window, history_compact_request, history_compact_result, history_summarize_for_handoff_result, history_truncate_request, history_truncate_result, hmac_auth_info, installed_plugin, installed_plugin_info, installed_plugin_source, installed_plugin_source_git_hub, installed_plugin_source_local, installed_plugin_source_url, instruction_discovery_path, instruction_discovery_path_kind, instruction_discovery_path_list, instruction_discovery_path_location, instructions_discover_request, instructions_get_discovery_paths_request, instructions_get_sources_result, instruction_source, instruction_source_location, instruction_source_type, local_session_metadata_value, log_request, log_result, lsp_initialize_request, marketplace_add_result, marketplace_browse_result, marketplace_info, marketplace_list_result, marketplace_plugin_info, marketplace_refresh_entry, marketplace_refresh_result, marketplace_remove_result, mcp_allowed_server, mcp_apps_call_tool_request, mcp_apps_diagnose_capability, mcp_apps_diagnose_request, mcp_apps_diagnose_result, mcp_apps_diagnose_server, mcp_apps_host_context, mcp_apps_host_context_details, mcp_apps_host_context_details_available_display_mode, mcp_apps_host_context_details_display_mode, mcp_apps_host_context_details_platform, mcp_apps_host_context_details_theme, mcp_apps_list_tools_request, mcp_apps_list_tools_result, mcp_apps_read_resource_request, mcp_apps_read_resource_result, mcp_apps_resource_content, mcp_apps_set_host_context_details, mcp_apps_set_host_context_details_available_display_mode, mcp_apps_set_host_context_details_display_mode, mcp_apps_set_host_context_details_platform, mcp_apps_set_host_context_details_theme, mcp_apps_set_host_context_request, mcp_cancel_sampling_execution_params, mcp_cancel_sampling_execution_result, mcp_config_add_request, mcp_config_disable_request, mcp_config_enable_request, mcp_config_list, mcp_config_remove_request, mcp_config_update_request, mcp_configure_git_hub_request, mcp_configure_git_hub_result, mcp_disable_request, mcp_discover_request, mcp_discover_result, mcp_enable_request, mcp_execute_sampling_params, mcp_execute_sampling_request, mcp_execute_sampling_result, mcp_filtered_server, mcp_host_state, mcp_is_server_running_request, mcp_is_server_running_result, mcp_list_tools_request, mcp_list_tools_result, mcp_oauth_login_request, mcp_oauth_login_result, mcp_oauth_respond_request, mcp_oauth_respond_result, mcp_register_external_client_request, mcp_reload_with_config_request, mcp_remove_git_hub_result, mcp_restart_server_request, mcp_sampling_execution_action, mcp_sampling_execution_result, mcp_server, mcp_server_auth_config, mcp_server_auth_config_redirect_port, mcp_server_config, mcp_server_config_defer_tools, mcp_server_config_http, mcp_server_config_http_oauth_grant_type, mcp_server_config_http_type, mcp_server_config_stdio, mcp_server_failure_info, mcp_server_list, mcp_server_needs_auth_info, mcp_set_env_value_mode_details, mcp_set_env_value_mode_params, mcp_set_env_value_mode_result, mcp_start_server_request, mcp_start_servers_result, mcp_stop_server_request, mcp_tools, mcp_unregister_external_client_request, memory_configuration, metadata_context_info_request, metadata_context_info_result, metadata_is_processing_result, metadata_recompute_context_tokens_request, metadata_recompute_context_tokens_result, metadata_record_context_change_request, metadata_record_context_change_result, metadata_set_working_directory_request, metadata_set_working_directory_result, metadata_snapshot_current_mode, metadata_snapshot_remote_metadata, metadata_snapshot_remote_metadata_repository, metadata_snapshot_remote_metadata_task_type, model, model_billing, model_billing_token_prices, model_billing_token_prices_long_context, model_capabilities, model_capabilities_limits, model_capabilities_limits_vision, model_capabilities_override, model_capabilities_override_limits, model_capabilities_override_limits_vision, model_capabilities_override_supports, model_capabilities_supports, model_list, model_list_request, model_picker_category, model_picker_price_category, model_policy, model_policy_state, model_set_reasoning_effort_request, model_set_reasoning_effort_result, models_list_request, model_switch_to_request, model_switch_to_result, mode_set_request, named_provider_config, name_get_result, name_set_auto_request, name_set_auto_result, name_set_request, open_canvas_instance, options_update_additional_content_exclusion_policy, options_update_additional_content_exclusion_policy_rule, options_update_additional_content_exclusion_policy_rule_source, options_update_additional_content_exclusion_policy_scope, options_update_context_tier, options_update_env_value_mode, options_update_reasoning_summary, options_update_tool_filter_precedence, pending_permission_request, pending_permission_request_list, permission_decision, permission_decision_approved, permission_decision_approved_for_location, permission_decision_approved_for_session, permission_decision_approve_for_location, permission_decision_approve_for_location_approval, permission_decision_approve_for_location_approval_commands, permission_decision_approve_for_location_approval_custom_tool, permission_decision_approve_for_location_approval_extension_management, permission_decision_approve_for_location_approval_extension_permission_access, permission_decision_approve_for_location_approval_mcp, permission_decision_approve_for_location_approval_mcp_sampling, permission_decision_approve_for_location_approval_memory, permission_decision_approve_for_location_approval_read, permission_decision_approve_for_location_approval_write, permission_decision_approve_for_session, permission_decision_approve_for_session_approval, permission_decision_approve_for_session_approval_commands, permission_decision_approve_for_session_approval_custom_tool, permission_decision_approve_for_session_approval_extension_management, permission_decision_approve_for_session_approval_extension_permission_access, permission_decision_approve_for_session_approval_mcp, permission_decision_approve_for_session_approval_mcp_sampling, permission_decision_approve_for_session_approval_memory, permission_decision_approve_for_session_approval_read, permission_decision_approve_for_session_approval_write, permission_decision_approve_once, permission_decision_approve_permanently, permission_decision_cancelled, permission_decision_denied_by_content_exclusion_policy, permission_decision_denied_by_permission_request_hook, permission_decision_denied_by_rules, permission_decision_denied_interactively_by_user, permission_decision_denied_no_approval_rule_and_could_not_request_from_user, permission_decision_reject, permission_decision_request, permission_decision_user_not_available, permission_location_add_tool_approval_params, permission_location_apply_params, permission_location_apply_result, permission_location_resolve_params, permission_location_resolve_result, permission_location_type, permission_paths_add_params, permission_paths_allowed_check_params, permission_paths_allowed_check_result, permission_paths_config, permission_paths_list, permission_paths_update_primary_params, permission_paths_workspace_check_params, permission_paths_workspace_check_result, permission_prompt_shown_notification, permission_request_result, permission_rules_set, permissions_configure_additional_content_exclusion_policy, permissions_configure_additional_content_exclusion_policy_rule, permissions_configure_additional_content_exclusion_policy_rule_source, permissions_configure_additional_content_exclusion_policy_scope, permissions_configure_params, permissions_configure_result, permissions_folder_trust_add_trusted_result, permissions_get_allow_all_request, permissions_locations_add_tool_approval_details, permissions_locations_add_tool_approval_details_commands, permissions_locations_add_tool_approval_details_custom_tool, permissions_locations_add_tool_approval_details_extension_management, permissions_locations_add_tool_approval_details_extension_permission_access, permissions_locations_add_tool_approval_details_mcp, permissions_locations_add_tool_approval_details_mcp_sampling, permissions_locations_add_tool_approval_details_memory, permissions_locations_add_tool_approval_details_read, permissions_locations_add_tool_approval_details_write, permissions_locations_add_tool_approval_result, permissions_modify_rules_params, permissions_modify_rules_result, permissions_modify_rules_scope, permissions_notify_prompt_shown_result, permissions_paths_add_result, permissions_paths_list_request, permissions_paths_update_primary_result, permissions_pending_requests_request, permissions_reset_session_approvals_request, permissions_reset_session_approvals_result, permissions_set_allow_all_request, permissions_set_allow_all_source, permissions_set_approve_all_request, permissions_set_approve_all_result, permissions_set_approve_all_source, permissions_set_required_request, permissions_set_required_result, permissions_urls_set_unrestricted_mode_result, permission_urls_config, permission_urls_set_unrestricted_mode_params, ping_request, ping_result, plan_read_result, plan_read_sql_todos_result, plan_read_sql_todos_with_dependencies_result, plan_sql_todo_dependency, plan_sql_todos_row, plan_update_request, plugin, plugin_install_result, plugin_list, plugin_list_result, plugins_disable_request, plugins_enable_request, plugins_install_request, plugins_marketplaces_add_request, plugins_marketplaces_browse_request, plugins_marketplaces_refresh_request, plugins_marketplaces_remove_request, plugins_reload_request, plugins_uninstall_request, plugins_update_request, plugin_update_all_entry, plugin_update_all_result, plugin_update_result, poll_spawned_sessions_result, provider_config, provider_config_azure, provider_config_type, provider_config_wire_api, provider_endpoint, provider_endpoint_type, provider_endpoint_wire_api, provider_get_endpoint_request, provider_model_config, provider_session_token, push_attachment, push_attachment_blob, push_attachment_directory, push_attachment_file, push_attachment_file_line_range, push_attachment_git_hub_reference, push_attachment_git_hub_reference_type, push_attachment_selection, push_attachment_selection_details, push_attachment_selection_details_end, push_attachment_selection_details_start, queued_command_handled, queued_command_not_handled, queued_command_result, queue_pending_items, queue_pending_items_kind, queue_pending_items_result, queue_remove_most_recent_result, register_event_interest_params, register_event_interest_result, register_extension_tools_params, register_extension_tools_result, release_event_interest_params, remote_control_config, remote_control_config_existing_mc_session, remote_control_status, remote_control_status_active, remote_control_status_connecting, remote_control_status_error, remote_control_status_off, remote_control_status_result, remote_control_stop_result, remote_control_transfer_result, remote_enable_request, remote_enable_result, remote_notify_steerable_changed_request, remote_notify_steerable_changed_result, remote_session_connection_result, remote_session_metadata_repository, remote_session_metadata_task_type, remote_session_metadata_value, remote_session_mode, remote_session_repository, sandbox_config, sandbox_config_user_policy, sandbox_config_user_policy_experimental, sandbox_config_user_policy_experimental_seatbelt, sandbox_config_user_policy_filesystem, sandbox_config_user_policy_network, schedule_entry, schedule_list, schedule_stop_request, schedule_stop_result, secrets_add_filter_values_request, secrets_add_filter_values_result, send_agent_mode, send_attachments_to_message_params, send_mode, send_request, send_result, server_agent_list, server_instruction_source_list, server_skill, server_skill_list, session_activity, session_auth_status, session_bulk_delete_result, session_capability, session_context, session_context_host_type, session_enrich_metadata_result, session_fs_append_file_request, session_fs_error, session_fs_error_code, session_fs_exists_request, session_fs_exists_result, session_fs_mkdir_request, session_fs_readdir_request, session_fs_readdir_result, session_fs_readdir_with_types_entry, session_fs_readdir_with_types_entry_type, session_fs_readdir_with_types_request, session_fs_readdir_with_types_result, session_fs_read_file_request, session_fs_read_file_result, session_fs_rename_request, session_fs_rm_request, session_fs_set_provider_capabilities, session_fs_set_provider_conventions, session_fs_set_provider_request, session_fs_set_provider_result, session_fs_sqlite_exists_request, session_fs_sqlite_exists_result, session_fs_sqlite_query_request, session_fs_sqlite_query_result, session_fs_sqlite_query_type, session_fs_stat_request, session_fs_stat_result, session_fs_write_file_request, session_installed_plugin, session_installed_plugin_source, session_installed_plugin_source_git_hub, session_installed_plugin_source_local, session_installed_plugin_source_url, session_list, session_list_entry, session_list_filter, session_load_deferred_repo_hooks_result, session_log_level, session_mcp_apps_call_tool_result, session_metadata_snapshot, session_mode, session_model_list, session_open_options, session_open_options_additional_content_exclusion_policy, session_open_options_additional_content_exclusion_policy_rule, session_open_options_additional_content_exclusion_policy_rule_source, session_open_options_additional_content_exclusion_policy_scope, session_open_options_env_value_mode, session_open_options_reasoning_summary, session_open_params, session_open_result, session_prune_result, sessions_bulk_delete_request, sessions_check_in_use_request, sessions_check_in_use_result, sessions_close_request, sessions_close_result, sessions_enrich_metadata_request, session_set_credentials_params, session_set_credentials_result, sessions_find_by_prefix_request, sessions_find_by_prefix_result, sessions_find_by_task_id_request, sessions_find_by_task_id_result, sessions_fork_request, sessions_fork_result, sessions_get_board_entry_count_request, sessions_get_board_entry_count_result, sessions_get_event_file_path_request, sessions_get_event_file_path_result, sessions_get_last_for_context_request, sessions_get_last_for_context_result, sessions_get_persisted_remote_steerable_request, sessions_get_persisted_remote_steerable_result, session_sizes, sessions_list_request, sessions_load_deferred_repo_hooks_request, sessions_open_attach, sessions_open_cloud, sessions_open_create, sessions_open_handoff, sessions_open_handoff_task_type, sessions_open_progress, sessions_open_progress_status, sessions_open_progress_step, sessions_open_remote, sessions_open_resume, sessions_open_resume_last, sessions_open_status, session_source, sessions_poll_spawned_sessions_event, sessions_poll_spawned_sessions_request, sessions_prune_old_request, sessions_register_extension_tools_on_session_options, sessions_release_lock_request, sessions_release_lock_result, sessions_reload_plugin_hooks_request, sessions_reload_plugin_hooks_result, sessions_save_request, sessions_save_result, sessions_set_additional_plugins_request, sessions_set_additional_plugins_result, sessions_set_remote_control_steering_request, sessions_start_remote_control_request, sessions_stop_remote_control_request, sessions_transfer_remote_control_request, session_telemetry_engagement, session_update_options_params, session_update_options_result, session_working_directory_context, session_working_directory_context_host_type, shell_cancel_user_requested_request, shell_exec_request, shell_exec_result, shell_execute_user_requested_request, shell_kill_request, shell_kill_result, shell_kill_signal, shutdown_request, skill, skill_discovery_path, skill_discovery_path_list, skill_discovery_scope, skill_list, skills_config_set_disabled_skills_request, skills_disable_request, skills_discover_request, skills_enable_request, skills_get_discovery_paths_request, skills_get_invoked_result, skills_invoked_skill, skills_load_diagnostics, slash_command_agent_prompt_result, slash_command_completed_result, slash_command_info, slash_command_input, slash_command_input_completion, slash_command_invocation_result, slash_command_kind, slash_command_select_subcommand_option, slash_command_select_subcommand_result, slash_command_text_result, subagent_settings_entry, subagent_settings_entry_context_tier, task_agent_info, task_agent_progress, task_execution_mode, task_info, task_list, task_progress_line, tasks_cancel_request, tasks_cancel_result, tasks_get_current_promotable_result, tasks_get_progress_request, tasks_get_progress_result, task_shell_info, task_shell_info_attachment_mode, task_shell_progress, tasks_promote_current_to_background_result, tasks_promote_to_background_request, tasks_promote_to_background_result, tasks_refresh_result, tasks_remove_request, tasks_remove_result, tasks_send_message_request, tasks_send_message_result, tasks_start_agent_request, tasks_start_agent_result, task_status, tasks_wait_for_pending_result, telemetry_set_feature_overrides_request, token_auth_info, tool, tool_list, tools_get_current_metadata_result, tools_initialize_and_validate_result, tools_list_request, tools_update_subagent_settings_result, ui_auto_mode_switch_response, ui_elicitation_array_any_of_field, ui_elicitation_array_any_of_field_items, ui_elicitation_array_any_of_field_items_any_of, ui_elicitation_array_enum_field, ui_elicitation_array_enum_field_items, ui_elicitation_field_value, ui_elicitation_request, ui_elicitation_response, ui_elicitation_response_action, ui_elicitation_response_content, ui_elicitation_result, ui_elicitation_schema, ui_elicitation_schema_property, ui_elicitation_schema_property_boolean, ui_elicitation_schema_property_number, ui_elicitation_schema_property_number_type, ui_elicitation_schema_property_string, ui_elicitation_schema_property_string_format, ui_elicitation_string_enum_field, ui_elicitation_string_one_of_field, ui_elicitation_string_one_of_field_one_of, ui_ephemeral_query_request, ui_ephemeral_query_result, ui_exit_plan_mode_action, ui_exit_plan_mode_response, ui_handle_pending_auto_mode_switch_request, ui_handle_pending_elicitation_request, ui_handle_pending_exit_plan_mode_request, ui_handle_pending_result, ui_handle_pending_sampling_request, ui_handle_pending_sampling_response, ui_handle_pending_user_input_request, ui_register_direct_auto_mode_switch_handler_result, ui_unregister_direct_auto_mode_switch_handler_request, ui_unregister_direct_auto_mode_switch_handler_result, ui_user_input_response, update_subagent_settings_request, usage_get_metrics_result, usage_metrics_code_changes, usage_metrics_model_metric, usage_metrics_model_metric_requests, usage_metrics_model_metric_token_detail, usage_metrics_model_metric_usage, usage_metrics_token_detail, user_auth_info, user_requested_shell_command_result, workspace_diff_file_change, workspace_diff_file_change_type, workspace_diff_mode, workspace_diff_result, workspaces_checkpoints, workspaces_create_file_request, workspaces_diff_request, workspaces_get_workspace_result, workspaces_list_checkpoints_result, workspaces_list_files_result, workspaces_read_checkpoint_request, workspaces_read_checkpoint_result, workspaces_read_file_request, workspaces_read_file_result, workspaces_save_large_paste_request, workspaces_save_large_paste_result, workspace_summary_host_type, workspaces_workspace_details_host_type, session_context_info, subagent_settings, task_progress, workspace_summary) + return RPC(abort_request, abort_result, account_get_quota_request, account_get_quota_result, account_quota_snapshot, agent_discovery_path, agent_discovery_path_list, agent_discovery_path_scope, agent_get_current_result, agent_info, agent_info_source, agent_list, agent_registry_live_target_entry, agent_registry_live_target_entry_attention_kind, agent_registry_live_target_entry_kind, agent_registry_live_target_entry_last_terminal_event, agent_registry_live_target_entry_status, agent_registry_log_capture, agent_registry_log_capture_open_error_reason, agent_registry_spawn_error, agent_registry_spawn_permission_mode, agent_registry_spawn_registry_timeout, agent_registry_spawn_request, agent_registry_spawn_result, agent_registry_spawn_spawned, agent_registry_spawn_validation_error, agent_registry_spawn_validation_error_field, agent_registry_spawn_validation_error_reason, agent_reload_result, agents_discover_request, agent_select_request, agent_select_result, agents_get_discovery_paths_request, allow_all_permission_set_result, allow_all_permission_state, api_key_auth_info, auth_info, auth_info_type, cancel_user_requested_shell_command_result, canvas_action, canvas_action_invoke_request, canvas_action_invoke_result, canvas_close_request, canvas_host_context, canvas_host_context_capabilities, canvas_instance_availability, canvas_json_schema, canvas_list, canvas_list_open_result, canvas_open_request, canvas_provider_close_request, canvas_provider_invoke_action_request, canvas_provider_open_request, canvas_provider_open_result, canvas_session_context, command_list, commands_handle_pending_command_request, commands_handle_pending_command_result, commands_invoke_request, commands_list_request, commands_respond_to_queued_command_request, commands_respond_to_queued_command_result, configure_session_extensions_params, connected_remote_session_metadata, connected_remote_session_metadata_kind, connected_remote_session_metadata_repository, connect_remote_session_params, connect_request, connect_result, content_filter_mode, copilot_api_token_auth_info, copilot_user_response, copilot_user_response_endpoints, copilot_user_response_quota_snapshots, copilot_user_response_quota_snapshots_chat, copilot_user_response_quota_snapshots_completions, copilot_user_response_quota_snapshots_premium_interactions, current_model, current_tool_metadata, discovered_canvas, discovered_mcp_server, discovered_mcp_server_type, enqueue_command_params, enqueue_command_result, env_auth_info, event_log_read_request, event_log_release_interest_result, event_log_tail_result, event_log_types, events_agent_scope, events_cursor_status, events_read_result, execute_command_params, execute_command_result, extension, extension_context_push_input, extension_list, extensions_disable_request, extensions_enable_request, extension_source, extension_status, external_tool_result, external_tool_text_result_for_llm, external_tool_text_result_for_llm_binary_results_for_llm, external_tool_text_result_for_llm_binary_results_for_llm_type, external_tool_text_result_for_llm_content, external_tool_text_result_for_llm_content_audio, external_tool_text_result_for_llm_content_image, external_tool_text_result_for_llm_content_resource, external_tool_text_result_for_llm_content_resource_details, external_tool_text_result_for_llm_content_resource_link, external_tool_text_result_for_llm_content_resource_link_icon, external_tool_text_result_for_llm_content_resource_link_icon_theme, external_tool_text_result_for_llm_content_terminal, external_tool_text_result_for_llm_content_text, filter_mapping, fleet_start_request, fleet_start_result, folder_trust_add_params, folder_trust_check_params, folder_trust_check_result, gh_cli_auth_info, handle_pending_tool_call_request, handle_pending_tool_call_result, history_abort_manual_compaction_result, history_cancel_background_compaction_result, history_compact_context_window, history_compact_request, history_compact_result, history_summarize_for_handoff_result, history_truncate_request, history_truncate_result, hmac_auth_info, installed_plugin, installed_plugin_info, installed_plugin_source, installed_plugin_source_git_hub, installed_plugin_source_local, installed_plugin_source_url, instruction_discovery_path, instruction_discovery_path_kind, instruction_discovery_path_list, instruction_discovery_path_location, instructions_discover_request, instructions_get_discovery_paths_request, instructions_get_sources_result, instruction_source, instruction_source_location, instruction_source_type, llm_inference_headers, llm_inference_http_request_chunk_request, llm_inference_http_request_chunk_result, llm_inference_http_request_start_request, llm_inference_http_request_start_result, llm_inference_http_request_start_transport, llm_inference_http_response_chunk_error, llm_inference_http_response_chunk_request, llm_inference_http_response_chunk_result, llm_inference_http_response_start_request, llm_inference_http_response_start_result, llm_inference_set_provider_result, local_session_metadata_value, log_request, log_result, lsp_initialize_request, marketplace_add_result, marketplace_browse_result, marketplace_info, marketplace_list_result, marketplace_plugin_info, marketplace_refresh_entry, marketplace_refresh_result, marketplace_remove_result, mcp_allowed_server, mcp_apps_call_tool_request, mcp_apps_diagnose_capability, mcp_apps_diagnose_request, mcp_apps_diagnose_result, mcp_apps_diagnose_server, mcp_apps_host_context, mcp_apps_host_context_details, mcp_apps_host_context_details_available_display_mode, mcp_apps_host_context_details_display_mode, mcp_apps_host_context_details_platform, mcp_apps_host_context_details_theme, mcp_apps_list_tools_request, mcp_apps_list_tools_result, mcp_apps_read_resource_request, mcp_apps_read_resource_result, mcp_apps_resource_content, mcp_apps_set_host_context_details, mcp_apps_set_host_context_details_available_display_mode, mcp_apps_set_host_context_details_display_mode, mcp_apps_set_host_context_details_platform, mcp_apps_set_host_context_details_theme, mcp_apps_set_host_context_request, mcp_cancel_sampling_execution_params, mcp_cancel_sampling_execution_result, mcp_config_add_request, mcp_config_disable_request, mcp_config_enable_request, mcp_config_list, mcp_config_remove_request, mcp_config_update_request, mcp_configure_git_hub_request, mcp_configure_git_hub_result, mcp_disable_request, mcp_discover_request, mcp_discover_result, mcp_enable_request, mcp_execute_sampling_params, mcp_execute_sampling_request, mcp_execute_sampling_result, mcp_filtered_server, mcp_host_state, mcp_is_server_running_request, mcp_is_server_running_result, mcp_list_tools_request, mcp_list_tools_result, mcp_oauth_handle_pending_request, mcp_oauth_handle_pending_result, mcp_oauth_login_request, mcp_oauth_login_result, mcp_oauth_pending_request_response, mcp_oauth_respond_request, mcp_oauth_respond_result, mcp_register_external_client_request, mcp_reload_with_config_request, mcp_remove_git_hub_result, mcp_restart_server_request, mcp_sampling_execution_action, mcp_sampling_execution_result, mcp_server, mcp_server_auth_config, mcp_server_auth_config_redirect_port, mcp_server_config, mcp_server_config_defer_tools, mcp_server_config_http, mcp_server_config_http_oauth_grant_type, mcp_server_config_http_type, mcp_server_config_stdio, mcp_server_failure_info, mcp_server_list, mcp_server_needs_auth_info, mcp_set_env_value_mode_details, mcp_set_env_value_mode_params, mcp_set_env_value_mode_result, mcp_start_server_request, mcp_start_servers_result, mcp_stop_server_request, mcp_tools, mcp_unregister_external_client_request, memory_configuration, metadata_context_info_request, metadata_context_info_result, metadata_is_processing_result, metadata_recompute_context_tokens_request, metadata_recompute_context_tokens_result, metadata_record_context_change_request, metadata_record_context_change_result, metadata_set_working_directory_request, metadata_set_working_directory_result, metadata_snapshot_current_mode, metadata_snapshot_remote_metadata, metadata_snapshot_remote_metadata_repository, metadata_snapshot_remote_metadata_task_type, model, model_billing, model_billing_token_prices, model_billing_token_prices_long_context, model_capabilities, model_capabilities_limits, model_capabilities_limits_vision, model_capabilities_override, model_capabilities_override_limits, model_capabilities_override_limits_vision, model_capabilities_override_supports, model_capabilities_supports, model_list, model_list_request, model_picker_category, model_picker_price_category, model_policy, model_policy_state, model_set_reasoning_effort_request, model_set_reasoning_effort_result, models_list_request, model_switch_to_request, model_switch_to_result, mode_set_request, named_provider_config, name_get_result, name_set_auto_request, name_set_auto_result, name_set_request, open_canvas_instance, options_update_additional_content_exclusion_policy, options_update_additional_content_exclusion_policy_rule, options_update_additional_content_exclusion_policy_rule_source, options_update_additional_content_exclusion_policy_scope, options_update_context_tier, options_update_env_value_mode, options_update_reasoning_summary, options_update_tool_filter_precedence, pending_permission_request, pending_permission_request_list, permission_decision, permission_decision_approved, permission_decision_approved_for_location, permission_decision_approved_for_session, permission_decision_approve_for_location, permission_decision_approve_for_location_approval, permission_decision_approve_for_location_approval_commands, permission_decision_approve_for_location_approval_custom_tool, permission_decision_approve_for_location_approval_extension_management, permission_decision_approve_for_location_approval_extension_permission_access, permission_decision_approve_for_location_approval_mcp, permission_decision_approve_for_location_approval_mcp_sampling, permission_decision_approve_for_location_approval_memory, permission_decision_approve_for_location_approval_read, permission_decision_approve_for_location_approval_write, permission_decision_approve_for_session, permission_decision_approve_for_session_approval, permission_decision_approve_for_session_approval_commands, permission_decision_approve_for_session_approval_custom_tool, permission_decision_approve_for_session_approval_extension_management, permission_decision_approve_for_session_approval_extension_permission_access, permission_decision_approve_for_session_approval_mcp, permission_decision_approve_for_session_approval_mcp_sampling, permission_decision_approve_for_session_approval_memory, permission_decision_approve_for_session_approval_read, permission_decision_approve_for_session_approval_write, permission_decision_approve_once, permission_decision_approve_permanently, permission_decision_cancelled, permission_decision_denied_by_content_exclusion_policy, permission_decision_denied_by_permission_request_hook, permission_decision_denied_by_rules, permission_decision_denied_interactively_by_user, permission_decision_denied_no_approval_rule_and_could_not_request_from_user, permission_decision_reject, permission_decision_request, permission_decision_user_not_available, permission_location_add_tool_approval_params, permission_location_apply_params, permission_location_apply_result, permission_location_resolve_params, permission_location_resolve_result, permission_location_type, permission_paths_add_params, permission_paths_allowed_check_params, permission_paths_allowed_check_result, permission_paths_config, permission_paths_list, permission_paths_update_primary_params, permission_paths_workspace_check_params, permission_paths_workspace_check_result, permission_prompt_shown_notification, permission_request_result, permission_rules_set, permissions_configure_additional_content_exclusion_policy, permissions_configure_additional_content_exclusion_policy_rule, permissions_configure_additional_content_exclusion_policy_rule_source, permissions_configure_additional_content_exclusion_policy_scope, permissions_configure_params, permissions_configure_result, permissions_folder_trust_add_trusted_result, permissions_get_allow_all_request, permissions_locations_add_tool_approval_details, permissions_locations_add_tool_approval_details_commands, permissions_locations_add_tool_approval_details_custom_tool, permissions_locations_add_tool_approval_details_extension_management, permissions_locations_add_tool_approval_details_extension_permission_access, permissions_locations_add_tool_approval_details_mcp, permissions_locations_add_tool_approval_details_mcp_sampling, permissions_locations_add_tool_approval_details_memory, permissions_locations_add_tool_approval_details_read, permissions_locations_add_tool_approval_details_write, permissions_locations_add_tool_approval_result, permissions_modify_rules_params, permissions_modify_rules_result, permissions_modify_rules_scope, permissions_notify_prompt_shown_result, permissions_paths_add_result, permissions_paths_list_request, permissions_paths_update_primary_result, permissions_pending_requests_request, permissions_reset_session_approvals_request, permissions_reset_session_approvals_result, permissions_set_allow_all_request, permissions_set_allow_all_source, permissions_set_approve_all_request, permissions_set_approve_all_result, permissions_set_approve_all_source, permissions_set_required_request, permissions_set_required_result, permissions_urls_set_unrestricted_mode_result, permission_urls_config, permission_urls_set_unrestricted_mode_params, ping_request, ping_result, plan_read_result, plan_read_sql_todos_result, plan_read_sql_todos_with_dependencies_result, plan_sql_todo_dependency, plan_sql_todos_row, plan_update_request, plugin, plugin_install_result, plugin_list, plugin_list_result, plugins_disable_request, plugins_enable_request, plugins_install_request, plugins_marketplaces_add_request, plugins_marketplaces_browse_request, plugins_marketplaces_refresh_request, plugins_marketplaces_remove_request, plugins_reload_request, plugins_uninstall_request, plugins_update_request, plugin_update_all_entry, plugin_update_all_result, plugin_update_result, poll_spawned_sessions_result, provider_config, provider_config_azure, provider_config_type, provider_config_wire_api, provider_endpoint, provider_endpoint_type, provider_endpoint_wire_api, provider_get_endpoint_request, provider_model_config, provider_session_token, push_attachment, push_attachment_blob, push_attachment_directory, push_attachment_file, push_attachment_file_line_range, push_attachment_git_hub_reference, push_attachment_git_hub_reference_type, push_attachment_selection, push_attachment_selection_details, push_attachment_selection_details_end, push_attachment_selection_details_start, queued_command_handled, queued_command_not_handled, queued_command_result, queue_pending_items, queue_pending_items_kind, queue_pending_items_result, queue_remove_most_recent_result, register_event_interest_params, register_event_interest_result, register_extension_tools_params, register_extension_tools_result, release_event_interest_params, remote_control_config, remote_control_config_existing_mc_session, remote_control_status, remote_control_status_active, remote_control_status_connecting, remote_control_status_error, remote_control_status_off, remote_control_status_result, remote_control_stop_result, remote_control_transfer_result, remote_enable_request, remote_enable_result, remote_notify_steerable_changed_request, remote_notify_steerable_changed_result, remote_session_connection_result, remote_session_metadata_repository, remote_session_metadata_task_type, remote_session_metadata_value, remote_session_mode, remote_session_repository, sandbox_config, sandbox_config_user_policy, sandbox_config_user_policy_experimental, sandbox_config_user_policy_experimental_seatbelt, sandbox_config_user_policy_filesystem, sandbox_config_user_policy_network, schedule_entry, schedule_list, schedule_stop_request, schedule_stop_result, secrets_add_filter_values_request, secrets_add_filter_values_result, send_agent_mode, send_attachments_to_message_params, send_mode, send_request, send_result, server_agent_list, server_instruction_source_list, server_skill, server_skill_list, session_activity, session_auth_status, session_bulk_delete_result, session_capability, session_context, session_context_host_type, session_enrich_metadata_result, session_fs_append_file_request, session_fs_error, session_fs_error_code, session_fs_exists_request, session_fs_exists_result, session_fs_mkdir_request, session_fs_readdir_request, session_fs_readdir_result, session_fs_readdir_with_types_entry, session_fs_readdir_with_types_entry_type, session_fs_readdir_with_types_request, session_fs_readdir_with_types_result, session_fs_read_file_request, session_fs_read_file_result, session_fs_rename_request, session_fs_rm_request, session_fs_set_provider_capabilities, session_fs_set_provider_conventions, session_fs_set_provider_request, session_fs_set_provider_result, session_fs_sqlite_exists_request, session_fs_sqlite_exists_result, session_fs_sqlite_query_request, session_fs_sqlite_query_result, session_fs_sqlite_query_type, session_fs_stat_request, session_fs_stat_result, session_fs_write_file_request, session_installed_plugin, session_installed_plugin_source, session_installed_plugin_source_git_hub, session_installed_plugin_source_local, session_installed_plugin_source_url, session_list, session_list_entry, session_list_filter, session_load_deferred_repo_hooks_result, session_log_level, session_mcp_apps_call_tool_result, session_metadata_snapshot, session_mode, session_model_list, session_open_options, session_open_options_additional_content_exclusion_policy, session_open_options_additional_content_exclusion_policy_rule, session_open_options_additional_content_exclusion_policy_rule_source, session_open_options_additional_content_exclusion_policy_scope, session_open_options_env_value_mode, session_open_options_reasoning_summary, session_open_params, session_open_result, session_prune_result, sessions_bulk_delete_request, sessions_check_in_use_request, sessions_check_in_use_result, sessions_close_request, sessions_close_result, sessions_enrich_metadata_request, session_set_credentials_params, session_set_credentials_result, sessions_find_by_prefix_request, sessions_find_by_prefix_result, sessions_find_by_task_id_request, sessions_find_by_task_id_result, sessions_fork_request, sessions_fork_result, sessions_get_board_entry_count_request, sessions_get_board_entry_count_result, sessions_get_event_file_path_request, sessions_get_event_file_path_result, sessions_get_last_for_context_request, sessions_get_last_for_context_result, sessions_get_persisted_remote_steerable_request, sessions_get_persisted_remote_steerable_result, session_sizes, sessions_list_request, sessions_load_deferred_repo_hooks_request, sessions_open_attach, sessions_open_cloud, sessions_open_create, sessions_open_handoff, sessions_open_handoff_task_type, sessions_open_progress, sessions_open_progress_status, sessions_open_progress_step, sessions_open_remote, sessions_open_resume, sessions_open_resume_last, sessions_open_status, session_source, sessions_poll_spawned_sessions_event, sessions_poll_spawned_sessions_request, sessions_prune_old_request, sessions_register_extension_tools_on_session_options, sessions_release_lock_request, sessions_release_lock_result, sessions_reload_plugin_hooks_request, sessions_reload_plugin_hooks_result, sessions_save_request, sessions_save_result, sessions_set_additional_plugins_request, sessions_set_additional_plugins_result, sessions_set_remote_control_steering_request, sessions_start_remote_control_request, sessions_stop_remote_control_request, sessions_transfer_remote_control_request, session_telemetry_engagement, session_update_options_params, session_update_options_result, session_working_directory_context, session_working_directory_context_host_type, shell_cancel_user_requested_request, shell_exec_request, shell_exec_result, shell_execute_user_requested_request, shell_kill_request, shell_kill_result, shell_kill_signal, shutdown_request, skill, skill_discovery_path, skill_discovery_path_list, skill_discovery_scope, skill_list, skills_config_set_disabled_skills_request, skills_disable_request, skills_discover_request, skills_enable_request, skills_get_discovery_paths_request, skills_get_invoked_result, skills_invoked_skill, skills_load_diagnostics, slash_command_agent_prompt_result, slash_command_completed_result, slash_command_info, slash_command_input, slash_command_input_completion, slash_command_invocation_result, slash_command_kind, slash_command_select_subcommand_option, slash_command_select_subcommand_result, slash_command_text_result, subagent_settings_entry, subagent_settings_entry_context_tier, task_agent_info, task_agent_progress, task_execution_mode, task_info, task_list, task_progress_line, tasks_cancel_request, tasks_cancel_result, tasks_get_current_promotable_result, tasks_get_progress_request, tasks_get_progress_result, task_shell_info, task_shell_info_attachment_mode, task_shell_progress, tasks_promote_current_to_background_result, tasks_promote_to_background_request, tasks_promote_to_background_result, tasks_refresh_result, tasks_remove_request, tasks_remove_result, tasks_send_message_request, tasks_send_message_result, tasks_start_agent_request, tasks_start_agent_result, task_status, tasks_wait_for_pending_result, telemetry_set_feature_overrides_request, token_auth_info, tool, tool_list, tools_get_current_metadata_result, tools_initialize_and_validate_result, tools_list_request, tools_update_subagent_settings_result, ui_auto_mode_switch_response, ui_elicitation_array_any_of_field, ui_elicitation_array_any_of_field_items, ui_elicitation_array_any_of_field_items_any_of, ui_elicitation_array_enum_field, ui_elicitation_array_enum_field_items, ui_elicitation_field_value, ui_elicitation_request, ui_elicitation_response, ui_elicitation_response_action, ui_elicitation_response_content, ui_elicitation_result, ui_elicitation_schema, ui_elicitation_schema_property, ui_elicitation_schema_property_boolean, ui_elicitation_schema_property_number, ui_elicitation_schema_property_number_type, ui_elicitation_schema_property_string, ui_elicitation_schema_property_string_format, ui_elicitation_string_enum_field, ui_elicitation_string_one_of_field, ui_elicitation_string_one_of_field_one_of, ui_ephemeral_query_request, ui_ephemeral_query_result, ui_exit_plan_mode_action, ui_exit_plan_mode_response, ui_handle_pending_auto_mode_switch_request, ui_handle_pending_elicitation_request, ui_handle_pending_exit_plan_mode_request, ui_handle_pending_result, ui_handle_pending_sampling_request, ui_handle_pending_sampling_response, ui_handle_pending_user_input_request, ui_register_direct_auto_mode_switch_handler_result, ui_unregister_direct_auto_mode_switch_handler_request, ui_unregister_direct_auto_mode_switch_handler_result, ui_user_input_response, update_subagent_settings_request, usage_get_metrics_result, usage_metrics_code_changes, usage_metrics_model_metric, usage_metrics_model_metric_requests, usage_metrics_model_metric_token_detail, usage_metrics_model_metric_usage, usage_metrics_token_detail, user_auth_info, user_requested_shell_command_result, workspace_diff_file_change, workspace_diff_file_change_type, workspace_diff_mode, workspace_diff_result, workspaces_checkpoints, workspaces_create_file_request, workspaces_diff_request, workspaces_get_workspace_result, workspaces_list_checkpoints_result, workspaces_list_files_result, workspaces_read_checkpoint_request, workspaces_read_checkpoint_result, workspaces_read_file_request, workspaces_read_file_result, workspaces_save_large_paste_request, workspaces_save_large_paste_result, workspace_summary_host_type, workspaces_workspace_details_host_type, session_context_info, subagent_settings, task_progress, workspace_summary) def to_dict(self) -> dict: result: dict = {} @@ -21748,6 +22221,18 @@ def to_dict(self) -> dict: result["InstructionSource"] = to_class(InstructionSource, self.instruction_source) result["InstructionSourceLocation"] = to_enum(InstructionLocation, self.instruction_source_location) result["InstructionSourceType"] = to_enum(InstructionSourceType, self.instruction_source_type) + result["LlmInferenceHeaders"] = from_dict(lambda x: from_list(from_str, x), self.llm_inference_headers) + result["LlmInferenceHttpRequestChunkRequest"] = to_class(LlmInferenceHTTPRequestChunkRequest, self.llm_inference_http_request_chunk_request) + result["LlmInferenceHttpRequestChunkResult"] = to_class(LlmInferenceHTTPRequestChunkResult, self.llm_inference_http_request_chunk_result) + result["LlmInferenceHttpRequestStartRequest"] = to_class(LlmInferenceHTTPRequestStartRequest, self.llm_inference_http_request_start_request) + result["LlmInferenceHttpRequestStartResult"] = to_class(LlmInferenceHTTPRequestStartResult, self.llm_inference_http_request_start_result) + result["LlmInferenceHttpRequestStartTransport"] = to_enum(LlmInferenceHTTPRequestStartTransport, self.llm_inference_http_request_start_transport) + result["LlmInferenceHttpResponseChunkError"] = to_class(LlmInferenceHTTPResponseChunkError, self.llm_inference_http_response_chunk_error) + result["LlmInferenceHttpResponseChunkRequest"] = to_class(LlmInferenceHTTPResponseChunkRequest, self.llm_inference_http_response_chunk_request) + result["LlmInferenceHttpResponseChunkResult"] = to_class(LlmInferenceHTTPResponseChunkResult, self.llm_inference_http_response_chunk_result) + result["LlmInferenceHttpResponseStartRequest"] = to_class(LlmInferenceHTTPResponseStartRequest, self.llm_inference_http_response_start_request) + result["LlmInferenceHttpResponseStartResult"] = to_class(LlmInferenceHTTPResponseStartResult, self.llm_inference_http_response_start_result) + result["LlmInferenceSetProviderResult"] = to_class(LlmInferenceSetProviderResult, self.llm_inference_set_provider_result) result["LocalSessionMetadataValue"] = to_class(LocalSessionMetadataValue, self.local_session_metadata_value) result["LogRequest"] = to_class(LogRequest, self.log_request) result["LogResult"] = to_class(LogResult, self.log_result) @@ -21806,8 +22291,11 @@ def to_dict(self) -> dict: result["McpIsServerRunningResult"] = to_class(MCPIsServerRunningResult, self.mcp_is_server_running_result) result["McpListToolsRequest"] = to_class(MCPListToolsRequest, self.mcp_list_tools_request) result["McpListToolsResult"] = to_class(MCPListToolsResult, self.mcp_list_tools_result) + result["McpOauthHandlePendingRequest"] = to_class(MCPOauthHandlePendingRequest, self.mcp_oauth_handle_pending_request) + result["McpOauthHandlePendingResult"] = to_class(MCPOauthHandlePendingResult, self.mcp_oauth_handle_pending_result) result["McpOauthLoginRequest"] = to_class(MCPOauthLoginRequest, self.mcp_oauth_login_request) result["McpOauthLoginResult"] = to_class(MCPOauthLoginResult, self.mcp_oauth_login_result) + result["McpOauthPendingRequestResponse"] = to_class(MCPOauthPendingRequestResponse, self.mcp_oauth_pending_request_response) result["McpOauthRespondRequest"] = to_class(MCPOauthRespondRequest, self.mcp_oauth_respond_request) result["McpOauthRespondResult"] = to_class(MCPOauthRespondResult, self.mcp_oauth_respond_result) result["McpRegisterExternalClientRequest"] = to_class(MCPRegisterExternalClientRequest, self.mcp_register_external_client_request) @@ -22565,6 +23053,7 @@ def _load_TaskInfo(obj: Any) -> "TaskInfo": FilterMapping = dict InstructionDiscoveryPathLocation = InstructionLocation InstructionSourceLocation = InstructionLocation +LlmInferenceHeaders = dict McpAppsHostContextDetailsAvailableDisplayMode = MCPAppsDisplayMode McpAppsHostContextDetailsDisplayMode = MCPAppsDisplayMode McpAppsHostContextDetailsTheme = Theme @@ -22879,6 +23368,26 @@ async def set_provider(self, params: SessionFSSetProviderRequest, *, timeout: fl return SessionFSSetProviderResult.from_dict(await self._client.request("sessionFs.setProvider", params_dict, **_timeout_kwargs(timeout))) +# Experimental: this API group is experimental and may change or be removed. +class ServerLlmInferenceApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + + async def set_provider(self, *, timeout: float | None = None) -> LlmInferenceSetProviderResult: + "Registers an SDK client as the LLM inference callback provider.\n\nReturns:\n Indicates whether the calling client was registered as the LLM inference provider." + return LlmInferenceSetProviderResult.from_dict(await self._client.request("llmInference.setProvider", {}, **_timeout_kwargs(timeout))) + + async def http_response_start(self, params: LlmInferenceHTTPResponseStartRequest, *, timeout: float | None = None) -> LlmInferenceHTTPResponseStartResult: + "Delivers the response head (status + headers) for an in-flight request, correlated by the requestId the runtime supplied in httpRequestStart. Must be called exactly once per request before any httpResponseChunk frames.\n\nArgs:\n params: Response head.\n\nReturns:\n Whether the start frame was accepted." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return LlmInferenceHTTPResponseStartResult.from_dict(await self._client.request("llmInference.httpResponseStart", params_dict, **_timeout_kwargs(timeout))) + + async def http_response_chunk(self, params: LlmInferenceHTTPResponseChunkRequest, *, timeout: float | None = None) -> LlmInferenceHTTPResponseChunkResult: + "Delivers a body byte range (or a terminal transport error) for an in-flight response, correlated by requestId. Set `end` true on the last chunk. When `error` is set the response terminates with a transport-level failure and the runtime raises an APIConnectionError.\n\nArgs:\n params: A response body chunk or terminal error.\n\nReturns:\n Whether the chunk was accepted." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return LlmInferenceHTTPResponseChunkResult.from_dict(await self._client.request("llmInference.httpResponseChunk", params_dict, **_timeout_kwargs(timeout))) + + # Experimental: this API group is experimental and may change or be removed. class ServerSessionsApi: def __init__(self, client: "JsonRpcClient"): @@ -23025,6 +23534,7 @@ def __init__(self, client: "JsonRpcClient"): self.user = ServerUserApi(client) self.runtime = ServerRuntimeApi(client) self.session_fs = ServerSessionFsApi(client) + self.llm_inference = ServerLlmInferenceApi(client) self.sessions = ServerSessionsApi(client) self.agent_registry = ServerAgentRegistryApi(client) @@ -23444,6 +23954,12 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + async def handle_pending_request(self, params: MCPOauthHandlePendingRequest, *, timeout: float | None = None) -> MCPOauthHandlePendingResult: + "Resolves a pending MCP OAuth request with a host-provided token or cancellation. The pending request is emitted as mcp.oauth_required with the data necessary to authorize the request.\n\nArgs:\n params: Pending MCP OAuth request ID and host-provided token or cancellation response.\n\nReturns:\n Indicates whether the pending MCP OAuth response was accepted." + params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} + params_dict["sessionId"] = self._session_id + return MCPOauthHandlePendingResult.from_dict(await self._client.request("session.mcp.oauth.handlePendingRequest", params_dict, **_timeout_kwargs(timeout))) + async def login(self, params: MCPOauthLoginRequest, *, timeout: float | None = None) -> MCPOauthLoginResult: "Starts OAuth authentication for a remote MCP server.\n\nArgs:\n params: Remote MCP server name and optional overrides controlling reauthentication, OAuth client display name, and the callback success-page copy.\n\nReturns:\n OAuth authorization URL the caller should open, or empty when cached tokens already authenticated the server." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -24230,7 +24746,7 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._session_id = session_id async def _respond(self, params: MCPOauthRespondRequest, *, timeout: float | None = None) -> MCPOauthRespondResult: - "Responds to a pending MCP OAuth provider request. Marked internal because the `provider` argument is an in-process OAuthClientProvider instance that cannot be carried over the wire; the public OAuth surface will route the response through a wire-clean handshake once the CLI moves on top of the SDK.\n\nArgs:\n params: MCP OAuth request id and optional provider response.\n\nReturns:\n Empty result after recording the MCP OAuth response.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." + "Responds to a pending MCP OAuth request with an in-process provider. This internal CLI-only API accepts a live OAuthClientProvider instance and cannot be used over the SDK JSON-RPC boundary. Use session.mcp.oauth.handlePendingRequest instead for the public SDK-safe response path.\n\nArgs:\n params: MCP OAuth request id and optional provider response.\n\nReturns:\n Empty result after recording the MCP OAuth response.\n\n:meta private:\n\nInternal SDK API; not part of the public surface." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return MCPOauthRespondResult.from_dict(await self._client.request("session.mcp.oauth.respond", params_dict, **_timeout_kwargs(timeout))) @@ -24638,6 +25154,18 @@ async def handle_canvas_action_invoke(params: dict) -> dict | None: "InstructionsGetDiscoveryPathsRequest", "InstructionsGetSourcesResult", "KindEnum", + "LlmInferenceHTTPRequestChunkRequest", + "LlmInferenceHTTPRequestChunkResult", + "LlmInferenceHTTPRequestStartRequest", + "LlmInferenceHTTPRequestStartResult", + "LlmInferenceHTTPRequestStartTransport", + "LlmInferenceHTTPResponseChunkError", + "LlmInferenceHTTPResponseChunkRequest", + "LlmInferenceHTTPResponseChunkResult", + "LlmInferenceHTTPResponseStartRequest", + "LlmInferenceHTTPResponseStartResult", + "LlmInferenceHeaders", + "LlmInferenceSetProviderResult", "LocalSessionMetadataValue", "LogRequest", "LogResult", @@ -24681,8 +25209,12 @@ async def handle_canvas_action_invoke(params: dict) -> dict | None: "MCPIsServerRunningResult", "MCPListToolsRequest", "MCPListToolsResult", + "MCPOauthHandlePendingRequest", + "MCPOauthHandlePendingResult", "MCPOauthLoginRequest", "MCPOauthLoginResult", + "MCPOauthPendingRequestResponse", + "MCPOauthPendingRequestResponseKind", "MCPOauthRespondRequest", "MCPOauthRespondResult", "MCPRegisterExternalClientRequest", @@ -25036,6 +25568,7 @@ async def handle_canvas_action_invoke(params: dict) -> dict | None: "ServerAgentsApi", "ServerInstructionSourceList", "ServerInstructionsApi", + "ServerLlmInferenceApi", "ServerMcpApi", "ServerMcpConfigApi", "ServerModelsApi", diff --git a/python/copilot/generated/session_events.py b/python/copilot/generated/session_events.py index 0a3c81816..14da3d634 100644 --- a/python/copilot/generated/session_events.py +++ b/python/copilot/generated/session_events.py @@ -371,6 +371,249 @@ def to_dict(self) -> dict: return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class CitableSource: + "A source supplied by a tool that should be made available to the model as citable content." + content: str + id: str + path: str | None = None + title: str | None = None + url: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "CitableSource": + assert isinstance(obj, dict) + content = from_str(obj.get("content")) + id = from_str(obj.get("id")) + path = from_union([from_none, from_str], obj.get("path")) + title = from_union([from_none, from_str], obj.get("title")) + url = from_union([from_none, from_str], obj.get("url")) + return CitableSource( + content=content, + id=id, + path=path, + title=title, + url=url, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["content"] = from_str(self.content) + result["id"] = from_str(self.id) + if self.path is not None: + result["path"] = from_union([from_none, from_str], self.path) + if self.title is not None: + result["title"] = from_union([from_none, from_str], self.title) + if self.url is not None: + result["url"] = from_union([from_none, from_str], self.url) + return result + + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class CitationLocationBlock: + "A content-block range within a structured source document." + end_block: int + start_block: int + type: ClassVar[str] = "block" + + @staticmethod + def from_dict(obj: Any) -> "CitationLocationBlock": + assert isinstance(obj, dict) + end_block = from_int(obj.get("endBlock")) + start_block = from_int(obj.get("startBlock")) + return CitationLocationBlock( + end_block=end_block, + start_block=start_block, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["endBlock"] = to_int(self.end_block) + result["startBlock"] = to_int(self.start_block) + result["type"] = self.type + return result + + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class CitationLocationChar: + "A character range within the source's text content." + end_index: int + start_index: int + type: ClassVar[str] = "char" + + @staticmethod + def from_dict(obj: Any) -> "CitationLocationChar": + assert isinstance(obj, dict) + end_index = from_int(obj.get("endIndex")) + start_index = from_int(obj.get("startIndex")) + return CitationLocationChar( + end_index=end_index, + start_index=start_index, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["endIndex"] = to_int(self.end_index) + result["startIndex"] = to_int(self.start_index) + result["type"] = self.type + return result + + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class CitationLocationPage: + "A page range within a paginated source document." + end_page: int + start_page: int + type: ClassVar[str] = "page" + + @staticmethod + def from_dict(obj: Any) -> "CitationLocationPage": + assert isinstance(obj, dict) + end_page = from_int(obj.get("endPage")) + start_page = from_int(obj.get("startPage")) + return CitationLocationPage( + end_page=end_page, + start_page=start_page, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["endPage"] = to_int(self.end_page) + result["startPage"] = to_int(self.start_page) + result["type"] = self.type + return result + + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class CitationReference: + "A single citation occurrence linking a span of generated text to a supporting source." + source_id: str + cited_text: str | None = None + location: CitationLocation | None = None + provider_metadata: Any = None + + @staticmethod + def from_dict(obj: Any) -> "CitationReference": + assert isinstance(obj, dict) + source_id = from_str(obj.get("sourceId")) + cited_text = from_union([from_none, from_str], obj.get("citedText")) + location = from_union([from_none, _load_CitationLocation], obj.get("location")) + provider_metadata = obj.get("providerMetadata") + return CitationReference( + source_id=source_id, + cited_text=cited_text, + location=location, + provider_metadata=provider_metadata, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["sourceId"] = from_str(self.source_id) + if self.cited_text is not None: + result["citedText"] = from_union([from_none, from_str], self.cited_text) + if self.location is not None: + result["location"] = from_union([from_none, lambda x: x.to_dict()], self.location) + if self.provider_metadata is not None: + result["providerMetadata"] = self.provider_metadata + return result + + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class CitationSource: + "A source that backs one or more cited spans in the assistant's response." + id: str + provider: CitationProvider + path: str | None = None + title: str | None = None + url: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "CitationSource": + assert isinstance(obj, dict) + id = from_str(obj.get("id")) + provider = parse_enum(CitationProvider, obj.get("provider")) + path = from_union([from_none, from_str], obj.get("path")) + title = from_union([from_none, from_str], obj.get("title")) + url = from_union([from_none, from_str], obj.get("url")) + return CitationSource( + id=id, + provider=provider, + path=path, + title=title, + url=url, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["id"] = from_str(self.id) + result["provider"] = to_enum(CitationProvider, self.provider) + if self.path is not None: + result["path"] = from_union([from_none, from_str], self.path) + if self.title is not None: + result["title"] = from_union([from_none, from_str], self.title) + if self.url is not None: + result["url"] = from_union([from_none, from_str], self.url) + return result + + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class CitationSpan: + "A contiguous span of generated assistant text and the source references that support it." + end_index: int + references: list[CitationReference] + start_index: int + + @staticmethod + def from_dict(obj: Any) -> "CitationSpan": + assert isinstance(obj, dict) + end_index = from_int(obj.get("endIndex")) + references = from_list(CitationReference.from_dict, obj.get("references")) + start_index = from_int(obj.get("startIndex")) + return CitationSpan( + end_index=end_index, + references=references, + start_index=start_index, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["endIndex"] = to_int(self.end_index) + result["references"] = from_list(lambda x: to_class(CitationReference, x), self.references) + result["startIndex"] = to_int(self.start_index) + return result + + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class Citations: + "Provider-agnostic citations linking spans of the assistant's response to their supporting sources." + sources: list[CitationSource] + spans: list[CitationSpan] + + @staticmethod + def from_dict(obj: Any) -> "Citations": + assert isinstance(obj, dict) + sources = from_list(CitationSource.from_dict, obj.get("sources")) + spans = from_list(CitationSpan.from_dict, obj.get("spans")) + return Citations( + sources=sources, + spans=spans, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["sources"] = from_list(lambda x: to_class(CitationSource, x), self.sources) + result["spans"] = from_list(lambda x: to_class(CitationSpan, x), self.spans) + return result + + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class OmittedBinaryResult: @@ -457,6 +700,8 @@ class AssistantMessageData: content: str message_id: str api_call_id: str | None = None + # Experimental: this field is part of an experimental API and may change or be removed. + citations: Citations | None = None encrypted_content: str | None = None interaction_id: str | None = None model: str | None = None @@ -478,6 +723,7 @@ def from_dict(obj: Any) -> "AssistantMessageData": content = from_str(obj.get("content")) message_id = from_str(obj.get("messageId")) api_call_id = from_union([from_none, from_str], obj.get("apiCallId")) + citations = from_union([from_none, Citations.from_dict], obj.get("citations")) encrypted_content = from_union([from_none, from_str], obj.get("encryptedContent")) interaction_id = from_union([from_none, from_str], obj.get("interactionId")) model = from_union([from_none, from_str], obj.get("model")) @@ -495,6 +741,7 @@ def from_dict(obj: Any) -> "AssistantMessageData": content=content, message_id=message_id, api_call_id=api_call_id, + citations=citations, encrypted_content=encrypted_content, interaction_id=interaction_id, model=model, @@ -516,6 +763,8 @@ def to_dict(self) -> dict: result["messageId"] = from_str(self.message_id) if self.api_call_id is not None: result["apiCallId"] = from_union([from_none, from_str], self.api_call_id) + if self.citations is not None: + result["citations"] = from_union([from_none, lambda x: to_class(Citations, x)], self.citations) if self.encrypted_content is not None: result["encryptedContent"] = from_union([from_none, from_str], self.encrypted_content) if self.interaction_id is not None: @@ -996,30 +1245,46 @@ def to_dict(self) -> dict: @dataclass class AttachmentBlob: "Blob attachment with inline base64-encoded data" - data: str mime_type: str type: ClassVar[str] = "blob" + asset_id: str | None = None + byte_length: int | None = None + data: str | None = None display_name: str | None = None + omitted_reason: OmittedBinaryOmittedReason | None = None @staticmethod def from_dict(obj: Any) -> "AttachmentBlob": assert isinstance(obj, dict) - data = from_str(obj.get("data")) mime_type = from_str(obj.get("mimeType")) + asset_id = from_union([from_none, from_str], obj.get("assetId")) + byte_length = from_union([from_none, from_int], obj.get("byteLength")) + data = from_union([from_none, from_str], obj.get("data")) display_name = from_union([from_none, from_str], obj.get("displayName")) + omitted_reason = from_union([from_none, lambda x: parse_enum(OmittedBinaryOmittedReason, x)], obj.get("omittedReason")) return AttachmentBlob( - data=data, mime_type=mime_type, + asset_id=asset_id, + byte_length=byte_length, + data=data, display_name=display_name, + omitted_reason=omitted_reason, ) def to_dict(self) -> dict: result: dict = {} - result["data"] = from_str(self.data) result["mimeType"] = from_str(self.mime_type) result["type"] = self.type + if self.asset_id is not None: + result["assetId"] = from_union([from_none, from_str], self.asset_id) + if self.byte_length is not None: + result["byteLength"] = from_union([from_none, to_int], self.byte_length) + if self.data is not None: + result["data"] = from_union([from_none, from_str], self.data) if self.display_name is not None: result["displayName"] = from_union([from_none, from_str], self.display_name) + if self.omitted_reason is not None: + result["omittedReason"] = from_union([from_none, lambda x: to_enum(OmittedBinaryOmittedReason, x)], self.omitted_reason) return result @@ -1098,18 +1363,30 @@ class AttachmentFile: display_name: str path: str type: ClassVar[str] = "file" + asset_id: str | None = None + byte_length: int | None = None line_range: AttachmentFileLineRange | None = None + mime_type: str | None = None + omitted_reason: OmittedBinaryOmittedReason | None = None @staticmethod def from_dict(obj: Any) -> "AttachmentFile": assert isinstance(obj, dict) display_name = from_str(obj.get("displayName")) path = from_str(obj.get("path")) + asset_id = from_union([from_none, from_str], obj.get("assetId")) + byte_length = from_union([from_none, from_int], obj.get("byteLength")) line_range = from_union([from_none, AttachmentFileLineRange.from_dict], obj.get("lineRange")) + mime_type = from_union([from_none, from_str], obj.get("mimeType")) + omitted_reason = from_union([from_none, lambda x: parse_enum(OmittedBinaryOmittedReason, x)], obj.get("omittedReason")) return AttachmentFile( display_name=display_name, path=path, + asset_id=asset_id, + byte_length=byte_length, line_range=line_range, + mime_type=mime_type, + omitted_reason=omitted_reason, ) def to_dict(self) -> dict: @@ -1117,8 +1394,16 @@ def to_dict(self) -> dict: result["displayName"] = from_str(self.display_name) result["path"] = from_str(self.path) result["type"] = self.type + if self.asset_id is not None: + result["assetId"] = from_union([from_none, from_str], self.asset_id) + if self.byte_length is not None: + result["byteLength"] = from_union([from_none, to_int], self.byte_length) if self.line_range is not None: result["lineRange"] = from_union([from_none, lambda x: to_class(AttachmentFileLineRange, x)], self.line_range) + if self.mime_type is not None: + result["mimeType"] = from_union([from_none, from_str], self.mime_type) + if self.omitted_reason is not None: + result["omittedReason"] = from_union([from_none, lambda x: to_enum(OmittedBinaryOmittedReason, x)], self.omitted_reason) return result @@ -1345,7 +1630,7 @@ class CanvasRegistryChangedCanvas: extension_id: str actions: list[CanvasRegistryChangedCanvasAction] | None = None extension_name: str | None = None - input_schema: dict[str, Any] | None = None + input_schema: Any = None @staticmethod def from_dict(obj: Any) -> "CanvasRegistryChangedCanvas": @@ -1356,7 +1641,7 @@ def from_dict(obj: Any) -> "CanvasRegistryChangedCanvas": extension_id = from_str(obj.get("extensionId")) actions = from_union([from_none, lambda x: from_list(CanvasRegistryChangedCanvasAction.from_dict, x)], obj.get("actions")) extension_name = from_union([from_none, from_str], obj.get("extensionName")) - input_schema = from_union([from_none, lambda x: from_dict(lambda x: x, x)], obj.get("inputSchema")) + input_schema = obj.get("inputSchema") return CanvasRegistryChangedCanvas( canvas_id=canvas_id, description=description, @@ -1378,7 +1663,7 @@ def to_dict(self) -> dict: if self.extension_name is not None: result["extensionName"] = from_union([from_none, from_str], self.extension_name) if self.input_schema is not None: - result["inputSchema"] = from_union([from_none, lambda x: from_dict(lambda x: x, x)], self.input_schema) + result["inputSchema"] = self.input_schema return result @@ -1387,14 +1672,14 @@ class CanvasRegistryChangedCanvasAction: "Schema for the `CanvasRegistryChangedCanvasAction` type." name: str description: str | None = None - input_schema: dict[str, Any] | None = None + input_schema: Any = None @staticmethod def from_dict(obj: Any) -> "CanvasRegistryChangedCanvasAction": assert isinstance(obj, dict) name = from_str(obj.get("name")) description = from_union([from_none, from_str], obj.get("description")) - input_schema = from_union([from_none, lambda x: from_dict(lambda x: x, x)], obj.get("inputSchema")) + input_schema = obj.get("inputSchema") return CanvasRegistryChangedCanvasAction( name=name, description=description, @@ -1407,7 +1692,7 @@ def to_dict(self) -> dict: if self.description is not None: result["description"] = from_union([from_none, from_str], self.description) if self.input_schema is not None: - result["inputSchema"] = from_union([from_none, lambda x: from_dict(lambda x: x, x)], self.input_schema) + result["inputSchema"] = self.input_schema return result @@ -2332,18 +2617,22 @@ def to_dict(self) -> dict: @dataclass class McpOauthCompletedData: "MCP OAuth request completion notification" + outcome: McpOauthCompletionOutcome request_id: str @staticmethod def from_dict(obj: Any) -> "McpOauthCompletedData": assert isinstance(obj, dict) + outcome = parse_enum(McpOauthCompletionOutcome, obj.get("outcome")) request_id = from_str(obj.get("requestId")) return McpOauthCompletedData( + outcome=outcome, request_id=request_id, ) def to_dict(self) -> dict: result: dict = {} + result["outcome"] = to_enum(McpOauthCompletionOutcome, self.outcome) result["requestId"] = from_str(self.request_id) return result @@ -2354,7 +2643,9 @@ class McpOauthRequiredData: request_id: str server_name: str server_url: str + resource_metadata: str | None = None static_client_config: McpOauthRequiredStaticClientConfig | None = None + www_authenticate_params: McpOauthWWWAuthenticateParams | None = None @staticmethod def from_dict(obj: Any) -> "McpOauthRequiredData": @@ -2362,12 +2653,16 @@ def from_dict(obj: Any) -> "McpOauthRequiredData": request_id = from_str(obj.get("requestId")) server_name = from_str(obj.get("serverName")) server_url = from_str(obj.get("serverUrl")) + resource_metadata = from_union([from_none, from_str], obj.get("resourceMetadata")) static_client_config = from_union([from_none, McpOauthRequiredStaticClientConfig.from_dict], obj.get("staticClientConfig")) + www_authenticate_params = from_union([from_none, McpOauthWWWAuthenticateParams.from_dict], obj.get("wwwAuthenticateParams")) return McpOauthRequiredData( request_id=request_id, server_name=server_name, server_url=server_url, + resource_metadata=resource_metadata, static_client_config=static_client_config, + www_authenticate_params=www_authenticate_params, ) def to_dict(self) -> dict: @@ -2375,8 +2670,12 @@ def to_dict(self) -> dict: result["requestId"] = from_str(self.request_id) result["serverName"] = from_str(self.server_name) result["serverUrl"] = from_str(self.server_url) + if self.resource_metadata is not None: + result["resourceMetadata"] = from_union([from_none, from_str], self.resource_metadata) if self.static_client_config is not None: result["staticClientConfig"] = from_union([from_none, lambda x: to_class(McpOauthRequiredStaticClientConfig, x)], self.static_client_config) + if self.www_authenticate_params is not None: + result["wwwAuthenticateParams"] = from_union([from_none, lambda x: to_class(McpOauthWWWAuthenticateParams, x)], self.www_authenticate_params) return result @@ -2409,6 +2708,35 @@ def to_dict(self) -> dict: return result +@dataclass +class McpOauthWWWAuthenticateParams: + "OAuth WWW-Authenticate parameters parsed from an MCP auth challenge" + resource_metadata_url: str + error: str | None = None + scope: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "McpOauthWWWAuthenticateParams": + assert isinstance(obj, dict) + resource_metadata_url = from_str(obj.get("resourceMetadataUrl")) + error = from_union([from_none, from_str], obj.get("error")) + scope = from_union([from_none, from_str], obj.get("scope")) + return McpOauthWWWAuthenticateParams( + resource_metadata_url=resource_metadata_url, + error=error, + scope=scope, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["resourceMetadataUrl"] = from_str(self.resource_metadata_url) + if self.error is not None: + result["error"] = from_union([from_none, from_str], self.error) + if self.scope is not None: + result["scope"] = from_union([from_none, from_str], self.scope) + return result + + @dataclass class McpServersLoadedServer: "Schema for the `McpServersLoadedServer` type." @@ -2462,8 +2790,11 @@ class ModelCallFailureData: "Failed LLM API call metadata for telemetry" source: ModelCallFailureSource api_call_id: str | None = None + bad_request_kind: ModelCallFailureBadRequestKind | None = None duration: timedelta | None = None + error_code: str | None = None error_message: str | None = None + error_type: str | None = None initiator: str | None = None model: str | None = None provider_call_id: str | None = None @@ -2475,8 +2806,11 @@ def from_dict(obj: Any) -> "ModelCallFailureData": assert isinstance(obj, dict) source = parse_enum(ModelCallFailureSource, obj.get("source")) api_call_id = from_union([from_none, from_str], obj.get("apiCallId")) + bad_request_kind = from_union([from_none, lambda x: parse_enum(ModelCallFailureBadRequestKind, x)], obj.get("badRequestKind")) duration = from_union([from_none, from_timedelta], obj.get("durationMs")) + error_code = from_union([from_none, from_str], obj.get("errorCode")) error_message = from_union([from_none, from_str], obj.get("errorMessage")) + error_type = from_union([from_none, from_str], obj.get("errorType")) initiator = from_union([from_none, from_str], obj.get("initiator")) model = from_union([from_none, from_str], obj.get("model")) provider_call_id = from_union([from_none, from_str], obj.get("providerCallId")) @@ -2485,8 +2819,11 @@ def from_dict(obj: Any) -> "ModelCallFailureData": return ModelCallFailureData( source=source, api_call_id=api_call_id, + bad_request_kind=bad_request_kind, duration=duration, + error_code=error_code, error_message=error_message, + error_type=error_type, initiator=initiator, model=model, provider_call_id=provider_call_id, @@ -2499,10 +2836,16 @@ def to_dict(self) -> dict: result["source"] = to_enum(ModelCallFailureSource, self.source) if self.api_call_id is not None: result["apiCallId"] = from_union([from_none, from_str], self.api_call_id) + if self.bad_request_kind is not None: + result["badRequestKind"] = from_union([from_none, lambda x: to_enum(ModelCallFailureBadRequestKind, x)], self.bad_request_kind) if self.duration is not None: result["durationMs"] = from_union([from_none, to_timedelta_int], self.duration) + if self.error_code is not None: + result["errorCode"] = from_union([from_none, from_str], self.error_code) if self.error_message is not None: result["errorMessage"] = from_union([from_none, from_str], self.error_message) + if self.error_type is not None: + result["errorType"] = from_union([from_none, from_str], self.error_type) if self.initiator is not None: result["initiator"] = from_union([from_none, from_str], self.initiator) if self.model is not None: @@ -2940,7 +3283,7 @@ class PermissionPromptRequestMcp: server_name: str tool_name: str tool_title: str - args: Any | None = None + args: Any = None tool_call_id: str | None = None @staticmethod @@ -2949,7 +3292,7 @@ def from_dict(obj: Any) -> "PermissionPromptRequestMcp": server_name = from_str(obj.get("serverName")) tool_name = from_str(obj.get("toolName")) tool_title = from_str(obj.get("toolTitle")) - args = from_union([from_none, lambda x: x], obj.get("args")) + args = obj.get("args") tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) return PermissionPromptRequestMcp( server_name=server_name, @@ -2966,7 +3309,7 @@ def to_dict(self) -> dict: result["toolName"] = from_str(self.tool_name) result["toolTitle"] = from_str(self.tool_title) if self.args is not None: - result["args"] = from_union([from_none, lambda x: x], self.args) + result["args"] = self.args if self.tool_call_id is not None: result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) return result @@ -6128,6 +6471,8 @@ class ToolExecutionCompleteResult: content: str # Experimental: this field is part of an experimental API and may change or be removed. binary_results_for_llm: list[PersistedBinaryResult] | None = None + # Experimental: this field is part of an experimental API and may change or be removed. + citable_sources: list[CitableSource] | None = None contents: list[ToolExecutionCompleteContent] | None = None detailed_content: str | None = None structured_content: Any = None @@ -6138,6 +6483,7 @@ def from_dict(obj: Any) -> "ToolExecutionCompleteResult": assert isinstance(obj, dict) content = from_str(obj.get("content")) binary_results_for_llm = from_union([from_none, lambda x: from_list(lambda x: from_union([PersistedBinaryImage.from_dict, OmittedBinaryResult.from_dict, BinaryAssetReference.from_dict], x), x)], obj.get("binaryResultsForLlm")) + citable_sources = from_union([from_none, lambda x: from_list(CitableSource.from_dict, x)], obj.get("citableSources")) contents = from_union([from_none, lambda x: from_list(_load_ToolExecutionCompleteContent, x)], obj.get("contents")) detailed_content = from_union([from_none, from_str], obj.get("detailedContent")) structured_content = obj.get("structuredContent") @@ -6145,6 +6491,7 @@ def from_dict(obj: Any) -> "ToolExecutionCompleteResult": return ToolExecutionCompleteResult( content=content, binary_results_for_llm=binary_results_for_llm, + citable_sources=citable_sources, contents=contents, detailed_content=detailed_content, structured_content=structured_content, @@ -6156,6 +6503,8 @@ def to_dict(self) -> dict: result["content"] = from_str(self.content) if self.binary_results_for_llm is not None: result["binaryResultsForLlm"] = from_union([from_none, lambda x: from_list(lambda x: from_union([lambda x: to_class(PersistedBinaryImage, x), lambda x: to_class(OmittedBinaryResult, x), lambda x: to_class(BinaryAssetReference, x)], x), x)], self.binary_results_for_llm) + if self.citable_sources is not None: + result["citableSources"] = from_union([from_none, lambda x: from_list(lambda x: to_class(CitableSource, x), x)], self.citable_sources) if self.contents is not None: result["contents"] = from_union([from_none, lambda x: from_list(lambda x: x.to_dict(), x)], self.contents) if self.detailed_content is not None: @@ -7023,6 +7372,16 @@ def _load_Attachment(obj: Any) -> "Attachment": case _: raise ValueError(f"Unknown Attachment type: {kind!r}") +def _load_CitationLocation(obj: Any) -> "CitationLocation": + assert isinstance(obj, dict) + kind = obj.get("type") + match kind: + case "char": return CitationLocationChar.from_dict(obj) + case "page": return CitationLocationPage.from_dict(obj) + case "block": return CitationLocationBlock.from_dict(obj) + case _: raise ValueError(f"Unknown CitationLocation type: {kind!r}") + + def _load_PermissionPromptRequest(obj: Any) -> "PermissionPromptRequest": assert isinstance(obj, dict) kind = obj.get("kind") @@ -7135,6 +7494,10 @@ def _load_UserToolSessionApproval(obj: Any) -> "UserToolSessionApproval": PermissionRequest = PermissionRequestShell | PermissionRequestWrite | PermissionRequestRead | PermissionRequestMcp | PermissionRequestUrl | PermissionRequestMemory | PermissionRequestCustomTool | PermissionRequestHook | PermissionRequestExtensionManagement | PermissionRequestExtensionPermissionAccess +# Location within a cited source (character, page, or content-block range) that supports a span. +CitationLocation = CitationLocationChar | CitationLocationPage | CitationLocationBlock + + # Structured metadata identifying what triggered this notification SystemNotification = SystemNotificationAgentCompleted | SystemNotificationAgentIdle | SystemNotificationNewInboxMessage | SystemNotificationShellCompleted | SystemNotificationShellDetachedCompleted | SystemNotificationInstructionDiscovered @@ -7151,6 +7514,17 @@ def _load_UserToolSessionApproval(obj: Any) -> "UserToolSessionApproval": PermissionResult = PermissionApproved | PermissionApprovedForSession | PermissionApprovedForLocation | PermissionCancelled | PermissionDeniedByRules | PermissionDeniedNoApprovalRuleAndCouldNotRequestFromUser | PermissionDeniedInteractivelyByUser | PermissionDeniedByContentExclusionPolicy | PermissionDeniedByPermissionRequestHook +# Experimental: this enum is part of an experimental API and may change or be removed. +class CitationProvider(Enum): + "The system that produced a citation." + # Citation produced by an Anthropic (Claude) model response. + ANTHROPIC = "anthropic" + # Citation produced by an OpenAI model response. + OPENAI = "openai" + # Citation synthesized client-side by the runtime from tool output. + CLIENT = "client" + + class AbortReason(Enum): "Finite reason code describing why the current turn was aborted" # The local user requested the abort, for example by pressing Ctrl+C in the CLI. @@ -7317,6 +7691,14 @@ class HandoffSourceType(Enum): LOCAL = "local" +class McpOauthCompletionOutcome(Enum): + "How the pending MCP OAuth request was completed" + # The request completed with a token-backed OAuth provider. + TOKEN = "token" + # The request completed without an OAuth provider. + CANCELLED = "cancelled" + + class McpServerSource(Enum): "Configuration source: user, workspace, plugin, or builtin" # Server configured in the user's global MCP configuration. @@ -7357,6 +7739,14 @@ class McpServerTransport(Enum): MEMORY = "memory" +class ModelCallFailureBadRequestKind(Enum): + "For HTTP 400 failures only: whether the response carried a structured CAPI error envelope (structured_error, a deterministic validation failure) or no error body (bodyless, the transient gateway/proxy signature). Absent for non-400 failures." + # The 400 response carried no error body (transient gateway/proxy signature). + BODYLESS = "bodyless" + # The 400 response carried a structured CAPI error envelope (deterministic validation failure). + STRUCTURED_ERROR = "structured_error" + + class ModelCallFailureSource(Enum): "Where the failed model call originated" # Model call from the top-level agent. @@ -7745,6 +8135,16 @@ def session_event_to_dict(x: SessionEvent) -> Any: "CanvasRegistryChangedCanvasAction", "CapabilitiesChangedData", "CapabilitiesChangedUI", + "CitableSource", + "CitationLocation", + "CitationLocationBlock", + "CitationLocationChar", + "CitationLocationPage", + "CitationProvider", + "CitationReference", + "CitationSource", + "CitationSpan", + "Citations", "CommandCompletedData", "CommandExecuteData", "CommandQueuedData", @@ -7781,12 +8181,15 @@ def session_event_to_dict(x: SessionEvent) -> Any: "McpAppToolCallCompleteToolMeta", "McpAppToolCallCompleteToolMetaUI", "McpOauthCompletedData", + "McpOauthCompletionOutcome", "McpOauthRequiredData", "McpOauthRequiredStaticClientConfig", + "McpOauthWWWAuthenticateParams", "McpServerSource", "McpServerStatus", "McpServerTransport", "McpServersLoadedServer", + "ModelCallFailureBadRequestKind", "ModelCallFailureData", "ModelCallFailureSource", "OmittedBinaryOmittedReason", diff --git a/python/e2e/testharness/context.py b/python/e2e/testharness/context.py index a5bfee28d..735c365c5 100644 --- a/python/e2e/testharness/context.py +++ b/python/e2e/testharness/context.py @@ -28,11 +28,15 @@ def get_cli_path_for_tests() -> str: if env_path and Path(env_path).exists(): return str(Path(env_path).resolve()) - # Look for CLI in sibling nodejs directory's node_modules + # Look for CLI in sibling nodejs directory's node_modules. As of CLI 1.0.64-1 + # the @github/copilot package is a thin loader; the runnable index.js ships in + # the installed platform package (e.g. @github/copilot-linux-x64). base_path = Path(__file__).parents[3] - full_path = base_path / "nodejs" / "node_modules" / "@github" / "copilot" / "index.js" - if full_path.exists(): - return str(full_path.resolve()) + github_modules = base_path / "nodejs" / "node_modules" / "@github" + for platform_pkg in sorted(github_modules.glob("copilot-*")): + candidate = platform_pkg / "index.js" + if candidate.exists(): + return str(candidate.resolve()) raise RuntimeError("CLI not found for tests. Run 'npm install' in the nodejs directory.") @@ -151,6 +155,12 @@ def get_env(self) -> dict: env.update( { "COPILOT_API_URL": self.proxy_url, + # Route GitHub API calls (e.g. the MCP registry policy check) to + # the replay proxy so MCP enablement stays hermetic. Without this + # the CLI reaches the real api.github.com, which is slow/unreachable + # on macOS CI runners and makes MCP servers time out before + # reaching connected. + "COPILOT_DEBUG_GITHUB_API_URL": self.proxy_url, "COPILOT_HOME": self.home_dir, "COPILOT_SDK_AUTH_TOKEN": DEFAULT_GITHUB_TOKEN, "GH_CONFIG_DIR": self.home_dir, diff --git a/rust/src/generated/api_types.rs b/rust/src/generated/api_types.rs index cd352a7fc..e9bd5ed86 100644 --- a/rust/src/generated/api_types.rs +++ b/rust/src/generated/api_types.rs @@ -89,6 +89,12 @@ pub mod rpc_methods { pub const RUNTIME_SHUTDOWN: &str = "runtime.shutdown"; /// `sessionFs.setProvider` pub const SESSIONFS_SETPROVIDER: &str = "sessionFs.setProvider"; + /// `llmInference.setProvider` + pub const LLMINFERENCE_SETPROVIDER: &str = "llmInference.setProvider"; + /// `llmInference.httpResponseStart` + pub const LLMINFERENCE_HTTPRESPONSESTART: &str = "llmInference.httpResponseStart"; + /// `llmInference.httpResponseChunk` + pub const LLMINFERENCE_HTTPRESPONSECHUNK: &str = "llmInference.httpResponseChunk"; /// `sessions.open` pub const SESSIONS_OPEN: &str = "sessions.open"; /// `sessions.fork` @@ -302,6 +308,9 @@ pub mod rpc_methods { pub const SESSION_MCP_ISSERVERRUNNING: &str = "session.mcp.isServerRunning"; /// `session.mcp.oauth.respond` pub const SESSION_MCP_OAUTH_RESPOND: &str = "session.mcp.oauth.respond"; + /// `session.mcp.oauth.handlePendingRequest` + pub const SESSION_MCP_OAUTH_HANDLEPENDINGREQUEST: &str = + "session.mcp.oauth.handlePendingRequest"; /// `session.mcp.oauth.login` pub const SESSION_MCP_OAUTH_LOGIN: &str = "session.mcp.oauth.login"; /// `session.mcp.apps.readResource` @@ -1335,13 +1344,23 @@ pub struct ApiKeyAuthInfo { #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct AttachmentBlob { - /// Base64-encoded content - pub data: String, + /// Internal: content-addressed id of the session.binary_asset event holding this attachment's model-facing bytes (e.g. "sha256:..."). Absent externally. + #[serde(skip_serializing_if = "Option::is_none")] + pub asset_id: Option, + /// Internal: decoded byte length of the attachment's model-facing bytes. Absent externally. + #[serde(skip_serializing_if = "Option::is_none")] + pub byte_length: Option, + /// Base64-encoded content. Present on input and for external consumers; replaced by an internal `assetId` reference in persisted events when interned to a content-addressed asset. + #[serde(skip_serializing_if = "Option::is_none")] + pub data: Option, /// User-facing display name for the attachment #[serde(skip_serializing_if = "Option::is_none")] pub display_name: Option, /// MIME type of the inline data pub mime_type: String, + /// Internal: why model-facing bytes are absent from persistence. Absent externally. + #[serde(skip_serializing_if = "Option::is_none")] + pub omitted_reason: Option, /// Attachment type discriminator pub r#type: AttachmentBlobType, } @@ -1423,11 +1442,23 @@ pub struct AttachmentFileLineRange { #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct AttachmentFile { + /// Internal: content-addressed id of the session.binary_asset event holding this attachment's model-facing bytes (e.g. "sha256:..."). Absent externally. + #[serde(skip_serializing_if = "Option::is_none")] + pub asset_id: Option, + /// Internal: decoded byte length of the attachment's model-facing bytes. Absent externally. + #[serde(skip_serializing_if = "Option::is_none")] + pub byte_length: Option, /// User-facing display name for the attachment pub display_name: String, /// Optional line range to scope the attachment to a specific section of the file #[serde(skip_serializing_if = "Option::is_none")] pub line_range: Option, + /// Internal: MIME type of the file's model-facing bytes (post-resize for images). Set when the file's bytes are interned to an asset. Absent externally. + #[serde(skip_serializing_if = "Option::is_none")] + pub mime_type: Option, + /// Internal: why model-facing bytes are absent from persistence. Absent externally. + #[serde(skip_serializing_if = "Option::is_none")] + pub omitted_reason: Option, /// Absolute file path pub path: String, /// Attachment type discriminator @@ -3295,6 +3326,167 @@ pub struct InstructionsGetSourcesResult { pub sources: Vec, } +/// A request body chunk or cancellation signal. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct LlmInferenceHttpRequestChunkRequest { + /// When true, `data` is base64-encoded bytes. When absent or false, `data` is UTF-8 text. + #[serde(skip_serializing_if = "Option::is_none")] + pub binary: Option, + /// When true, the runtime is cancelling the in-flight request (e.g. upstream consumer aborted). `data` is ignored. Implies end-of-request. + #[serde(skip_serializing_if = "Option::is_none")] + pub cancel: Option, + /// Optional human-readable reason for the cancellation, propagated for logging. + #[serde(skip_serializing_if = "Option::is_none")] + pub cancel_reason: Option, + /// Body byte range. UTF-8 text when `binary` is absent or false; base64-encoded bytes when `binary` is true. May be empty. + pub data: String, + /// When true, this is the final body chunk for the request. The SDK may rely on having received an end-marked chunk before treating the request body as complete. + #[serde(skip_serializing_if = "Option::is_none")] + pub end: Option, + /// Matches the requestId from the originating httpRequestStart frame. + pub request_id: RequestId, +} + +/// Acknowledgement. The SDK is free to ignore the ack and treat chunk delivery as fire-and-forget. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct LlmInferenceHttpRequestChunkResult {} + +/// The head of an outbound model-layer HTTP request. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct LlmInferenceHttpRequestStartRequest { + pub headers: HashMap>, + /// HTTP method, e.g. GET, POST. + pub method: String, + /// Opaque runtime-minted id, unique per in-flight request. The SDK uses this to correlate httpRequestChunk frames and to address its httpResponseStart / httpResponseChunk replies back to the runtime. + pub request_id: RequestId, + /// Id of the runtime session that triggered this request, when one is in scope. Absent for requests issued outside any session (e.g. startup model-catalog or capability resolution). This is a payload field — not a dispatch key — because the client-global API is registered process-wide rather than per session. + #[serde(skip_serializing_if = "Option::is_none")] + pub session_id: Option, + /// Transport the runtime would otherwise use for this request. `http` (the default when absent) covers plain HTTP and SSE responses; `websocket` indicates a full-duplex message channel where each body chunk maps to one WebSocket message and the `binary` flag distinguishes text from binary frames. The SDK consumer uses this to decide whether to service the request with an HTTP client or a WebSocket client. It is the one piece of request metadata the consumer cannot reliably infer from the URL or headers alone. + #[serde(skip_serializing_if = "Option::is_none")] + pub transport: Option, + /// Absolute request URL. + pub url: String, +} + +/// Acknowledgement. Returning successfully simply means the SDK accepted the start frame; it does not imply the request will succeed. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct LlmInferenceHttpRequestStartResult {} + +/// Set to terminate the response with a transport-level failure. Implies end-of-stream; any further chunks for this requestId are ignored. +/// +///

    +/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
    +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct LlmInferenceHttpResponseChunkError { + /// Optional machine-readable error code. + #[serde(skip_serializing_if = "Option::is_none")] + pub code: Option, + /// Human-readable failure description. + pub message: String, +} + +/// A response body chunk or terminal error. +/// +///
    +/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
    +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct LlmInferenceHttpResponseChunkRequest { + /// When true, `data` is base64-encoded bytes. When absent or false, `data` is UTF-8 text. + #[serde(skip_serializing_if = "Option::is_none")] + pub binary: Option, + /// Body byte range. UTF-8 text when `binary` is absent or false; base64-encoded bytes when `binary` is true. May be empty (e.g. when the response body is empty: send a single chunk with empty data and end=true). + pub data: String, + /// When true, this is the final body chunk for the response. The runtime treats the response body as complete after receiving an end-marked chunk. + #[serde(skip_serializing_if = "Option::is_none")] + pub end: Option, + /// Set to terminate the response with a transport-level failure. Implies end-of-stream; any further chunks for this requestId are ignored. + #[serde(skip_serializing_if = "Option::is_none")] + pub error: Option, + /// Matches the requestId from the originating httpRequestStart frame. + pub request_id: RequestId, +} + +/// Whether the chunk was accepted. +/// +///
    +/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
    +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct LlmInferenceHttpResponseChunkResult { + /// True when the chunk was matched to a pending request; false when unknown. + pub accepted: bool, +} + +/// Response head. +/// +///
    +/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
    +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct LlmInferenceHttpResponseStartRequest { + pub headers: HashMap>, + /// Matches the requestId from the originating httpRequestStart frame. + pub request_id: RequestId, + /// HTTP status code. + pub status: i64, + /// Optional HTTP status reason phrase. + #[serde(skip_serializing_if = "Option::is_none")] + pub status_text: Option, +} + +/// Whether the start frame was accepted. +/// +///
    +/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
    +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct LlmInferenceHttpResponseStartResult { + /// True when the response start was matched to a pending request; false when unknown. + pub accepted: bool, +} + +/// Indicates whether the calling client was registered as the LLM inference provider. +/// +///
    +/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
    +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct LlmInferenceSetProviderResult { + /// Whether the provider was set successfully + pub success: bool, +} + /// Pre-resolved working-directory context for session startup. /// ///
    @@ -4218,6 +4410,61 @@ pub struct McpListToolsResult { pub tools: Vec, } +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct McpOauthPendingRequestResponseToken { + /// Access token acquired by the SDK host + pub access_token: String, + /// Token lifetime in seconds, if known. + #[serde(skip_serializing_if = "Option::is_none")] + pub expires_in: Option, + pub kind: McpOauthPendingRequestResponseTokenKind, + /// Refresh token supplied by the host, if available. + #[serde(skip_serializing_if = "Option::is_none")] + pub refresh_token: Option, + /// OAuth token type. Defaults to Bearer when omitted. + #[serde(skip_serializing_if = "Option::is_none")] + pub token_type: Option, +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct McpOauthPendingRequestResponseCancelled { + pub kind: McpOauthPendingRequestResponseCancelledKind, +} + +/// Pending MCP OAuth request ID and host-provided token or cancellation response. +/// +///
    +/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
    +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct McpOauthHandlePendingRequest { + /// OAuth request identifier from the mcp.oauth_required event + pub request_id: RequestId, + /// Host response to the pending OAuth request. + pub result: McpOauthPendingRequestResponse, +} + +/// Indicates whether the pending MCP OAuth response was accepted. +/// +///
    +/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
    +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct McpOauthHandlePendingResult { + /// Whether the response was accepted. False if the request was unknown, timed out, or already resolved. + pub success: bool, +} + /// Remote MCP server name and optional overrides controlling reauthentication, OAuth client display name, and the callback success-page copy. /// ///
    @@ -4882,15 +5129,24 @@ pub struct MetadataSnapshotRemoteMetadata { #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ModelBillingTokenPricesLongContext { - /// AI Credits cost per billing batch of cached tokens + /// Deprecated: use cacheReadPrice. AI Credits cost per billing batch of cached tokens #[serde(skip_serializing_if = "Option::is_none")] pub cache_price: Option, - /// Prompt token budget (max_prompt_tokens) for the long context tier. The total context window is this value plus the model's max_output_tokens. + /// AI Credits cost per billing batch of cached (read) tokens + #[serde(skip_serializing_if = "Option::is_none")] + pub cache_read_price: Option, + /// AI Credits cost per billing batch of cache-write (cache creation) tokens. + #[serde(skip_serializing_if = "Option::is_none")] + pub cache_write_price: Option, + /// Deprecated: use maxPromptTokens. Prompt token budget for the long context tier. The total context window is this value plus the model's max_output_tokens. #[serde(skip_serializing_if = "Option::is_none")] pub context_max: Option, /// AI Credits cost per billing batch of input tokens #[serde(skip_serializing_if = "Option::is_none")] pub input_price: Option, + /// Prompt token budget for the long context tier. The total context window is this value plus the model's max_output_tokens. + #[serde(skip_serializing_if = "Option::is_none")] + pub max_prompt_tokens: Option, /// AI Credits cost per billing batch of output tokens #[serde(skip_serializing_if = "Option::is_none")] pub output_price: Option, @@ -4903,10 +5159,16 @@ pub struct ModelBillingTokenPrices { /// Number of tokens per standard billing batch #[serde(skip_serializing_if = "Option::is_none")] pub batch_size: Option, - /// AI Credits cost per billing batch of cached tokens + /// Deprecated: use cacheReadPrice. AI Credits cost per billing batch of cached tokens #[serde(skip_serializing_if = "Option::is_none")] pub cache_price: Option, - /// Prompt token budget (max_prompt_tokens) for the default tier. The total context window is this value plus the model's max_output_tokens. + /// AI Credits cost per billing batch of cached (read) tokens + #[serde(skip_serializing_if = "Option::is_none")] + pub cache_read_price: Option, + /// AI Credits cost per billing batch of cache-write (cache creation) tokens. + #[serde(skip_serializing_if = "Option::is_none")] + pub cache_write_price: Option, + /// Deprecated: use maxPromptTokens. Prompt token budget for the default tier. The total context window is this value plus the model's max_output_tokens. #[serde(skip_serializing_if = "Option::is_none")] pub context_max: Option, /// AI Credits cost per billing batch of input tokens @@ -4915,6 +5177,9 @@ pub struct ModelBillingTokenPrices { /// Long context tier pricing (available for models with extended context windows) #[serde(skip_serializing_if = "Option::is_none")] pub long_context: Option, + /// Prompt token budget for the default tier. The total context window is this value plus the model's max_output_tokens. + #[serde(skip_serializing_if = "Option::is_none")] + pub max_prompt_tokens: Option, /// AI Credits cost per billing batch of output tokens #[serde(skip_serializing_if = "Option::is_none")] pub output_price: Option, @@ -8293,7 +8558,7 @@ pub struct SandboxConfig { pub add_current_working_directory: Option, /// Raw `ContainerConfig` (per `@microsoft/mxc-sdk`) passed directly to `spawnSandboxFromConfig`, bypassing policy merging. #[serde(skip_serializing_if = "Option::is_none")] - pub config: Option>, + pub config: Option, /// Whether sandboxing is enabled for the session. pub enabled: bool, /// User-managed sandbox policy fragment merged into the auto-discovered base policy. @@ -9435,6 +9700,16 @@ pub struct SessionOpenOptions { /// Skill IDs disabled for this session. #[serde(skip_serializing_if = "Option::is_none")] pub disabled_skills: Option>, + /// Experimental: enable native model citations (Anthropic models today), normalized onto the `assistant.message` event. Off by default; may change or be removed while the citations surface is experimental. + /// + ///
    + /// + /// **Experimental.** This type is part of an experimental wire-protocol surface + /// and may change or be removed in future SDK or CLI releases. + /// + ///
    + #[serde(skip_serializing_if = "Option::is_none")] + pub enable_citations: Option, /// Whether on-demand custom instruction discovery is enabled. #[serde(skip_serializing_if = "Option::is_none")] pub enable_on_demand_instruction_discovery: Option, @@ -14746,6 +15021,21 @@ pub struct SessionMcpIsServerRunningResult { #[serde(rename_all = "camelCase")] pub struct SessionMcpOauthRespondResult {} +/// Indicates whether the pending MCP OAuth response was accepted. +/// +///
    +/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
    +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SessionMcpOauthHandlePendingRequestResult { + /// Whether the response was accepted. False if the request was unknown, timed out, or already resolved. + pub success: bool, +} + /// OAuth authorization URL the caller should open, or empty when cached tokens already authenticated the server. /// ///
    @@ -16431,6 +16721,16 @@ pub struct CanvasOpenResult { pub url: Option, } +/// HTTP headers as a map from lowercased header name to a list of values. Multi-valued headers (e.g. Set-Cookie) preserve all values. +/// +///
    +/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
    +pub type LlmInferenceHeaders = HashMap>; + /// MCP CreateMessageResult payload (with optional 'tools' extension), present when action='success'. Treated as opaque at the schema layer; consumers should construct/consume it per the MCP CreateMessageResult shape. /// ///
    @@ -16789,6 +17089,28 @@ pub enum ApiKeyAuthInfoType { ApiKey, } +/// Why the binary data is absent: it exceeded the inline size limit, or its asset was unavailable +/// +///
    +/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
    +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum OmittedBinaryOmittedReason { + /// Bytes exceeded the session's inline size limit. + #[serde(rename = "too_large")] + TooLarge, + /// The referenced binary asset could not be found (e.g. a truncated log). + #[serde(rename = "asset_unavailable")] + AssetUnavailable, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// Attachment type discriminator #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum AttachmentBlobType { @@ -17405,6 +17727,21 @@ pub enum InstructionSourceType { Unknown, } +/// Transport the runtime would otherwise use for this request. `http` (the default when absent) covers plain HTTP and SSE responses; `websocket` indicates a full-duplex message channel where each body chunk maps to one WebSocket message and the `binary` flag distinguishes text from binary frames. The SDK consumer uses this to decide whether to service the request with an HTTP client or a WebSocket client. It is the one piece of request metadata the consumer cannot reliably infer from the URL or headers alone. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum LlmInferenceHttpRequestStartTransport { + /// Plain HTTP or SSE response. Each body chunk is an opaque byte range; the response is a status line, headers, and a (possibly streamed) body. + #[serde(rename = "http")] + Http, + /// Full-duplex WebSocket channel. Each body chunk maps to exactly one WebSocket message and the `binary` flag distinguishes text from binary frames; request and response chunks flow concurrently. + #[serde(rename = "websocket")] + Websocket, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// Repository host type /// ///
    @@ -17646,6 +17983,35 @@ pub enum McpAppsSetHostContextDetailsTheme { Unknown, } +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum McpOauthPendingRequestResponseTokenKind { + #[serde(rename = "token")] + #[default] + Token, +} + +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum McpOauthPendingRequestResponseCancelledKind { + #[serde(rename = "cancelled")] + #[default] + Cancelled, +} + +/// Host response to the pending OAuth request. +/// +///
    +/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
    +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum McpOauthPendingRequestResponse { + Token(McpOauthPendingRequestResponseToken), + Cancelled(McpOauthPendingRequestResponseCancelled), +} + /// Outcome of the sampling inference. 'success' produced a response; 'failure' encountered an error (including agent-side rejection by content filter or criteria); 'cancelled' the caller cancelled this execution via cancelSamplingExecution. /// ///
    diff --git a/rust/src/generated/rpc.rs b/rust/src/generated/rpc.rs index 82319dbbf..fc969c188 100644 --- a/rust/src/generated/rpc.rs +++ b/rust/src/generated/rpc.rs @@ -49,6 +49,13 @@ impl<'a> ClientRpc<'a> { } } + /// `llmInference.*` sub-namespace. + pub fn llm_inference(&self) -> ClientRpcLlmInference<'a> { + ClientRpcLlmInference { + client: self.client, + } + } + /// `mcp.*` sub-namespace. pub fn mcp(&self) -> ClientRpcMcp<'a> { ClientRpcMcp { @@ -386,6 +393,106 @@ impl<'a> ClientRpcInstructions<'a> { } } +/// `llmInference.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcLlmInference<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcLlmInference<'a> { + /// Registers an SDK client as the LLM inference callback provider. + /// + /// Wire method: `llmInference.setProvider`. + /// + /// # Returns + /// + /// Indicates whether the calling client was registered as the LLM inference provider. + /// + ///
    + /// + /// **Experimental.** This API is part of an experimental wire-protocol surface + /// and may change or be removed in future SDK or CLI releases. Pin both the + /// SDK and CLI versions if your code depends on it. + /// + ///
    + pub async fn set_provider(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::LLMINFERENCE_SETPROVIDER, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Delivers the response head (status + headers) for an in-flight request, correlated by the requestId the runtime supplied in httpRequestStart. Must be called exactly once per request before any httpResponseChunk frames. + /// + /// Wire method: `llmInference.httpResponseStart`. + /// + /// # Parameters + /// + /// * `params` - Response head. + /// + /// # Returns + /// + /// Whether the start frame was accepted. + /// + ///
    + /// + /// **Experimental.** This API is part of an experimental wire-protocol surface + /// and may change or be removed in future SDK or CLI releases. Pin both the + /// SDK and CLI versions if your code depends on it. + /// + ///
    + pub async fn http_response_start( + &self, + params: LlmInferenceHttpResponseStartRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call( + rpc_methods::LLMINFERENCE_HTTPRESPONSESTART, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Delivers a body byte range (or a terminal transport error) for an in-flight response, correlated by requestId. Set `end` true on the last chunk. When `error` is set the response terminates with a transport-level failure and the runtime raises an APIConnectionError. + /// + /// Wire method: `llmInference.httpResponseChunk`. + /// + /// # Parameters + /// + /// * `params` - A response body chunk or terminal error. + /// + /// # Returns + /// + /// Whether the chunk was accepted. + /// + ///
    + /// + /// **Experimental.** This API is part of an experimental wire-protocol surface + /// and may change or be removed in future SDK or CLI releases. Pin both the + /// SDK and CLI versions if your code depends on it. + /// + ///
    + pub async fn http_response_chunk( + &self, + params: LlmInferenceHttpResponseChunkRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call( + rpc_methods::LLMINFERENCE_HTTPRESPONSECHUNK, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } +} + /// `mcp.*` RPCs. #[derive(Clone, Copy)] pub struct ClientRpcMcp<'a> { @@ -4428,7 +4535,7 @@ pub struct SessionRpcMcpOauth<'a> { } impl<'a> SessionRpcMcpOauth<'a> { - /// Responds to a pending MCP OAuth provider request. Marked internal because the `provider` argument is an in-process OAuthClientProvider instance that cannot be carried over the wire; the public OAuth surface will route the response through a wire-clean handshake once the CLI moves on top of the SDK. + /// Responds to a pending MCP OAuth request with an in-process provider. This internal CLI-only API accepts a live OAuthClientProvider instance and cannot be used over the SDK JSON-RPC boundary. Use session.mcp.oauth.handlePendingRequest instead for the public SDK-safe response path. /// /// Wire method: `session.mcp.oauth.respond`. /// @@ -4461,6 +4568,42 @@ impl<'a> SessionRpcMcpOauth<'a> { Ok(serde_json::from_value(_value)?) } + /// Resolves a pending MCP OAuth request with a host-provided token or cancellation. The pending request is emitted as mcp.oauth_required with the data necessary to authorize the request. + /// + /// Wire method: `session.mcp.oauth.handlePendingRequest`. + /// + /// # Parameters + /// + /// * `params` - Pending MCP OAuth request ID and host-provided token or cancellation response. + /// + /// # Returns + /// + /// Indicates whether the pending MCP OAuth response was accepted. + /// + ///
    + /// + /// **Experimental.** This API is part of an experimental wire-protocol surface + /// and may change or be removed in future SDK or CLI releases. Pin both the + /// SDK and CLI versions if your code depends on it. + /// + ///
    + pub async fn handle_pending_request( + &self, + params: McpOauthHandlePendingRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_MCP_OAUTH_HANDLEPENDINGREQUEST, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Starts OAuth authentication for a remote MCP server. /// /// Wire method: `session.mcp.oauth.login`. diff --git a/rust/src/generated/session_events.rs b/rust/src/generated/session_events.rs index 639b466d1..0fe47f263 100644 --- a/rust/src/generated/session_events.rs +++ b/rust/src/generated/session_events.rs @@ -1233,6 +1233,92 @@ pub struct AssistantStreamingDeltaData { pub total_response_size_bytes: i64, } +/// A source that backs one or more cited spans in the assistant's response. +/// +///
    +/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
    +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CitationSource { + /// Stable, turn-scoped identifier for this source, referenced by CitationReference.sourceId. + pub id: String, + /// File path relative to the agent's workspace root, when the source is a file. + #[serde(skip_serializing_if = "Option::is_none")] + pub path: Option, + /// The system that produced this citation. + pub provider: CitationProvider, + /// Human-readable title of the source. + #[serde(skip_serializing_if = "Option::is_none")] + pub title: Option, + /// URL of the source, when it is a web resource. + #[serde(skip_serializing_if = "Option::is_none")] + pub url: Option, +} + +/// A single citation occurrence linking a span of generated text to a supporting source. +/// +///
    +/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
    +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CitationReference { + /// The exact text from the source that supports the cited span, when provided by the model. + #[serde(skip_serializing_if = "Option::is_none")] + pub cited_text: Option, + /// Location within the source that supports the cited span, when the provider reports one. + #[serde(skip_serializing_if = "Option::is_none")] + pub location: Option, + /// Provider-native citation correlation data (e.g. Anthropic search_result_index / document_index), passed through opaquely for debugging and forward compatibility. + #[serde(skip_serializing_if = "Option::is_none")] + pub provider_metadata: Option, + /// Identifier of the CitationSource this reference points to (CitationSource.id). + pub source_id: String, +} + +/// A contiguous span of generated assistant text and the source references that support it. +/// +///
    +/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
    +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CitationSpan { + /// End offset of the cited span within the final assistant message content (UTF-16 code units, zero-based, exclusive). + pub end_index: i64, + /// The sources that support this span of generated text. + pub references: Vec, + /// Start offset of the cited span within the final assistant message content (UTF-16 code units, zero-based, inclusive). + pub start_index: i64, +} + +/// Provider-agnostic citations linking spans of the assistant's response to their supporting sources. +/// +///
    +/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
    +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Citations { + /// Deduplicated set of sources referenced by the citation spans. + pub sources: Vec, + /// Spans of generated text annotated with the sources that support them. + pub spans: Vec, +} + /// Neutral provider-tagged server-side tool-use payload (tool search, advisor) for verbatim round-tripping /// ///
    @@ -1290,6 +1376,16 @@ pub struct AssistantMessageData { /// Provider's completion / response identifier; shared across all chunks of a single API call. Used to group multi-chunk assistant utterances. #[serde(skip_serializing_if = "Option::is_none")] pub api_call_id: Option, + /// Provider-agnostic citations linking spans of this message's content to the sources that support them. Experimental; only populated when citation emission is enabled. + /// + ///
    + /// + /// **Experimental.** This type is part of an experimental wire-protocol surface + /// and may change or be removed in future SDK or CLI releases. + /// + ///
    + #[serde(skip_serializing_if = "Option::is_none")] + pub citations: Option, /// The assistant's text response content pub content: String, /// Encrypted reasoning content from OpenAI models. Session-bound and stripped on resume. @@ -1513,12 +1609,21 @@ pub struct ModelCallFailureData { /// Completion ID from the model provider (e.g., chatcmpl-abc123) #[serde(skip_serializing_if = "Option::is_none")] pub api_call_id: Option, + /// For HTTP 400 failures only: whether the response carried a structured CAPI error envelope (structured_error, a deterministic validation failure) or no error body (bodyless, the transient gateway/proxy signature). Absent for non-400 failures. + #[serde(skip_serializing_if = "Option::is_none")] + pub bad_request_kind: Option, /// Duration of the failed API call in milliseconds #[serde(skip_serializing_if = "Option::is_none")] pub duration_ms: Option, + /// For HTTP 400 failures only: the `code` from the CAPI error envelope (e.g. 'model_max_prompt_tokens_exceeded') identifying which deterministic validation failure occurred. Raw server-controlled string, emitted only through restricted telemetry. Absent for bodyless or non-400 failures. + #[serde(skip_serializing_if = "Option::is_none")] + pub error_code: Option, /// Raw provider/runtime error message for restricted telemetry #[serde(skip_serializing_if = "Option::is_none")] pub error_message: Option, + /// For HTTP 400 failures only: the `type` from the CAPI error envelope (e.g. 'websocket_error'), a coarser companion to errorCode for envelopes that carry no code. Raw server-controlled string, emitted only through restricted telemetry. Absent for bodyless or non-400 failures. + #[serde(skip_serializing_if = "Option::is_none")] + pub error_type: Option, /// What initiated this API call (e.g., "sub-agent", "mcp-sampling"); absent for user-initiated calls #[serde(skip_serializing_if = "Option::is_none")] pub initiator: Option, @@ -1661,6 +1766,32 @@ pub struct ToolExecutionCompleteError { pub message: String, } +/// A source supplied by a tool that should be made available to the model as citable content. +/// +///
    +/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
    +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CitableSource { + /// The source text made available to the model as citable content. + pub content: String, + /// Stable identifier for this source within the tool result. Used for deduplication and may be used by future provider integrations to correlate response citations back to the originating source. + pub id: String, + /// File path relative to the agent's workspace root, when the source is a file. + #[serde(skip_serializing_if = "Option::is_none")] + pub path: Option, + /// Human-readable title of the source. + #[serde(skip_serializing_if = "Option::is_none")] + pub title: Option, + /// URL of the source, when it is a web resource. + #[serde(skip_serializing_if = "Option::is_none")] + pub url: Option, +} + /// Plain text content block #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -1901,6 +2032,16 @@ pub struct ToolExecutionCompleteResult { ///
    #[serde(skip_serializing_if = "Option::is_none")] pub binary_results_for_llm: Option>, + /// Provider-neutral source material this tool makes available to the model as citable content. Persisted so it survives session resume. Experimental. + /// + ///
    + /// + /// **Experimental.** This type is part of an experimental wire-protocol surface + /// and may change or be removed in future SDK or CLI releases. + /// + ///
    + #[serde(skip_serializing_if = "Option::is_none")] + pub citable_sources: Option>, /// Concise tool result text sent to the LLM for chat completion, potentially truncated for token efficiency pub content: String, /// Structured content blocks (text, images, audio, resources) returned by the tool in their native format @@ -2979,12 +3120,29 @@ pub struct McpOauthRequiredStaticClientConfig { pub public_client: Option, } +/// OAuth WWW-Authenticate parameters parsed from an MCP auth challenge +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct McpOauthWWWAuthenticateParams { + /// OAuth error from the WWW-Authenticate error parameter, if present + #[serde(skip_serializing_if = "Option::is_none")] + pub error: Option, + /// Protected resource metadata URL from the WWW-Authenticate resource_metadata parameter + pub resource_metadata_url: String, + /// Requested OAuth scopes from the WWW-Authenticate scope parameter, if present + #[serde(skip_serializing_if = "Option::is_none")] + pub scope: Option, +} + /// Session event "mcp.oauth_required". OAuth authentication request for an MCP server #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct McpOauthRequiredData { - /// Unique identifier for this OAuth request; used to respond via session.respondToMcpOAuth() + /// Unique identifier for this OAuth request; used to respond via session.mcp.oauth.handlePendingRequest pub request_id: RequestId, + /// Raw OAuth protected-resource metadata document fetched for the MCP server, if available + #[serde(skip_serializing_if = "Option::is_none")] + pub resource_metadata: Option, /// Display name of the MCP server that requires OAuth pub server_name: String, /// URL of the MCP server that requires OAuth @@ -2992,12 +3150,17 @@ pub struct McpOauthRequiredData { /// Static OAuth client configuration, if the server specifies one #[serde(skip_serializing_if = "Option::is_none")] pub static_client_config: Option, + /// OAuth WWW-Authenticate parameters parsed from the auth challenge, if available + #[serde(skip_serializing_if = "Option::is_none")] + pub www_authenticate_params: Option, } /// Session event "mcp.oauth_completed". MCP OAuth request completion notification #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct McpOauthCompletedData { + /// How the pending OAuth request was completed + pub outcome: McpOauthCompletionOutcome, /// Request ID of the resolved OAuth request pub request_id: RequestId, } @@ -3372,7 +3535,7 @@ pub struct CanvasRegistryChangedCanvasAction { pub description: Option, /// JSON Schema for action input #[serde(skip_serializing_if = "Option::is_none")] - pub input_schema: Option>, + pub input_schema: Option, /// Action name pub name: String, } @@ -3397,7 +3560,7 @@ pub struct CanvasRegistryChangedCanvas { pub extension_name: Option, /// JSON Schema for canvas open input #[serde(skip_serializing_if = "Option::is_none")] - pub input_schema: Option>, + pub input_schema: Option, } /// Session event "session.canvas.registry_changed". @@ -3672,6 +3835,31 @@ pub enum UserMessageAgentMode { Unknown, } +/// The system that produced a citation. +/// +///
    +/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
    +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum CitationProvider { + /// Citation produced by an Anthropic (Claude) model response. + #[serde(rename = "anthropic")] + Anthropic, + /// Citation produced by an OpenAI model response. + #[serde(rename = "openai")] + Openai, + /// Citation synthesized client-side by the runtime from tool output. + #[serde(rename = "client")] + Client, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// Tool call type: "function" for standard tool calls, "custom" for grammar-based tool calls. Defaults to "function" when absent. #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum AssistantMessageToolRequestType { @@ -3708,6 +3896,21 @@ pub enum AssistantUsageApiEndpoint { Unknown, } +/// For HTTP 400 failures only: whether the response carried a structured CAPI error envelope (structured_error, a deterministic validation failure) or no error body (bodyless, the transient gateway/proxy signature). Absent for non-400 failures. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum ModelCallFailureBadRequestKind { + /// The 400 response carried no error body (transient gateway/proxy signature). + #[serde(rename = "bodyless")] + Bodyless, + /// The 400 response carried a structured CAPI error envelope (deterministic validation failure). + #[serde(rename = "structured_error")] + StructuredError, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// Where the failed model call originated #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum ModelCallFailureSource { @@ -4370,6 +4573,21 @@ pub enum McpOauthRequiredStaticClientConfigGrantType { ClientCredentials, } +/// How the pending MCP OAuth request was completed +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum McpOauthCompletionOutcome { + /// The request completed with a token-backed OAuth provider. + #[serde(rename = "token")] + Token, + /// The request completed without an OAuth provider. + #[serde(rename = "cancelled")] + Cancelled, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// The user's auto-mode-switch choice #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum AutoModeSwitchResponse { diff --git a/rust/tests/e2e/support.rs b/rust/tests/e2e/support.rs index 742d5834d..5554c5a06 100644 --- a/rust/tests/e2e/support.rs +++ b/rust/tests/e2e/support.rs @@ -517,21 +517,29 @@ fn cli_path(repo_root: &Path) -> std::io::Result { } } - let path = repo_root + // The `@github/copilot` package is a thin loader; the runnable `index.js` + // ships in a platform-specific `@github/copilot--` package, + // exactly one of which is installed. Resolve whichever one is present. + let github_dir = repo_root .join("nodejs") .join("node_modules") - .join("@github") - .join("copilot") - .join("index.js"); - if path.exists() { - return Ok(path); + .join("@github"); + if let Ok(entries) = std::fs::read_dir(&github_dir) { + for entry in entries.flatten() { + if entry.file_name().to_string_lossy().starts_with("copilot-") { + let candidate = entry.path().join("index.js"); + if candidate.exists() { + return Ok(candidate); + } + } + } } Err(std::io::Error::new( std::io::ErrorKind::NotFound, format!( - "CLI not found at {}; run npm install in nodejs first", - path.display() + "CLI not found under {}; run npm install in nodejs first", + github_dir.display() ), )) } diff --git a/test/harness/package-lock.json b/test/harness/package-lock.json index 5a6e086e0..a71ef1835 100644 --- a/test/harness/package-lock.json +++ b/test/harness/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "devDependencies": { - "@github/copilot": "^1.0.64-0", + "@github/copilot": "^1.0.64-1", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "@types/node-forge": "^1.3.14", @@ -501,33 +501,32 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.64-0", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.64-0.tgz", - "integrity": "sha512-PlH7ByBHjmPLqLXS4CE2q8hN6CFEfkCMV6ScBEzW/u73+KYQB4fGNouo8Lr8okL6D5CW5rzPJbsXyISyJqVOZg==", + "version": "1.0.64-1", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.64-1.tgz", + "integrity": "sha512-lojV4Cb7oT4VJnYPEKBRH8KI3W43Q4Lh0Pc+V6sej+xjPJkoqwm68sNKn73/p3wXPBSTVTzPeCm9WhIisgf1Jw==", "dev": true, "license": "SEE LICENSE IN LICENSE.md", "dependencies": { - "detect-libc": "^2.1.2", - "os-theme": "^0.0.8" + "detect-libc": "^2.1.2" }, "bin": { "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.64-0", - "@github/copilot-darwin-x64": "1.0.64-0", - "@github/copilot-linux-arm64": "1.0.64-0", - "@github/copilot-linux-x64": "1.0.64-0", - "@github/copilot-linuxmusl-arm64": "1.0.64-0", - "@github/copilot-linuxmusl-x64": "1.0.64-0", - "@github/copilot-win32-arm64": "1.0.64-0", - "@github/copilot-win32-x64": "1.0.64-0" + "@github/copilot-darwin-arm64": "1.0.64-1", + "@github/copilot-darwin-x64": "1.0.64-1", + "@github/copilot-linux-arm64": "1.0.64-1", + "@github/copilot-linux-x64": "1.0.64-1", + "@github/copilot-linuxmusl-arm64": "1.0.64-1", + "@github/copilot-linuxmusl-x64": "1.0.64-1", + "@github/copilot-win32-arm64": "1.0.64-1", + "@github/copilot-win32-x64": "1.0.64-1" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.64-0", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.64-0.tgz", - "integrity": "sha512-97DUGiuYrkCYOlSSLWMmr+K0uGzAxz1JOL/GyO/7mNl6V/1xgs6Van1Jj+Dpj4ly96iHE8lUIW8cQNCG66644g==", + "version": "1.0.64-1", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.64-1.tgz", + "integrity": "sha512-MQHZT9LhmCiq+ogO1E8cPCWrurZ6x+r9lPJfYUSnOyMO+EHbREpiJwOOChxtLHgL2/tKJSZdId2pg3tDgUlcsw==", "cpu": [ "arm64" ], @@ -542,9 +541,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.64-0", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.64-0.tgz", - "integrity": "sha512-2PXY4mSFtIjFdRaAt8PakegRgGtf6Sz9z6U/dIgVygNfctVNzaL5FH65PNPm8Y80jaDvEcz1/XY5MiQtxnlzZQ==", + "version": "1.0.64-1", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.64-1.tgz", + "integrity": "sha512-kOQY7CvI7He0eO3ObQAHePWdkNLWAOegCSzUqUmdcpa1SNVqbZ3GBMsQ7uAZQip2cQxnGZ7pS1v6tKQ0HJdkYw==", "cpu": [ "x64" ], @@ -559,9 +558,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.64-0", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.64-0.tgz", - "integrity": "sha512-PLP+vR508fOTlCr9CSZiXi9geicHKXuX9jLGdwNqK2TMZO5TqCLz8wP7dBEmkdkeXcFKovMb8nQVB1Toc6xutw==", + "version": "1.0.64-1", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.64-1.tgz", + "integrity": "sha512-hIfuO7Q+pWs0SKfIRYqT+CjMaupudnhp4RMS6XoJ5s/e33rvpj2tkTkXYlHJo1PMDI823vvbqgpEdr+KeewMwg==", "cpu": [ "arm64" ], @@ -576,9 +575,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.64-0", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.64-0.tgz", - "integrity": "sha512-NvVjQ69zr390ijzo2f75+v0DHm6xnvPbi67ugnKDk7ZPbx8P3vSxVdAnrzrrL4T3T8ng3pJANcC4p+eGbx+UDw==", + "version": "1.0.64-1", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.64-1.tgz", + "integrity": "sha512-VHaE62pha0rDDvuNN3bd97gf0EZ+EJebstM1ejHsMYoPT1IOUkYEXlNfGGHY+GfUGYxAiy/+Uew4xw5mJyy/Sw==", "cpu": [ "x64" ], @@ -593,9 +592,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.64-0", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.64-0.tgz", - "integrity": "sha512-qCnVF5vIcTO74CukAENZo8e5nqXm4QUshuKN69aiZb5GOhVvyyIKsf5Jo7ikZt54jJBHycAMUKlTA8L3/nK+KA==", + "version": "1.0.64-1", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.64-1.tgz", + "integrity": "sha512-L/YrZPotRujAP0QERq+DlkR1SLr7abbTSz/56JqKKOqEdjKZPdQW1bUlhL/w1CZg1gXlTNUsNVyKz/fUfrEBgw==", "cpu": [ "arm64" ], @@ -610,9 +609,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.64-0", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.64-0.tgz", - "integrity": "sha512-WDBEmkBk1RulTfdLK5IuttNBadjLOBpvQyonGQ/aLeaetRNNdapoygrSjFU7q1QBSenmCyanXH6D+TS7tP3Qsw==", + "version": "1.0.64-1", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.64-1.tgz", + "integrity": "sha512-AGMjXqR128oyjiJhoI6Gd7JP5ddWkib+P4YH/JoHm05iNn23ZYl4tSc0XihHzeyMI1ix7Aacn8UINYB7lGOGOA==", "cpu": [ "x64" ], @@ -627,9 +626,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.64-0", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.64-0.tgz", - "integrity": "sha512-PC7yuUKcVbhli4bpzWFVT3juxj+v/iONazetNe3tMpHWza3W7MeFRifzAseSErKQCt2fHJth3m8bQAwFN2jfrA==", + "version": "1.0.64-1", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.64-1.tgz", + "integrity": "sha512-vvv+gnemi9WKaxF41zz7Xmq6a493n8Yjps5UFaOY6a3WR222kKXZXfOpeRvIYsDgnIPHGBHIj1TBOmnHQT4V4w==", "cpu": [ "arm64" ], @@ -644,9 +643,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.64-0", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.64-0.tgz", - "integrity": "sha512-d2fnUTIlqNxCqS2PuV+FD99ZOYBaX72OLtAmphbKyz36KyZ6D4ssiu8M4vHVTKWWdyc3TWiLsnIB+ryWdv1gGw==", + "version": "1.0.64-1", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.64-1.tgz", + "integrity": "sha512-mcHvD0fjGDuqr/YXzy8mKuDmah1F+qjPujxoFuGmabmTJZ33cSIJ3nq7RRvxZNIdp8YJ57NkbcW30WvIcOeJ3w==", "cpu": [ "x64" ], @@ -740,48 +739,6 @@ "@emnapi/runtime": "^1.7.1" } }, - "node_modules/@os-theme/darwin-arm64": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/@os-theme/darwin-arm64/-/darwin-arm64-0.0.8.tgz", - "integrity": "sha512-gMsOs+8Ju396a5yyMWigkbA0dMTxD78U3HzG3mlpiAyn6hfd5dbyI4VGP+sfTB82KGgWLzIhWWTFX5UYY6iX0A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@os-theme/linux-x64": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/@os-theme/linux-x64/-/linux-x64-0.0.8.tgz", - "integrity": "sha512-zvjmBUiSQPjM1RbhpsfCDYMJxW4eLlGmkFPnpteC/03X2lz6CjiX2hfbN2EWLxXjNnIje3Jqaen8IsqEnWrRBg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@os-theme/win32-x64": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/@os-theme/win32-x64/-/win32-x64-0.0.8.tgz", - "integrity": "sha512-N3yxKNbVl2IBa/ncDuq55QhwqwUjnYLJxDKMEmYeJbLIV950qZNojPw3scXA6PbfxPZfIiRa8iz1pzNg9XxP8w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@oxc-project/types": { "version": "0.133.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", @@ -2482,21 +2439,6 @@ } } }, - "node_modules/os-theme": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/os-theme/-/os-theme-0.0.8.tgz", - "integrity": "sha512-u1q3bLSv5uMHNIiPItkfDrHXu6ZFs2juwqxWREFM/uVBa+7Kkhy2v49LmJev2JcinGwqiEccElB/XsH9gwasuA==", - "dev": true, - "license": "MIT", - "optionalDependencies": { - "@os-theme/darwin-arm64": "0.0.8", - "@os-theme/linux-x64": "0.0.8", - "@os-theme/win32-x64": "0.0.8" - }, - "peerDependencies": { - "typescript": "^5" - } - }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", diff --git a/test/harness/package.json b/test/harness/package.json index 669ebee1b..af588a91c 100644 --- a/test/harness/package.json +++ b/test/harness/package.json @@ -14,7 +14,7 @@ "node": "^20.19.0 || >=22.12.0" }, "devDependencies": { - "@github/copilot": "^1.0.64-0", + "@github/copilot": "^1.0.64-1", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "@types/node-forge": "^1.3.14", diff --git a/test/harness/replayingCapiProxy.ts b/test/harness/replayingCapiProxy.ts index 5e6b8e3f1..740f8ab74 100644 --- a/test/harness/replayingCapiProxy.ts +++ b/test/harness/replayingCapiProxy.ts @@ -241,34 +241,12 @@ export class ReplayingCapiProxy extends CapturingHttpProxy { return; } - const state = this.state; - if (!state) { - throw new Error( - "ReplayingCapiProxy not yet initialized. Either pass filePath and workDir to the constructor, " + - "or post configuration to /config before making other HTTP requests.", - ); - } - - // Handle /models endpoint - // Use stored models if available, otherwise use default model - if (options.requestOptions.path === "/models") { - const models = - state.storedData?.models && state.storedData.models.length > 0 - ? state.storedData.models - : [defaultModel]; - const modelsResponse = createGetModelsResponse(models); - const body = JSON.stringify(modelsResponse); - const headers = { - "content-type": "application/json", - ...commonResponseHeaders, - }; - options.onResponseStart(200, headers); - options.onData(Buffer.from(body)); - options.onResponseEnd(); - return; - } - - // Handle /copilot_internal/user endpoint for per-session auth + // Handle /copilot_internal/user endpoint for per-session auth. + // This must run before the state guard below: the CLI authenticates and + // calls /copilot_internal/user at startup, which can race ahead of the + // per-test POST /config (e.g. the Go harness spawns the CLI before the + // first ConfigureForTest). The response only depends on the token map, + // which is populated independently of `state`. if (options.requestOptions.path === "/copilot_internal/user") { const headers = options.requestOptions.headers; const headerMap = headers as @@ -283,9 +261,15 @@ export class ReplayingCapiProxy extends CapturingHttpProxy { ? rawAuthHeader : undefined; const token = authHeader?.replace("Bearer ", ""); - const userResponse = token + const registered = token ? this.copilotUserByToken.get(token) : undefined; + // The CLI gates third-party MCP servers behind the copilot user's + // `is_mcp_enabled` flag (a null/missing value disables them). Default + // it to true so e2e MCP servers are enabled unless a test opts out. + const userResponse = registered + ? ({ is_mcp_enabled: true, ...registered } as CopilotUserResponse) + : undefined; if (userResponse) { const headers = { "content-type": "application/json", @@ -304,6 +288,33 @@ export class ReplayingCapiProxy extends CapturingHttpProxy { return; } + const state = this.state; + if (!state) { + throw new Error( + "ReplayingCapiProxy not yet initialized. Either pass filePath and workDir to the constructor, " + + "or post configuration to /config before making other HTTP requests.", + ); + } + + // Handle /models endpoint + // Use stored models if available, otherwise use default model + if (options.requestOptions.path === "/models") { + const models = + state.storedData?.models && state.storedData.models.length > 0 + ? state.storedData.models + : [defaultModel]; + const modelsResponse = createGetModelsResponse(models); + const body = JSON.stringify(modelsResponse); + const headers = { + "content-type": "application/json", + ...commonResponseHeaders, + }; + options.onResponseStart(200, headers); + options.onData(Buffer.from(body)); + options.onResponseEnd(); + return; + } + // Handle memory endpoints - return stub responses in tests // Matches: /agents/*/memory/*/enabled, /agents/*/memory/*/recent, etc. if (options.requestOptions.path?.match(/\/agents\/.*\/memory\//)) { @@ -1504,6 +1515,7 @@ export type ToolResultNormalizer = { export type CopilotUserResponse = { login: string; copilot_plan?: string; + is_mcp_enabled?: boolean; endpoints?: { api?: string; telemetry?: string;