diff --git a/dotnet/src/Generated/Rpc.cs b/dotnet/src/Generated/Rpc.cs index b06b68676..82b8cb1a3 100644 --- a/dotnet/src/Generated/Rpc.cs +++ b/dotnet/src/Generated/Rpc.cs @@ -239,6 +239,42 @@ public class AccountGetQuotaResult public Dictionary QuotaSnapshots { get => field ??= []; set; } } +/// RPC data type for DiscoveredMcpServer operations. +public class DiscoveredMcpServer +{ + /// Server name (config key). + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Server type: local, stdio, http, or sse. + [JsonPropertyName("type")] + public string? Type { get; set; } + + /// Configuration source. + [JsonPropertyName("source")] + public DiscoveredMcpServerSource Source { get; set; } + + /// Whether the server is enabled (not in the disabled list). + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } +} + +/// RPC data type for McpDiscover operations. +public class McpDiscoverResult +{ + /// MCP servers discovered from all sources. + [JsonPropertyName("servers")] + public List Servers { get => field ??= []; set; } +} + +/// RPC data type for McpDiscover operations. +internal class McpDiscoverRequest +{ + /// Working directory used as context for discovery (e.g., plugin resolution). + [JsonPropertyName("workingDirectory")] + public string? WorkingDirectory { get; set; } +} + /// RPC data type for SessionFsSetProvider operations. public class SessionFsSetProviderResult { @@ -1067,15 +1103,15 @@ internal class SessionToolsHandlePendingToolCallRequest [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - /// Gets or sets the requestId value. + /// Request ID of the pending tool call. [JsonPropertyName("requestId")] public string RequestId { get; set; } = string.Empty; - /// Gets or sets the result value. + /// Tool call result (string or expanded result object). [JsonPropertyName("result")] public object? Result { get; set; } - /// Gets or sets the error value. + /// Error message if the tool call failed. [JsonPropertyName("error")] public string? Error { get; set; } } @@ -1083,7 +1119,7 @@ internal class SessionToolsHandlePendingToolCallRequest /// RPC data type for SessionCommandsHandlePendingCommand operations. public class SessionCommandsHandlePendingCommandResult { - /// Gets or sets the success value. + /// Whether the command was handled successfully. [JsonPropertyName("success")] public bool Success { get; set; } } @@ -1199,7 +1235,7 @@ internal class SessionPermissionsHandlePendingPermissionRequestRequest [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - /// Gets or sets the requestId value. + /// Request ID of the pending permission request. [JsonPropertyName("requestId")] public string RequestId { get; set; } = string.Empty; @@ -1260,6 +1296,34 @@ internal class SessionShellKillRequest public SessionShellKillRequestSignal? Signal { get; set; } } +/// Post-compaction context window usage breakdown. +public class SessionHistoryCompactResultContextWindow +{ + /// Maximum token count for the model's context window. + [JsonPropertyName("tokenLimit")] + public double TokenLimit { get; set; } + + /// Current total tokens in the context window (system + conversation + tool definitions). + [JsonPropertyName("currentTokens")] + public double CurrentTokens { get; set; } + + /// Current number of messages in the conversation. + [JsonPropertyName("messagesLength")] + public double MessagesLength { get; set; } + + /// Token count from system message(s). + [JsonPropertyName("systemTokens")] + public double? SystemTokens { get; set; } + + /// Token count from non-system messages (user, assistant, tool). + [JsonPropertyName("conversationTokens")] + public double? ConversationTokens { get; set; } + + /// Token count from tool definitions. + [JsonPropertyName("toolDefinitionsTokens")] + public double? ToolDefinitionsTokens { get; set; } +} + /// RPC data type for SessionHistoryCompact operations. [Experimental(Diagnostics.Experimental)] public class SessionHistoryCompactResult @@ -1275,6 +1339,10 @@ public class SessionHistoryCompactResult /// Number of messages removed during compaction. [JsonPropertyName("messagesRemoved")] public double MessagesRemoved { get; set; } + + /// Post-compaction context window usage breakdown. + [JsonPropertyName("contextWindow")] + public SessionHistoryCompactResultContextWindow? ContextWindow { get; set; } } /// RPC data type for SessionHistoryCompact operations. @@ -1308,6 +1376,116 @@ internal class SessionHistoryTruncateRequest public string EventId { get; set; } = string.Empty; } +/// Aggregated code change metrics. +public class SessionUsageGetMetricsResultCodeChanges +{ + /// Total lines of code added. + [JsonPropertyName("linesAdded")] + public double LinesAdded { get; set; } + + /// Total lines of code removed. + [JsonPropertyName("linesRemoved")] + public double LinesRemoved { get; set; } + + /// Number of distinct files modified. + [JsonPropertyName("filesModifiedCount")] + public double FilesModifiedCount { get; set; } +} + +/// Request count and cost metrics for this model. +public class SessionUsageGetMetricsResultModelMetricsValueRequests +{ + /// Number of API requests made with this model. + [JsonPropertyName("count")] + public double Count { get; set; } + + /// User-initiated premium request cost (with multiplier applied). + [JsonPropertyName("cost")] + public double Cost { get; set; } +} + +/// Token usage metrics for this model. +public class SessionUsageGetMetricsResultModelMetricsValueUsage +{ + /// Total input tokens consumed. + [JsonPropertyName("inputTokens")] + public double InputTokens { get; set; } + + /// Total output tokens produced. + [JsonPropertyName("outputTokens")] + public double OutputTokens { get; set; } + + /// Total tokens read from prompt cache. + [JsonPropertyName("cacheReadTokens")] + public double CacheReadTokens { get; set; } + + /// Total tokens written to prompt cache. + [JsonPropertyName("cacheWriteTokens")] + public double CacheWriteTokens { get; set; } +} + +/// RPC data type for SessionUsageGetMetricsResultModelMetricsValue operations. +public class SessionUsageGetMetricsResultModelMetricsValue +{ + /// Request count and cost metrics for this model. + [JsonPropertyName("requests")] + public SessionUsageGetMetricsResultModelMetricsValueRequests Requests { get => field ??= new(); set; } + + /// Token usage metrics for this model. + [JsonPropertyName("usage")] + public SessionUsageGetMetricsResultModelMetricsValueUsage Usage { get => field ??= new(); set; } +} + +/// RPC data type for SessionUsageGetMetrics operations. +[Experimental(Diagnostics.Experimental)] +public class SessionUsageGetMetricsResult +{ + /// Total user-initiated premium request cost across all models (may be fractional due to multipliers). + [JsonPropertyName("totalPremiumRequestCost")] + public double TotalPremiumRequestCost { get; set; } + + /// Raw count of user-initiated API requests. + [JsonPropertyName("totalUserRequests")] + public double TotalUserRequests { get; set; } + + /// Total time spent in model API calls (milliseconds). + [JsonPropertyName("totalApiDurationMs")] + public double TotalApiDurationMs { get; set; } + + /// Session start timestamp (epoch milliseconds). + [JsonPropertyName("sessionStartTime")] + public double SessionStartTime { get; set; } + + /// Aggregated code change metrics. + [JsonPropertyName("codeChanges")] + public SessionUsageGetMetricsResultCodeChanges CodeChanges { get => field ??= new(); set; } + + /// Per-model token and request metrics, keyed by model identifier. + [JsonPropertyName("modelMetrics")] + public Dictionary ModelMetrics { get => field ??= []; set; } + + /// Currently active model identifier. + [JsonPropertyName("currentModel")] + public string? CurrentModel { get; set; } + + /// Input tokens from the most recent main-agent API call. + [JsonPropertyName("lastCallInputTokens")] + public double LastCallInputTokens { get; set; } + + /// Output tokens from the most recent main-agent API call. + [JsonPropertyName("lastCallOutputTokens")] + public double LastCallOutputTokens { get; set; } +} + +/// RPC data type for SessionUsageGetMetrics operations. +[Experimental(Diagnostics.Experimental)] +internal class SessionUsageGetMetricsRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + /// RPC data type for SessionFsReadFile operations. public class SessionFsReadFileResult { @@ -1532,6 +1710,25 @@ public class SessionFsRenameParams public string Dest { get; set; } = string.Empty; } +/// Configuration source. +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum DiscoveredMcpServerSource +{ + /// The user variant. + [JsonStringEnumMemberName("user")] + User, + /// The workspace variant. + [JsonStringEnumMemberName("workspace")] + Workspace, + /// The plugin variant. + [JsonStringEnumMemberName("plugin")] + Plugin, + /// The builtin variant. + [JsonStringEnumMemberName("builtin")] + Builtin, +} + + /// Path conventions used by this filesystem. [JsonConverter(typeof(JsonStringEnumConverter))] public enum SessionFsSetProviderRequestConventions @@ -1782,6 +1979,13 @@ internal ServerMcpApi(JsonRpc rpc) { _rpc = rpc; } + + /// Calls "mcp.discover". + public async Task DiscoverAsync(string? workingDirectory = null, CancellationToken cancellationToken = default) + { + var request = new McpDiscoverRequest { WorkingDirectory = workingDirectory }; + return await CopilotClient.InvokeRpcAsync(_rpc, "mcp.discover", [request], cancellationToken); + } } /// Provides server-scoped SessionFs APIs. @@ -1847,6 +2051,7 @@ internal SessionRpc(JsonRpc rpc, string sessionId) Permissions = new PermissionsApi(rpc, sessionId); Shell = new ShellApi(rpc, sessionId); History = new HistoryApi(rpc, sessionId); + Usage = new UsageApi(rpc, sessionId); } /// Model APIs. @@ -1897,6 +2102,9 @@ internal SessionRpc(JsonRpc rpc, string sessionId) /// History APIs. public HistoryApi History { get; } + /// Usage APIs. + public UsageApi Usage { get; } + /// Calls "session.log". public async Task LogAsync(string message, SessionLogRequestLevel? level = null, bool? ephemeral = null, string? url = null, CancellationToken cancellationToken = default) { @@ -2386,6 +2594,27 @@ public async Task TruncateAsync(string eventId, Ca } } +/// Provides session-scoped Usage APIs. +[Experimental(Diagnostics.Experimental)] +public class UsageApi +{ + private readonly JsonRpc _rpc; + private readonly string _sessionId; + + internal UsageApi(JsonRpc rpc, string sessionId) + { + _rpc = rpc; + _sessionId = sessionId; + } + + /// Calls "session.usage.getMetrics". + public async Task GetMetricsAsync(CancellationToken cancellationToken = default) + { + var request = new SessionUsageGetMetricsRequest { SessionId = _sessionId }; + return await CopilotClient.InvokeRpcAsync(_rpc, "session.usage.getMetrics", [request], cancellationToken); + } +} + /// Handles `sessionFs` client session API methods. public interface ISessionFsHandler { @@ -2538,8 +2767,11 @@ public static void RegisterClientSessionApiHandlers(JsonRpc rpc, FuncMemory storage permission request. +/// Memory operation permission request. /// The memory variant of . public partial class PermissionRequestMemory : PermissionRequest { @@ -3570,17 +3570,34 @@ public partial class PermissionRequestMemory : PermissionRequest [JsonPropertyName("toolCallId")] public string? ToolCallId { get; set; } - /// Topic or subject of the memory being stored. + /// Whether this is a store or vote memory operation. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("action")] + public PermissionRequestMemoryAction? Action { get; set; } + + /// Topic or subject of the memory (store only). + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("subject")] - public required string Subject { get; set; } + public string? Subject { get; set; } - /// The fact or convention being stored. + /// The fact being stored or voted on. [JsonPropertyName("fact")] public required string Fact { get; set; } - /// Source references for the stored fact. + /// Source references for the stored fact (store only). + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("citations")] - public required string Citations { get; set; } + public string? Citations { get; set; } + + /// Vote direction (vote only). + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("direction")] + public PermissionRequestMemoryDirection? Direction { get; set; } + + /// Reason for the vote (vote only). + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("reason")] + public string? Reason { get; set; } } /// Custom tool invocation permission request. @@ -3975,6 +3992,30 @@ public enum SystemNotificationDataKindAgentCompletedStatus Failed, } +/// Whether this is a store or vote memory operation. +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum PermissionRequestMemoryAction +{ + /// The store variant. + [JsonStringEnumMemberName("store")] + Store, + /// The vote variant. + [JsonStringEnumMemberName("vote")] + Vote, +} + +/// Vote direction (vote only). +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum PermissionRequestMemoryDirection +{ + /// The upvote variant. + [JsonStringEnumMemberName("upvote")] + Upvote, + /// The downvote variant. + [JsonStringEnumMemberName("downvote")] + Downvote, +} + /// The outcome of the permission request. [JsonConverter(typeof(JsonStringEnumConverter))] public enum PermissionCompletedDataResultKind diff --git a/go/generated_session_events.go b/go/generated_session_events.go index 0599e7fcc..1bd2e8959 100644 --- a/go/generated_session_events.go +++ b/go/generated_session_events.go @@ -1993,12 +1993,18 @@ type PermissionRequestedDataPermissionRequest struct { ReadOnly *bool `json:"readOnly,omitempty"` // URL to be fetched URL *string `json:"url,omitempty"` - // Topic or subject of the memory being stored + // Whether this is a store or vote memory operation + Action *PermissionRequestedDataPermissionRequestAction `json:"action,omitempty"` + // Topic or subject of the memory (store only) Subject *string `json:"subject,omitempty"` - // The fact or convention being stored + // The fact being stored or voted on Fact *string `json:"fact,omitempty"` - // Source references for the stored fact + // Source references for the stored fact (store only) Citations *string `json:"citations,omitempty"` + // Vote direction (vote only) + Direction *PermissionRequestedDataPermissionRequestDirection `json:"direction,omitempty"` + // Reason for the vote (vote only) + Reason *string `json:"reason,omitempty"` // Description of what the custom tool does ToolDescription *string `json:"toolDescription,omitempty"` // Arguments of the tool call being gated @@ -2237,6 +2243,22 @@ const ( PermissionRequestedDataPermissionRequestKindHook PermissionRequestedDataPermissionRequestKind = "hook" ) +// Whether this is a store or vote memory operation +type PermissionRequestedDataPermissionRequestAction string + +const ( + PermissionRequestedDataPermissionRequestActionStore PermissionRequestedDataPermissionRequestAction = "store" + PermissionRequestedDataPermissionRequestActionVote PermissionRequestedDataPermissionRequestAction = "vote" +) + +// Vote direction (vote only) +type PermissionRequestedDataPermissionRequestDirection string + +const ( + PermissionRequestedDataPermissionRequestDirectionUpvote PermissionRequestedDataPermissionRequestDirection = "upvote" + PermissionRequestedDataPermissionRequestDirectionDownvote PermissionRequestedDataPermissionRequestDirection = "downvote" +) + // The outcome of the permission request type PermissionCompletedDataResultKind string diff --git a/go/rpc/generated_rpc.go b/go/rpc/generated_rpc.go index 6782f499d..698b3e95e 100644 --- a/go/rpc/generated_rpc.go +++ b/go/rpc/generated_rpc.go @@ -222,6 +222,27 @@ type MCPConfigRemoveParams struct { Name string `json:"name"` } +type MCPDiscoverResult struct { + // MCP servers discovered from all sources + Servers []DiscoveredMCPServer `json:"servers"` +} + +type DiscoveredMCPServer struct { + // Whether the server is enabled (not in the disabled list) + Enabled bool `json:"enabled"` + // Server name (config key) + Name string `json:"name"` + // Configuration source + Source ServerSource `json:"source"` + // Server type: local, stdio, http, or sse + Type *string `json:"type,omitempty"` +} + +type MCPDiscoverParams struct { + // Working directory used as context for discovery (e.g., plugin resolution) + WorkingDirectory *string `json:"workingDirectory,omitempty"` +} + type SessionFSSetProviderResult struct { // Whether the provider was set successfully Success bool `json:"success"` @@ -528,10 +549,10 @@ type SessionMCPReloadResult struct { // Experimental: SessionPluginsListResult is part of an experimental API and may change or be removed. type SessionPluginsListResult struct { // Installed plugins - Plugins []Plugin `json:"plugins"` + Plugins []PluginElement `json:"plugins"` } -type Plugin struct { +type PluginElement struct { // Whether the plugin is currently enabled Enabled bool `json:"enabled"` // Marketplace the plugin came from @@ -556,7 +577,7 @@ type Extension struct { // Process ID if the extension is running PID *int64 `json:"pid,omitempty"` // Discovery source: project (.github/extensions/) or user (~/.copilot/extensions/) - Source Source `json:"source"` + Source ExtensionSource `json:"source"` // Current status: running, disabled, failed, or starting Status ExtensionStatus `json:"status"` } @@ -591,19 +612,27 @@ type SessionToolsHandlePendingToolCallResult struct { } type SessionToolsHandlePendingToolCallParams struct { - Error *string `json:"error,omitempty"` - RequestID string `json:"requestId"` - Result *ResultUnion `json:"result"` + // Error message if the tool call failed + Error *string `json:"error,omitempty"` + // Request ID of the pending tool call + RequestID string `json:"requestId"` + // Tool call result (string or expanded result object) + Result *ResultUnion `json:"result"` } type ResultResult struct { - Error *string `json:"error,omitempty"` - ResultType *string `json:"resultType,omitempty"` - TextResultForLlm string `json:"textResultForLlm"` - ToolTelemetry map[string]any `json:"toolTelemetry,omitempty"` + // Error message if the tool call failed + Error *string `json:"error,omitempty"` + // Type of the tool result + ResultType *string `json:"resultType,omitempty"` + // Text result to send back to the LLM + TextResultForLlm string `json:"textResultForLlm"` + // Telemetry data from tool execution + ToolTelemetry map[string]any `json:"toolTelemetry,omitempty"` } type SessionCommandsHandlePendingCommandResult struct { + // Whether the command was handled successfully Success bool `json:"success"` } @@ -699,17 +728,36 @@ type SessionPermissionsHandlePendingPermissionRequestResult struct { } type SessionPermissionsHandlePendingPermissionRequestParams struct { + // Request ID of the pending permission request RequestID string `json:"requestId"` Result SessionPermissionsHandlePendingPermissionRequestParamsResult `json:"result"` } type SessionPermissionsHandlePendingPermissionRequestParamsResult struct { - Kind Kind `json:"kind"` - Rules []any `json:"rules,omitempty"` - Feedback *string `json:"feedback,omitempty"` - Message *string `json:"message,omitempty"` - Path *string `json:"path,omitempty"` - Interrupt *bool `json:"interrupt,omitempty"` + // The permission request was approved + // + // Denied because approval rules explicitly blocked it + // + // Denied because no approval rule matched and user confirmation was unavailable + // + // Denied by the user during an interactive prompt + // + // Denied by the organization's content exclusion policy + // + // Denied by a permission request hook registered by an extension or plugin + Kind Kind `json:"kind"` + // Rules that denied the request + Rules []any `json:"rules,omitempty"` + // Optional feedback from the user explaining the denial + Feedback *string `json:"feedback,omitempty"` + // Human-readable explanation of why the path was excluded + // + // Optional message from the hook explaining the denial + Message *string `json:"message,omitempty"` + // File path that triggered the exclusion + Path *string `json:"path,omitempty"` + // Whether to interrupt the current agent turn + Interrupt *bool `json:"interrupt,omitempty"` } type SessionLogResult struct { @@ -757,6 +805,8 @@ type SessionShellKillParams struct { // Experimental: SessionHistoryCompactResult is part of an experimental API and may change or be removed. type SessionHistoryCompactResult struct { + // Post-compaction context window usage breakdown + ContextWindow *ContextWindow `json:"contextWindow,omitempty"` // Number of messages removed during compaction MessagesRemoved float64 `json:"messagesRemoved"` // Whether compaction completed successfully @@ -765,6 +815,22 @@ type SessionHistoryCompactResult struct { TokensRemoved float64 `json:"tokensRemoved"` } +// Post-compaction context window usage breakdown +type ContextWindow struct { + // Token count from non-system messages (user, assistant, tool) + ConversationTokens *float64 `json:"conversationTokens,omitempty"` + // Current total tokens in the context window (system + conversation + tool definitions) + CurrentTokens float64 `json:"currentTokens"` + // Current number of messages in the conversation + MessagesLength float64 `json:"messagesLength"` + // Token count from system message(s) + SystemTokens *float64 `json:"systemTokens,omitempty"` + // Maximum token count for the model's context window + TokenLimit float64 `json:"tokenLimit"` + // Token count from tool definitions + ToolDefinitionsTokens *float64 `json:"toolDefinitionsTokens,omitempty"` +} + // Experimental: SessionHistoryTruncateResult is part of an experimental API and may change or be removed. type SessionHistoryTruncateResult struct { // Number of events that were removed @@ -777,6 +843,66 @@ type SessionHistoryTruncateParams struct { EventID string `json:"eventId"` } +// Experimental: SessionUsageGetMetricsResult is part of an experimental API and may change or be removed. +type SessionUsageGetMetricsResult struct { + // Aggregated code change metrics + CodeChanges CodeChanges `json:"codeChanges"` + // Currently active model identifier + CurrentModel *string `json:"currentModel,omitempty"` + // Input tokens from the most recent main-agent API call + LastCallInputTokens int64 `json:"lastCallInputTokens"` + // Output tokens from the most recent main-agent API call + LastCallOutputTokens int64 `json:"lastCallOutputTokens"` + // Per-model token and request metrics, keyed by model identifier + ModelMetrics map[string]ModelMetric `json:"modelMetrics"` + // Session start timestamp (epoch milliseconds) + SessionStartTime int64 `json:"sessionStartTime"` + // Total time spent in model API calls (milliseconds) + TotalAPIDurationMS float64 `json:"totalApiDurationMs"` + // Total user-initiated premium request cost across all models (may be fractional due to + // multipliers) + TotalPremiumRequestCost float64 `json:"totalPremiumRequestCost"` + // Raw count of user-initiated API requests + TotalUserRequests int64 `json:"totalUserRequests"` +} + +// Aggregated code change metrics +type CodeChanges struct { + // Number of distinct files modified + FilesModifiedCount int64 `json:"filesModifiedCount"` + // Total lines of code added + LinesAdded int64 `json:"linesAdded"` + // Total lines of code removed + LinesRemoved int64 `json:"linesRemoved"` +} + +type ModelMetric struct { + // Request count and cost metrics for this model + Requests Requests `json:"requests"` + // Token usage metrics for this model + Usage Usage `json:"usage"` +} + +// Request count and cost metrics for this model +type Requests struct { + // User-initiated premium request cost (with multiplier applied) + Cost float64 `json:"cost"` + // Number of API requests made with this model + Count int64 `json:"count"` +} + +// Token usage metrics for this model +type Usage struct { + // Total tokens read from prompt cache + CacheReadTokens int64 `json:"cacheReadTokens"` + // Total tokens written to prompt cache + CacheWriteTokens int64 `json:"cacheWriteTokens"` + // Total input tokens consumed + InputTokens int64 `json:"inputTokens"` + // Total output tokens produced + OutputTokens int64 `json:"outputTokens"` +} + type SessionFSReadFileResult struct { // File content as UTF-8 string Content string `json:"content"` @@ -922,6 +1048,16 @@ const ( ServerTypeStdio ServerType = "stdio" ) +// Configuration source +type ServerSource string + +const ( + ServerSourceBuiltin ServerSource = "builtin" + ServerSourcePlugin ServerSource = "plugin" + ServerSourceUser ServerSource = "user" + ServerSourceWorkspace ServerSource = "workspace" +) + // Path conventions used by this filesystem type Conventions string @@ -956,11 +1092,11 @@ const ( ) // Discovery source: project (.github/extensions/) or user (~/.copilot/extensions/) -type Source string +type ExtensionSource string const ( - SourceProject Source = "project" - SourceUser Source = "user" + ExtensionSourceUser ExtensionSource = "user" + ExtensionSourceProject ExtensionSource = "project" ) // Current status: running, disabled, failed, or starting @@ -1056,6 +1192,7 @@ type FilterMappingUnion struct { EnumMap map[string]FilterMappingEnum } +// Tool call result (string or expanded result object) type ResultUnion struct { ResultResult *ResultResult String *string @@ -1116,6 +1253,18 @@ func (a *ServerAccountApi) GetQuota(ctx context.Context) (*AccountGetQuotaResult type ServerMcpApi serverApi +func (a *ServerMcpApi) Discover(ctx context.Context, params *MCPDiscoverParams) (*MCPDiscoverResult, error) { + raw, err := a.client.Request("mcp.discover", params) + if err != nil { + return nil, err + } + var result MCPDiscoverResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + type ServerSessionFsApi serverApi func (a *ServerSessionFsApi) SetProvider(ctx context.Context, params *SessionFSSetProviderParams) (*SessionFSSetProviderResult, error) { @@ -1812,6 +1961,22 @@ func (a *HistoryApi) Truncate(ctx context.Context, params *SessionHistoryTruncat return &result, nil } +// Experimental: UsageApi contains experimental APIs that may change or be removed. +type UsageApi sessionApi + +func (a *UsageApi) GetMetrics(ctx context.Context) (*SessionUsageGetMetricsResult, error) { + req := map[string]any{"sessionId": a.sessionID} + raw, err := a.client.Request("session.usage.getMetrics", req) + if err != nil { + return nil, err + } + var result SessionUsageGetMetricsResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // SessionRpc provides typed session-scoped RPC methods. type SessionRpc struct { common sessionApi // Reuse a single struct instead of allocating one for each service on the heap. @@ -1832,6 +1997,7 @@ type SessionRpc struct { Permissions *PermissionsApi Shell *ShellApi History *HistoryApi + Usage *UsageApi } func (a *SessionRpc) Log(ctx context.Context, params *SessionLogParams) (*SessionLogResult, error) { @@ -1878,6 +2044,7 @@ func NewSessionRpc(client *jsonrpc2.Client, sessionID string) *SessionRpc { r.Permissions = (*PermissionsApi)(&r.common) r.Shell = (*ShellApi)(&r.common) r.History = (*HistoryApi)(&r.common) + r.Usage = (*UsageApi)(&r.common) return r } diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index 84754e70f..55c3a4f24 100644 --- a/nodejs/package-lock.json +++ b/nodejs/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.8", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.21", + "@github/copilot": "^1.0.22", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" }, @@ -663,26 +663,26 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.21.tgz", - "integrity": "sha512-P+nORjNKAtl92jYCG6Qr1Rsw2JoyScgeQSkIR6O2WB37WS5JVdA4ax1WVualMbfuc9V58CPHX6fwyNpkI89FkQ==", + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.22.tgz", + "integrity": "sha512-BR9oTJ1tQ51RV81xcxmlZe0zB3Tf8i/vFsKSTm2f5wRLJgtuVl2LgaFStoI/peTFcmgtZbhrqsnWTu5GkEPK5Q==", "license": "SEE LICENSE IN LICENSE.md", "bin": { "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.21", - "@github/copilot-darwin-x64": "1.0.21", - "@github/copilot-linux-arm64": "1.0.21", - "@github/copilot-linux-x64": "1.0.21", - "@github/copilot-win32-arm64": "1.0.21", - "@github/copilot-win32-x64": "1.0.21" + "@github/copilot-darwin-arm64": "1.0.22", + "@github/copilot-darwin-x64": "1.0.22", + "@github/copilot-linux-arm64": "1.0.22", + "@github/copilot-linux-x64": "1.0.22", + "@github/copilot-win32-arm64": "1.0.22", + "@github/copilot-win32-x64": "1.0.22" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.21.tgz", - "integrity": "sha512-aB+s9ldTwcyCOYmzjcQ4SknV6g81z92T8aUJEJZBwOXOTBeWKAJtk16ooAKangZgdwuLgO3or1JUjx1FJAm5nQ==", + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.22.tgz", + "integrity": "sha512-cK42uX+oz46Cjsb7z+rdPw+DIGczfVSFWlc1WDcdVlwBW4cEfV0pzFXExpN1r1z179TFgAaVMbhkgLqhOZ/PeQ==", "cpu": [ "arm64" ], @@ -696,9 +696,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.21.tgz", - "integrity": "sha512-aNad81DOGuGShmaiFNIxBUSZLwte0dXmDYkGfAF9WJIgY4qP4A8CPWFoNr8//gY+4CwaIf9V+f/OC6k2BdECbw==", + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.22.tgz", + "integrity": "sha512-Pmw0ipF+yeLbP6JctsEoMS2LUCpVdC2r557BnCoe48BN8lO8i9JLnkpuDDrJ1AZuCk1VjnujFKEQywOOdfVlpA==", "cpu": [ "x64" ], @@ -712,9 +712,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.21.tgz", - "integrity": "sha512-FL0NsCnHax4czHVv1S8iBqPLGZDhZ28N3+6nT29xWGhmjBWTkIofxLThKUPcyyMsfPTTxIlrdwWa8qQc5z2Q+g==", + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.22.tgz", + "integrity": "sha512-WVgG67VmZgHoD7GMlkTxEVe1qK8k9Ek9A02/Da7obpsDdtBInt3nJTwBEgm4cNDM4XaenQH17/jmwVtTwXB6lw==", "cpu": [ "arm64" ], @@ -728,9 +728,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.21.tgz", - "integrity": "sha512-S7pWVI16hesZtxYbIyfw+MHZpc5ESoGKUVr5Y+lZJNaM2340gJGPQzQwSpvKIRMLHRKI2hXLwciAnYeMFxE/Tg==", + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.22.tgz", + "integrity": "sha512-XRkHVFmdC7FMrczXOdPjbNKiknMr13asKtwJoErJO/Xdy4cmzKQHSvNsBk8VNrr7oyWrUcB1F6mbIxb2LFxPOw==", "cpu": [ "x64" ], @@ -744,9 +744,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.21.tgz", - "integrity": "sha512-a9qc2Ku+XbyBkXCclbIvBbIVnECACTIWnPctmXWsQeSdeapGxgfHGux7y8hAFV5j6+nhCm6cnyEMS3rkZjAhdA==", + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.22.tgz", + "integrity": "sha512-Ao6gv1f2ZV+HVlkB1MV7YFdCuaB3NcFCnNu0a6/WLl2ypsfP1vWosPPkIB32jQJeBkT9ku3exOZLRj+XC0P3Mg==", "cpu": [ "arm64" ], @@ -760,9 +760,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.21.tgz", - "integrity": "sha512-9klu+7NQ6tEyb8sibb0rsbimBivDrnNltZho10Bgbf1wh3o+erTjffXDjW9Zkyaw8lZA9Fz8bqhVkKntZq58Lg==", + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.22.tgz", + "integrity": "sha512-EppcL+3TpxC+X/eQEIYtkN0PaA3/cvtI9UJqldLIkKDPXNYk/0mw877Ru9ypRcBWBWokDN6iKIWk5IxYH+JIvg==", "cpu": [ "x64" ], diff --git a/nodejs/package.json b/nodejs/package.json index e79814992..6a0ef9567 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -56,7 +56,7 @@ "author": "GitHub", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.21", + "@github/copilot": "^1.0.22", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" }, diff --git a/nodejs/samples/package-lock.json b/nodejs/samples/package-lock.json index d95f5582a..3c5ebfd97 100644 --- a/nodejs/samples/package-lock.json +++ b/nodejs/samples/package-lock.json @@ -18,7 +18,7 @@ "version": "0.1.8", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.21", + "@github/copilot": "^1.0.22", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" }, diff --git a/nodejs/src/generated/rpc.ts b/nodejs/src/generated/rpc.ts index 753a6a65f..1733e5cd9 100644 --- a/nodejs/src/generated/rpc.ts +++ b/nodejs/src/generated/rpc.ts @@ -361,6 +361,38 @@ export interface McpConfigRemoveParams { name: string; } +export interface McpDiscoverResult { + /** + * MCP servers discovered from all sources + */ + servers: DiscoveredMcpServer[]; +} +export interface DiscoveredMcpServer { + /** + * Server name (config key) + */ + name: string; + /** + * Server type: local, stdio, http, or sse + */ + type?: string; + /** + * Configuration source + */ + source: "user" | "workspace" | "plugin" | "builtin"; + /** + * Whether the server is enabled (not in the disabled list) + */ + enabled: boolean; +} + +export interface McpDiscoverParams { + /** + * Working directory used as context for discovery (e.g., plugin resolution) + */ + workingDirectory?: string; +} + export interface SessionFsSetProviderResult { /** * Whether the provider was set successfully @@ -1035,21 +1067,45 @@ export interface SessionToolsHandlePendingToolCallParams { * Target session identifier */ sessionId: string; + /** + * Request ID of the pending tool call + */ requestId: string; + /** + * Tool call result (string or expanded result object) + */ result?: | string | { + /** + * Text result to send back to the LLM + */ textResultForLlm: string; + /** + * Type of the tool result + */ resultType?: string; + /** + * Error message if the tool call failed + */ error?: string; + /** + * Telemetry data from tool execution + */ toolTelemetry?: { [k: string]: unknown; }; }; + /** + * Error message if the tool call failed + */ error?: string; } export interface SessionCommandsHandlePendingCommandResult { + /** + * Whether the command was handled successfully + */ success: boolean; } @@ -1223,30 +1279,69 @@ export interface SessionPermissionsHandlePendingPermissionRequestParams { * Target session identifier */ sessionId: string; + /** + * Request ID of the pending permission request + */ requestId: string; result: | { + /** + * The permission request was approved + */ kind: "approved"; } | { + /** + * Denied because approval rules explicitly blocked it + */ kind: "denied-by-rules"; + /** + * Rules that denied the request + */ rules: unknown[]; } | { + /** + * Denied because no approval rule matched and user confirmation was unavailable + */ kind: "denied-no-approval-rule-and-could-not-request-from-user"; } | { + /** + * Denied by the user during an interactive prompt + */ kind: "denied-interactively-by-user"; + /** + * Optional feedback from the user explaining the denial + */ feedback?: string; } | { + /** + * Denied by the organization's content exclusion policy + */ kind: "denied-by-content-exclusion-policy"; + /** + * File path that triggered the exclusion + */ path: string; + /** + * Human-readable explanation of why the path was excluded + */ message: string; } | { + /** + * Denied by a permission request hook registered by an extension or plugin + */ kind: "denied-by-permission-request-hook"; + /** + * Optional message from the hook explaining the denial + */ message?: string; + /** + * Whether to interrupt the current agent turn + */ interrupt?: boolean; }; } @@ -1343,6 +1438,35 @@ export interface SessionHistoryCompactResult { * Number of messages removed during compaction */ messagesRemoved: number; + /** + * Post-compaction context window usage breakdown + */ + contextWindow?: { + /** + * Maximum token count for the model's context window + */ + tokenLimit: number; + /** + * Current total tokens in the context window (system + conversation + tool definitions) + */ + currentTokens: number; + /** + * Current number of messages in the conversation + */ + messagesLength: number; + /** + * Token count from system message(s) + */ + systemTokens?: number; + /** + * Token count from non-system messages (user, assistant, tool) + */ + conversationTokens?: number; + /** + * Token count from tool definitions + */ + toolDefinitionsTokens?: number; + }; } /** @experimental */ @@ -1373,6 +1497,104 @@ export interface SessionHistoryTruncateParams { eventId: string; } +/** @experimental */ +export interface SessionUsageGetMetricsResult { + /** + * Total user-initiated premium request cost across all models (may be fractional due to multipliers) + */ + totalPremiumRequestCost: number; + /** + * Raw count of user-initiated API requests + */ + totalUserRequests: number; + /** + * Total time spent in model API calls (milliseconds) + */ + totalApiDurationMs: number; + /** + * Session start timestamp (epoch milliseconds) + */ + sessionStartTime: number; + /** + * Aggregated code change metrics + */ + codeChanges: { + /** + * Total lines of code added + */ + linesAdded: number; + /** + * Total lines of code removed + */ + linesRemoved: number; + /** + * Number of distinct files modified + */ + filesModifiedCount: number; + }; + /** + * Per-model token and request metrics, keyed by model identifier + */ + modelMetrics: { + [k: string]: { + /** + * Request count and cost metrics for this model + */ + requests: { + /** + * Number of API requests made with this model + */ + count: number; + /** + * User-initiated premium request cost (with multiplier applied) + */ + cost: number; + }; + /** + * Token usage metrics for this model + */ + usage: { + /** + * Total input tokens consumed + */ + inputTokens: number; + /** + * Total output tokens produced + */ + outputTokens: number; + /** + * Total tokens read from prompt cache + */ + cacheReadTokens: number; + /** + * Total tokens written to prompt cache + */ + cacheWriteTokens: number; + }; + }; + }; + /** + * Currently active model identifier + */ + currentModel?: string; + /** + * Input tokens from the most recent main-agent API call + */ + lastCallInputTokens: number; + /** + * Output tokens from the most recent main-agent API call + */ + lastCallOutputTokens: number; +} + +/** @experimental */ +export interface SessionUsageGetMetricsParams { + /** + * Target session identifier + */ + sessionId: string; +} + export interface SessionFsReadFileResult { /** * File content as UTF-8 string @@ -1607,6 +1829,8 @@ export function createServerRpc(connection: MessageConnection) { remove: async (params: McpConfigRemoveParams): Promise => connection.sendRequest("mcp.config.remove", params), }, + discover: async (params: McpDiscoverParams): Promise => + connection.sendRequest("mcp.discover", params), }, sessionFs: { setProvider: async (params: SessionFsSetProviderParams): Promise => @@ -1740,6 +1964,11 @@ export function createSessionRpc(connection: MessageConnection, sessionId: strin truncate: async (params: Omit): Promise => connection.sendRequest("session.history.truncate", { sessionId, ...params }), }, + /** @experimental */ + usage: { + getMetrics: async (): Promise => + connection.sendRequest("session.usage.getMetrics", { sessionId }), + }, }; } diff --git a/nodejs/src/generated/session-events.ts b/nodejs/src/generated/session-events.ts index e9bc2a550..7cfc60522 100644 --- a/nodejs/src/generated/session-events.ts +++ b/nodejs/src/generated/session-events.ts @@ -2785,17 +2785,29 @@ export type SessionEvent = */ toolCallId?: string; /** - * Topic or subject of the memory being stored + * Whether this is a store or vote memory operation */ - subject: string; + action?: "store" | "vote"; /** - * The fact or convention being stored + * Topic or subject of the memory (store only) + */ + subject?: string; + /** + * The fact being stored or voted on */ fact: string; /** - * Source references for the stored fact + * Source references for the stored fact (store only) + */ + citations?: string; + /** + * Vote direction (vote only) + */ + direction?: "upvote" | "downvote"; + /** + * Reason for the vote (vote only) */ - citations: string; + reason?: string; } | { /** diff --git a/python/copilot/generated/rpc.py b/python/copilot/generated/rpc.py index 43bb879be..19265c557 100644 --- a/python/copilot/generated/rpc.py +++ b/python/copilot/generated/rpc.py @@ -790,6 +790,83 @@ def to_dict(self) -> dict: return result +class ServerSource(Enum): + """Configuration source""" + + BUILTIN = "builtin" + PLUGIN = "plugin" + USER = "user" + WORKSPACE = "workspace" + + +@dataclass +class DiscoveredMCPServer: + enabled: bool + """Whether the server is enabled (not in the disabled list)""" + + name: str + """Server name (config key)""" + + source: ServerSource + """Configuration source""" + + type: str | None = None + """Server type: local, stdio, http, or sse""" + + @staticmethod + def from_dict(obj: Any) -> 'DiscoveredMCPServer': + assert isinstance(obj, dict) + enabled = from_bool(obj.get("enabled")) + name = from_str(obj.get("name")) + source = ServerSource(obj.get("source")) + type = from_union([from_str, from_none], obj.get("type")) + return DiscoveredMCPServer(enabled, name, source, type) + + def to_dict(self) -> dict: + result: dict = {} + result["enabled"] = from_bool(self.enabled) + result["name"] = from_str(self.name) + result["source"] = to_enum(ServerSource, self.source) + if self.type is not None: + result["type"] = from_union([from_str, from_none], self.type) + return result + + +@dataclass +class MCPDiscoverResult: + servers: list[DiscoveredMCPServer] + """MCP servers discovered from all sources""" + + @staticmethod + def from_dict(obj: Any) -> 'MCPDiscoverResult': + assert isinstance(obj, dict) + servers = from_list(DiscoveredMCPServer.from_dict, obj.get("servers")) + return MCPDiscoverResult(servers) + + def to_dict(self) -> dict: + result: dict = {} + result["servers"] = from_list(lambda x: to_class(DiscoveredMCPServer, x), self.servers) + return result + + +@dataclass +class MCPDiscoverParams: + working_directory: str | None = None + """Working directory used as context for discovery (e.g., plugin resolution)""" + + @staticmethod + def from_dict(obj: Any) -> 'MCPDiscoverParams': + assert isinstance(obj, dict) + working_directory = from_union([from_str, from_none], obj.get("workingDirectory")) + return MCPDiscoverParams(working_directory) + + def to_dict(self) -> dict: + result: dict = {} + if self.working_directory is not None: + result["workingDirectory"] = from_union([from_str, from_none], self.working_directory) + return result + + @dataclass class SessionFSSetProviderResult: success: bool @@ -1847,7 +1924,7 @@ def to_dict(self) -> dict: return result -class Source(Enum): +class ExtensionSource(Enum): """Discovery source: project (.github/extensions/) or user (~/.copilot/extensions/)""" PROJECT = "project" @@ -1871,7 +1948,7 @@ class Extension: name: str """Extension name (directory name)""" - source: Source + source: ExtensionSource """Discovery source: project (.github/extensions/) or user (~/.copilot/extensions/)""" status: ExtensionStatus @@ -1885,7 +1962,7 @@ def from_dict(obj: Any) -> 'Extension': assert isinstance(obj, dict) id = from_str(obj.get("id")) name = from_str(obj.get("name")) - source = Source(obj.get("source")) + source = ExtensionSource(obj.get("source")) status = ExtensionStatus(obj.get("status")) pid = from_union([from_int, from_none], obj.get("pid")) return Extension(id, name, source, status, pid) @@ -1894,7 +1971,7 @@ def to_dict(self) -> dict: result: dict = {} result["id"] = from_str(self.id) result["name"] = from_str(self.name) - result["source"] = to_enum(Source, self.source) + result["source"] = to_enum(ExtensionSource, self.source) result["status"] = to_enum(ExtensionStatus, self.status) if self.pid is not None: result["pid"] = from_union([from_int, from_none], self.pid) @@ -2014,9 +2091,16 @@ def to_dict(self) -> dict: @dataclass class ResultResult: text_result_for_llm: str + """Text result to send back to the LLM""" + error: str | None = None + """Error message if the tool call failed""" + result_type: str | None = None + """Type of the tool result""" + tool_telemetry: dict[str, Any] | None = None + """Telemetry data from tool execution""" @staticmethod def from_dict(obj: Any) -> 'ResultResult': @@ -2042,8 +2126,13 @@ def to_dict(self) -> dict: @dataclass class SessionToolsHandlePendingToolCallParams: request_id: str + """Request ID of the pending tool call""" + error: str | None = None + """Error message if the tool call failed""" + result: ResultResult | str | None = None + """Tool call result (string or expanded result object)""" @staticmethod def from_dict(obj: Any) -> 'SessionToolsHandlePendingToolCallParams': @@ -2066,6 +2155,7 @@ def to_dict(self) -> dict: @dataclass class SessionCommandsHandlePendingCommandResult: success: bool + """Whether the command was handled successfully""" @staticmethod def from_dict(obj: Any) -> 'SessionCommandsHandlePendingCommandResult': @@ -2438,11 +2528,34 @@ class Kind(Enum): @dataclass class SessionPermissionsHandlePendingPermissionRequestParamsResult: kind: Kind + """The permission request was approved + + Denied because approval rules explicitly blocked it + + Denied because no approval rule matched and user confirmation was unavailable + + Denied by the user during an interactive prompt + + Denied by the organization's content exclusion policy + + Denied by a permission request hook registered by an extension or plugin + """ rules: list[Any] | None = None + """Rules that denied the request""" + feedback: str | None = None + """Optional feedback from the user explaining the denial""" + message: str | None = None + """Human-readable explanation of why the path was excluded + + Optional message from the hook explaining the denial + """ path: str | None = None + """File path that triggered the exclusion""" + interrupt: bool | None = None + """Whether to interrupt the current agent turn""" @staticmethod def from_dict(obj: Any) -> 'SessionPermissionsHandlePendingPermissionRequestParamsResult': @@ -2474,6 +2587,8 @@ def to_dict(self) -> dict: @dataclass class SessionPermissionsHandlePendingPermissionRequestParams: request_id: str + """Request ID of the pending permission request""" + result: SessionPermissionsHandlePendingPermissionRequestParamsResult @staticmethod @@ -2646,6 +2761,53 @@ def to_dict(self) -> dict: return result +@dataclass +class ContextWindow: + """Post-compaction context window usage breakdown""" + + current_tokens: float + """Current total tokens in the context window (system + conversation + tool definitions)""" + + messages_length: float + """Current number of messages in the conversation""" + + token_limit: float + """Maximum token count for the model's context window""" + + conversation_tokens: float | None = None + """Token count from non-system messages (user, assistant, tool)""" + + system_tokens: float | None = None + """Token count from system message(s)""" + + tool_definitions_tokens: float | None = None + """Token count from tool definitions""" + + @staticmethod + def from_dict(obj: Any) -> 'ContextWindow': + assert isinstance(obj, dict) + current_tokens = from_float(obj.get("currentTokens")) + messages_length = from_float(obj.get("messagesLength")) + token_limit = from_float(obj.get("tokenLimit")) + conversation_tokens = from_union([from_float, from_none], obj.get("conversationTokens")) + system_tokens = from_union([from_float, from_none], obj.get("systemTokens")) + tool_definitions_tokens = from_union([from_float, from_none], obj.get("toolDefinitionsTokens")) + return ContextWindow(current_tokens, messages_length, token_limit, conversation_tokens, system_tokens, tool_definitions_tokens) + + def to_dict(self) -> dict: + result: dict = {} + result["currentTokens"] = to_float(self.current_tokens) + result["messagesLength"] = to_float(self.messages_length) + result["tokenLimit"] = to_float(self.token_limit) + if self.conversation_tokens is not None: + result["conversationTokens"] = from_union([to_float, from_none], self.conversation_tokens) + if self.system_tokens is not None: + result["systemTokens"] = from_union([to_float, from_none], self.system_tokens) + if self.tool_definitions_tokens is not None: + result["toolDefinitionsTokens"] = from_union([to_float, from_none], self.tool_definitions_tokens) + return result + + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class SessionHistoryCompactResult: @@ -2658,19 +2820,25 @@ class SessionHistoryCompactResult: tokens_removed: float """Number of tokens freed by compaction""" + context_window: ContextWindow | None = None + """Post-compaction context window usage breakdown""" + @staticmethod def from_dict(obj: Any) -> 'SessionHistoryCompactResult': assert isinstance(obj, dict) messages_removed = from_float(obj.get("messagesRemoved")) success = from_bool(obj.get("success")) tokens_removed = from_float(obj.get("tokensRemoved")) - return SessionHistoryCompactResult(messages_removed, success, tokens_removed) + context_window = from_union([ContextWindow.from_dict, from_none], obj.get("contextWindow")) + return SessionHistoryCompactResult(messages_removed, success, tokens_removed, context_window) def to_dict(self) -> dict: result: dict = {} result["messagesRemoved"] = to_float(self.messages_removed) result["success"] = from_bool(self.success) result["tokensRemoved"] = to_float(self.tokens_removed) + if self.context_window is not None: + result["contextWindow"] = from_union([lambda x: to_class(ContextWindow, x), from_none], self.context_window) return result @@ -2710,6 +2878,175 @@ def to_dict(self) -> dict: return result +@dataclass +class CodeChanges: + """Aggregated code change metrics""" + + files_modified_count: int + """Number of distinct files modified""" + + lines_added: int + """Total lines of code added""" + + lines_removed: int + """Total lines of code removed""" + + @staticmethod + def from_dict(obj: Any) -> 'CodeChanges': + assert isinstance(obj, dict) + files_modified_count = from_int(obj.get("filesModifiedCount")) + lines_added = from_int(obj.get("linesAdded")) + lines_removed = from_int(obj.get("linesRemoved")) + return CodeChanges(files_modified_count, lines_added, lines_removed) + + def to_dict(self) -> dict: + result: dict = {} + result["filesModifiedCount"] = from_int(self.files_modified_count) + result["linesAdded"] = from_int(self.lines_added) + result["linesRemoved"] = from_int(self.lines_removed) + return result + + +@dataclass +class Requests: + """Request count and cost metrics for this model""" + + cost: float + """User-initiated premium request cost (with multiplier applied)""" + + count: int + """Number of API requests made with this model""" + + @staticmethod + def from_dict(obj: Any) -> 'Requests': + assert isinstance(obj, dict) + cost = from_float(obj.get("cost")) + count = from_int(obj.get("count")) + return Requests(cost, count) + + def to_dict(self) -> dict: + result: dict = {} + result["cost"] = to_float(self.cost) + result["count"] = from_int(self.count) + return result + + +@dataclass +class Usage: + """Token usage metrics for this model""" + + cache_read_tokens: int + """Total tokens read from prompt cache""" + + cache_write_tokens: int + """Total tokens written to prompt cache""" + + input_tokens: int + """Total input tokens consumed""" + + output_tokens: int + """Total output tokens produced""" + + @staticmethod + def from_dict(obj: Any) -> 'Usage': + assert isinstance(obj, dict) + cache_read_tokens = from_int(obj.get("cacheReadTokens")) + cache_write_tokens = from_int(obj.get("cacheWriteTokens")) + input_tokens = from_int(obj.get("inputTokens")) + output_tokens = from_int(obj.get("outputTokens")) + return Usage(cache_read_tokens, cache_write_tokens, input_tokens, output_tokens) + + def to_dict(self) -> dict: + result: dict = {} + result["cacheReadTokens"] = from_int(self.cache_read_tokens) + result["cacheWriteTokens"] = from_int(self.cache_write_tokens) + result["inputTokens"] = from_int(self.input_tokens) + result["outputTokens"] = from_int(self.output_tokens) + return result + + +@dataclass +class ModelMetric: + requests: Requests + """Request count and cost metrics for this model""" + + usage: Usage + """Token usage metrics for this model""" + + @staticmethod + def from_dict(obj: Any) -> 'ModelMetric': + assert isinstance(obj, dict) + requests = Requests.from_dict(obj.get("requests")) + usage = Usage.from_dict(obj.get("usage")) + return ModelMetric(requests, usage) + + def to_dict(self) -> dict: + result: dict = {} + result["requests"] = to_class(Requests, self.requests) + result["usage"] = to_class(Usage, self.usage) + return result + + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class SessionUsageGetMetricsResult: + code_changes: CodeChanges + """Aggregated code change metrics""" + + last_call_input_tokens: int + """Input tokens from the most recent main-agent API call""" + + last_call_output_tokens: int + """Output tokens from the most recent main-agent API call""" + + model_metrics: dict[str, ModelMetric] + """Per-model token and request metrics, keyed by model identifier""" + + session_start_time: int + """Session start timestamp (epoch milliseconds)""" + + total_api_duration_ms: float + """Total time spent in model API calls (milliseconds)""" + + total_premium_request_cost: float + """Total user-initiated premium request cost across all models (may be fractional due to + multipliers) + """ + total_user_requests: int + """Raw count of user-initiated API requests""" + + current_model: str | None = None + """Currently active model identifier""" + + @staticmethod + def from_dict(obj: Any) -> 'SessionUsageGetMetricsResult': + assert isinstance(obj, dict) + code_changes = CodeChanges.from_dict(obj.get("codeChanges")) + last_call_input_tokens = from_int(obj.get("lastCallInputTokens")) + last_call_output_tokens = from_int(obj.get("lastCallOutputTokens")) + model_metrics = from_dict(ModelMetric.from_dict, obj.get("modelMetrics")) + session_start_time = from_int(obj.get("sessionStartTime")) + total_api_duration_ms = from_float(obj.get("totalApiDurationMs")) + total_premium_request_cost = from_float(obj.get("totalPremiumRequestCost")) + total_user_requests = from_int(obj.get("totalUserRequests")) + current_model = from_union([from_str, from_none], obj.get("currentModel")) + return SessionUsageGetMetricsResult(code_changes, last_call_input_tokens, last_call_output_tokens, model_metrics, session_start_time, total_api_duration_ms, total_premium_request_cost, total_user_requests, current_model) + + def to_dict(self) -> dict: + result: dict = {} + result["codeChanges"] = to_class(CodeChanges, self.code_changes) + result["lastCallInputTokens"] = from_int(self.last_call_input_tokens) + result["lastCallOutputTokens"] = from_int(self.last_call_output_tokens) + result["modelMetrics"] = from_dict(lambda x: to_class(ModelMetric, x), self.model_metrics) + result["sessionStartTime"] = from_int(self.session_start_time) + result["totalApiDurationMs"] = to_float(self.total_api_duration_ms) + result["totalPremiumRequestCost"] = to_float(self.total_premium_request_cost) + result["totalUserRequests"] = from_int(self.total_user_requests) + if self.current_model is not None: + result["currentModel"] = from_union([from_str, from_none], self.current_model) + return result + + @dataclass class SessionFSReadFileResult: content: str @@ -3195,6 +3532,22 @@ def mcp_config_remove_params_to_dict(x: MCPConfigRemoveParams) -> Any: return to_class(MCPConfigRemoveParams, x) +def mcp_discover_result_from_dict(s: Any) -> MCPDiscoverResult: + return MCPDiscoverResult.from_dict(s) + + +def mcp_discover_result_to_dict(x: MCPDiscoverResult) -> Any: + return to_class(MCPDiscoverResult, x) + + +def mcp_discover_params_from_dict(s: Any) -> MCPDiscoverParams: + return MCPDiscoverParams.from_dict(s) + + +def mcp_discover_params_to_dict(x: MCPDiscoverParams) -> Any: + return to_class(MCPDiscoverParams, x) + + def session_fs_set_provider_result_from_dict(s: Any) -> SessionFSSetProviderResult: return SessionFSSetProviderResult.from_dict(s) @@ -3715,6 +4068,14 @@ def session_history_truncate_params_to_dict(x: SessionHistoryTruncateParams) -> return to_class(SessionHistoryTruncateParams, x) +def session_usage_get_metrics_result_from_dict(s: Any) -> SessionUsageGetMetricsResult: + return SessionUsageGetMetricsResult.from_dict(s) + + +def session_usage_get_metrics_result_to_dict(x: SessionUsageGetMetricsResult) -> Any: + return to_class(SessionUsageGetMetricsResult, x) + + def session_fs_read_file_result_from_dict(s: Any) -> SessionFSReadFileResult: return SessionFSReadFileResult.from_dict(s) @@ -3871,6 +4232,10 @@ class ServerMcpApi: def __init__(self, client: "JsonRpcClient"): self._client = client + async def discover(self, params: MCPDiscoverParams, *, timeout: float | None = None) -> MCPDiscoverResult: + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + return MCPDiscoverResult.from_dict(await self._client.request("mcp.discover", params_dict, **_timeout_kwargs(timeout))) + class ServerSessionFsApi: def __init__(self, client: "JsonRpcClient"): @@ -4166,6 +4531,16 @@ async def truncate(self, params: SessionHistoryTruncateParams, *, timeout: float return SessionHistoryTruncateResult.from_dict(await self._client.request("session.history.truncate", params_dict, **_timeout_kwargs(timeout))) +# Experimental: this API group is experimental and may change or be removed. +class UsageApi: + def __init__(self, client: "JsonRpcClient", session_id: str): + self._client = client + self._session_id = session_id + + async def get_metrics(self, *, timeout: float | None = None) -> SessionUsageGetMetricsResult: + return SessionUsageGetMetricsResult.from_dict(await self._client.request("session.usage.getMetrics", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + + class SessionRpc: """Typed session-scoped RPC methods.""" def __init__(self, client: "JsonRpcClient", session_id: str): @@ -4187,6 +4562,7 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self.permissions = PermissionsApi(client, session_id) self.shell = ShellApi(client, session_id) self.history = HistoryApi(client, session_id) + self.usage = UsageApi(client, session_id) async def log(self, params: SessionLogParams, *, timeout: float | None = None) -> SessionLogResult: params_dict = {k: v for k, v in params.to_dict().items() if v is not None} diff --git a/python/copilot/generated/session_events.py b/python/copilot/generated/session_events.py index dea0e79fd..2c29c791a 100644 --- a/python/copilot/generated/session_events.py +++ b/python/copilot/generated/session_events.py @@ -78,7 +78,7 @@ def from_int(x: Any) -> int: return x -class Action(Enum): +class DataAction(Enum): """The user action: "accept" (submitted form), "decline" (explicitly refused), or "cancel" (dismissed) """ @@ -857,6 +857,13 @@ class Operation(Enum): UPDATE = "update" +class PermissionRequestAction(Enum): + """Whether this is a store or vote memory operation""" + + STORE = "store" + VOTE = "vote" + + @dataclass class PermissionRequestCommand: identifier: str @@ -879,6 +886,13 @@ def to_dict(self) -> dict: return result +class Direction(Enum): + """Vote direction (vote only)""" + + DOWNVOTE = "downvote" + UPVOTE = "upvote" + + class PermissionRequestKind(Enum): CUSTOM_TOOL = "custom-tool" HOOK = "hook" @@ -921,7 +935,7 @@ class PermissionRequest: URL access permission request - Memory storage permission request + Memory operation permission request Custom tool invocation permission request @@ -999,14 +1013,23 @@ class PermissionRequest: url: str | None = None """URL to be fetched""" + action: PermissionRequestAction | None = None + """Whether this is a store or vote memory operation""" + citations: str | None = None - """Source references for the stored fact""" + """Source references for the stored fact (store only)""" + + direction: Direction | None = None + """Vote direction (vote only)""" fact: str | None = None - """The fact or convention being stored""" + """The fact being stored or voted on""" + + reason: str | None = None + """Reason for the vote (vote only)""" subject: str | None = None - """Topic or subject of the memory being stored""" + """Topic or subject of the memory (store only)""" tool_description: str | None = None """Description of what the custom tool does""" @@ -1040,13 +1063,16 @@ def from_dict(obj: Any) -> 'PermissionRequest': tool_name = from_union([from_str, from_none], obj.get("toolName")) tool_title = from_union([from_str, from_none], obj.get("toolTitle")) url = from_union([from_str, from_none], obj.get("url")) + action = from_union([PermissionRequestAction, from_none], obj.get("action")) citations = from_union([from_str, from_none], obj.get("citations")) + direction = from_union([Direction, from_none], obj.get("direction")) fact = from_union([from_str, from_none], obj.get("fact")) + reason = from_union([from_str, from_none], obj.get("reason")) subject = from_union([from_str, from_none], obj.get("subject")) tool_description = from_union([from_str, from_none], obj.get("toolDescription")) hook_message = from_union([from_str, from_none], obj.get("hookMessage")) tool_args = obj.get("toolArgs") - return PermissionRequest(kind, can_offer_session_approval, commands, full_command_text, has_write_file_redirection, intention, possible_paths, possible_urls, tool_call_id, warning, diff, file_name, new_file_contents, path, args, read_only, server_name, tool_name, tool_title, url, citations, fact, subject, tool_description, hook_message, tool_args) + return PermissionRequest(kind, can_offer_session_approval, commands, full_command_text, has_write_file_redirection, intention, possible_paths, possible_urls, tool_call_id, warning, diff, file_name, new_file_contents, path, args, read_only, server_name, tool_name, tool_title, url, action, citations, direction, fact, reason, subject, tool_description, hook_message, tool_args) def to_dict(self) -> dict: result: dict = {} @@ -1089,10 +1115,16 @@ def to_dict(self) -> dict: result["toolTitle"] = from_union([from_str, from_none], self.tool_title) if self.url is not None: result["url"] = from_union([from_str, from_none], self.url) + if self.action is not None: + result["action"] = from_union([lambda x: to_enum(PermissionRequestAction, x), from_none], self.action) if self.citations is not None: result["citations"] = from_union([from_str, from_none], self.citations) + if self.direction is not None: + result["direction"] = from_union([lambda x: to_enum(Direction, x), from_none], self.direction) if self.fact is not None: result["fact"] = from_union([from_str, from_none], self.fact) + if self.reason is not None: + result["reason"] = from_union([from_str, from_none], self.reason) if self.subject is not None: result["subject"] = from_union([from_str, from_none], self.subject) if self.tool_description is not None: @@ -2495,7 +2527,7 @@ class Data: requested_schema: RequestedSchema | None = None """JSON Schema describing the form fields to present to the user (form mode only)""" - action: Action | None = None + action: DataAction | None = None """The user action: "accept" (submitted form), "decline" (explicitly refused), or "cancel" (dismissed) """ @@ -2731,7 +2763,7 @@ def from_dict(obj: Any) -> 'Data': elicitation_source = from_union([from_str, from_none], obj.get("elicitationSource")) mode = from_union([Mode, from_none], obj.get("mode")) requested_schema = from_union([RequestedSchema.from_dict, from_none], obj.get("requestedSchema")) - action = from_union([Action, from_none], obj.get("action")) + action = from_union([DataAction, from_none], obj.get("action")) mcp_request_id = from_union([from_float, from_str, from_none], obj.get("mcpRequestId")) server_name = from_union([from_str, from_none], obj.get("serverName")) server_url = from_union([from_str, from_none], obj.get("serverUrl")) @@ -3058,7 +3090,7 @@ def to_dict(self) -> dict: if self.requested_schema is not None: result["requestedSchema"] = from_union([lambda x: to_class(RequestedSchema, x), from_none], self.requested_schema) if self.action is not None: - result["action"] = from_union([lambda x: to_enum(Action, x), from_none], self.action) + result["action"] = from_union([lambda x: to_enum(DataAction, x), from_none], self.action) if self.mcp_request_id is not None: result["mcpRequestId"] = from_union([to_float, from_str, from_none], self.mcp_request_id) if self.server_name is not None: diff --git a/scripts/codegen/csharp.ts b/scripts/codegen/csharp.ts index 9049cb38c..e6ec8b057 100644 --- a/scripts/codegen/csharp.ts +++ b/scripts/codegen/csharp.ts @@ -596,6 +596,7 @@ export async function generateSessionEvents(schemaPath?: string): Promise // ══════════════════════════════════════════════════════════════════════════════ let emittedRpcClasses = new Set(); +let emittedRpcClassSchemas = new Map(); let experimentalRpcTypes = new Set(); let rpcKnownTypes = new Map(); let rpcEnumOutput: string[] = []; @@ -616,6 +617,31 @@ function paramsTypeName(rpcMethod: string): string { return `${typeToClassName(rpcMethod)}Params`; } +function stableStringify(value: unknown): string { + if (Array.isArray(value)) { + return `[${value.map((item) => stableStringify(item)).join(",")}]`; + } + if (value && typeof value === "object") { + const entries = Object.entries(value as Record).sort(([a], [b]) => a.localeCompare(b)); + return `{${entries.map(([key, entryValue]) => `${JSON.stringify(key)}:${stableStringify(entryValue)}`).join(",")}}`; + } + return JSON.stringify(value); +} + +function chooseRpcClassName(preferredName: string, fallbackName: string, schema: JSONSchema7): string { + const schemaKey = stableStringify(schema); + const existingPreferred = emittedRpcClassSchemas.get(preferredName); + if (!existingPreferred || existingPreferred === schemaKey) return preferredName; + + let candidate = fallbackName; + let suffix = 2; + while (true) { + const existing = emittedRpcClassSchemas.get(candidate); + if (!existing || existing === schemaKey) return candidate; + candidate = `${fallbackName}${suffix++}`; + } +} + function resolveRpcType(schema: JSONSchema7, isRequired: boolean, parentClassName: string, propName: string, classes: string[]): string { // Handle anyOf: [T, null] → T? (nullable typed property) if (schema.anyOf) { @@ -638,7 +664,9 @@ function resolveRpcType(schema: JSONSchema7, isRequired: boolean, parentClassNam if (schema.type === "array" && schema.items) { const items = schema.items as JSONSchema7; if (items.type === "object" && items.properties) { - const itemClass = singularPascal(propName); + const defaultName = (items.title as string) ?? singularPascal(propName); + const contextualName = `${parentClassName}${defaultName}`; + const itemClass = chooseRpcClassName(defaultName, contextualName, items); if (!emittedRpcClasses.has(itemClass)) classes.push(emitRpcClass(itemClass, items, "public", classes)); return isRequired ? `List<${itemClass}>` : `List<${itemClass}>?`; } @@ -661,6 +689,7 @@ function resolveRpcType(schema: JSONSchema7, isRequired: boolean, parentClassNam function emitRpcClass(className: string, schema: JSONSchema7, visibility: "public" | "internal", extraClasses: string[]): string { if (emittedRpcClasses.has(className)) return ""; emittedRpcClasses.add(className); + emittedRpcClassSchemas.set(className, stableStringify(schema)); const requiredSet = new Set(schema.required || []); const lines: string[] = []; @@ -1061,6 +1090,7 @@ function emitClientSessionApiRegistration(clientSchema: Record, function generateRpcCode(schema: ApiSchema): string { emittedRpcClasses.clear(); + emittedRpcClassSchemas.clear(); experimentalRpcTypes.clear(); rpcKnownTypes.clear(); rpcEnumOutput = []; diff --git a/test/harness/package-lock.json b/test/harness/package-lock.json index 7b3277eba..691d66bf9 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.21", + "@github/copilot": "^1.0.22", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "openai": "^6.17.0", @@ -462,27 +462,27 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.21.tgz", - "integrity": "sha512-P+nORjNKAtl92jYCG6Qr1Rsw2JoyScgeQSkIR6O2WB37WS5JVdA4ax1WVualMbfuc9V58CPHX6fwyNpkI89FkQ==", + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.22.tgz", + "integrity": "sha512-BR9oTJ1tQ51RV81xcxmlZe0zB3Tf8i/vFsKSTm2f5wRLJgtuVl2LgaFStoI/peTFcmgtZbhrqsnWTu5GkEPK5Q==", "dev": true, "license": "SEE LICENSE IN LICENSE.md", "bin": { "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.21", - "@github/copilot-darwin-x64": "1.0.21", - "@github/copilot-linux-arm64": "1.0.21", - "@github/copilot-linux-x64": "1.0.21", - "@github/copilot-win32-arm64": "1.0.21", - "@github/copilot-win32-x64": "1.0.21" + "@github/copilot-darwin-arm64": "1.0.22", + "@github/copilot-darwin-x64": "1.0.22", + "@github/copilot-linux-arm64": "1.0.22", + "@github/copilot-linux-x64": "1.0.22", + "@github/copilot-win32-arm64": "1.0.22", + "@github/copilot-win32-x64": "1.0.22" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.21.tgz", - "integrity": "sha512-aB+s9ldTwcyCOYmzjcQ4SknV6g81z92T8aUJEJZBwOXOTBeWKAJtk16ooAKangZgdwuLgO3or1JUjx1FJAm5nQ==", + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.22.tgz", + "integrity": "sha512-cK42uX+oz46Cjsb7z+rdPw+DIGczfVSFWlc1WDcdVlwBW4cEfV0pzFXExpN1r1z179TFgAaVMbhkgLqhOZ/PeQ==", "cpu": [ "arm64" ], @@ -497,9 +497,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.21.tgz", - "integrity": "sha512-aNad81DOGuGShmaiFNIxBUSZLwte0dXmDYkGfAF9WJIgY4qP4A8CPWFoNr8//gY+4CwaIf9V+f/OC6k2BdECbw==", + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.22.tgz", + "integrity": "sha512-Pmw0ipF+yeLbP6JctsEoMS2LUCpVdC2r557BnCoe48BN8lO8i9JLnkpuDDrJ1AZuCk1VjnujFKEQywOOdfVlpA==", "cpu": [ "x64" ], @@ -514,9 +514,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.21.tgz", - "integrity": "sha512-FL0NsCnHax4czHVv1S8iBqPLGZDhZ28N3+6nT29xWGhmjBWTkIofxLThKUPcyyMsfPTTxIlrdwWa8qQc5z2Q+g==", + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.22.tgz", + "integrity": "sha512-WVgG67VmZgHoD7GMlkTxEVe1qK8k9Ek9A02/Da7obpsDdtBInt3nJTwBEgm4cNDM4XaenQH17/jmwVtTwXB6lw==", "cpu": [ "arm64" ], @@ -531,9 +531,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.21.tgz", - "integrity": "sha512-S7pWVI16hesZtxYbIyfw+MHZpc5ESoGKUVr5Y+lZJNaM2340gJGPQzQwSpvKIRMLHRKI2hXLwciAnYeMFxE/Tg==", + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.22.tgz", + "integrity": "sha512-XRkHVFmdC7FMrczXOdPjbNKiknMr13asKtwJoErJO/Xdy4cmzKQHSvNsBk8VNrr7oyWrUcB1F6mbIxb2LFxPOw==", "cpu": [ "x64" ], @@ -548,9 +548,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.21.tgz", - "integrity": "sha512-a9qc2Ku+XbyBkXCclbIvBbIVnECACTIWnPctmXWsQeSdeapGxgfHGux7y8hAFV5j6+nhCm6cnyEMS3rkZjAhdA==", + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.22.tgz", + "integrity": "sha512-Ao6gv1f2ZV+HVlkB1MV7YFdCuaB3NcFCnNu0a6/WLl2ypsfP1vWosPPkIB32jQJeBkT9ku3exOZLRj+XC0P3Mg==", "cpu": [ "arm64" ], @@ -565,9 +565,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.21.tgz", - "integrity": "sha512-9klu+7NQ6tEyb8sibb0rsbimBivDrnNltZho10Bgbf1wh3o+erTjffXDjW9Zkyaw8lZA9Fz8bqhVkKntZq58Lg==", + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.22.tgz", + "integrity": "sha512-EppcL+3TpxC+X/eQEIYtkN0PaA3/cvtI9UJqldLIkKDPXNYk/0mw877Ru9ypRcBWBWokDN6iKIWk5IxYH+JIvg==", "cpu": [ "x64" ], diff --git a/test/harness/package.json b/test/harness/package.json index d9b9ea64b..def9f09cf 100644 --- a/test/harness/package.json +++ b/test/harness/package.json @@ -11,7 +11,7 @@ "test": "vitest run" }, "devDependencies": { - "@github/copilot": "^1.0.21", + "@github/copilot": "^1.0.22", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "openai": "^6.17.0",