diff --git a/.github/workflows/sdk-e2e-tests.yml b/.github/workflows/sdk-e2e-tests.yml index 3665f051..b95b1d32 100644 --- a/.github/workflows/sdk-e2e-tests.yml +++ b/.github/workflows/sdk-e2e-tests.yml @@ -111,7 +111,7 @@ jobs: env: COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} COPILOT_CLI_PATH: ${{ steps.cli-path.outputs.path }} - run: ./test.sh + run: /bin/bash test.sh python-sdk: name: "Python SDK Tests" diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs index 98e7d747..6962a977 100644 --- a/dotnet/src/Client.cs +++ b/dotnet/src/Client.cs @@ -464,6 +464,50 @@ public async Task PingAsync(string? message = null, CancellationTo "ping", [new { message }], cancellationToken); } + /// + /// Gets CLI status including version and protocol information. + /// + /// A that can be used to cancel the operation. + /// A task that resolves with the status response containing version and protocol version. + /// Thrown when the client is not connected. + public async Task GetStatusAsync(CancellationToken cancellationToken = default) + { + var connection = await EnsureConnectedAsync(cancellationToken); + + return await connection.Rpc.InvokeWithCancellationAsync( + "status.get", [], cancellationToken); + } + + /// + /// Gets current authentication status. + /// + /// A that can be used to cancel the operation. + /// A task that resolves with the authentication status. + /// Thrown when the client is not connected. + public async Task GetAuthStatusAsync(CancellationToken cancellationToken = default) + { + var connection = await EnsureConnectedAsync(cancellationToken); + + return await connection.Rpc.InvokeWithCancellationAsync( + "auth.getStatus", [], cancellationToken); + } + + /// + /// Lists available models with their metadata. + /// + /// A that can be used to cancel the operation. + /// A task that resolves with a list of available models. + /// Thrown when the client is not connected or not authenticated. + public async Task> ListModelsAsync(CancellationToken cancellationToken = default) + { + var connection = await EnsureConnectedAsync(cancellationToken); + + var response = await connection.Rpc.InvokeWithCancellationAsync( + "models.list", [], cancellationToken); + + return response.Models; + } + /// /// Gets the ID of the most recently used session. /// diff --git a/dotnet/src/Generated/SessionEvents.cs b/dotnet/src/Generated/SessionEvents.cs index 6a3b9c5f..d0fb44e0 100644 --- a/dotnet/src/Generated/SessionEvents.cs +++ b/dotnet/src/Generated/SessionEvents.cs @@ -6,7 +6,7 @@ // // Generated from: @github/copilot/session-events.schema.json // Generated by: scripts/generate-session-types.ts -// Generated at: 2026-01-21T14:50:29.306Z +// Generated at: 2026-01-22T04:11:05.393Z // // To update these types: // 1. Update the schema in copilot-agent-runtime @@ -58,6 +58,7 @@ namespace GitHub.Copilot.SDK [JsonDerivedType(typeof(SystemMessageEvent), "system.message")] [JsonDerivedType(typeof(ToolExecutionCompleteEvent), "tool.execution_complete")] [JsonDerivedType(typeof(ToolExecutionPartialResultEvent), "tool.execution_partial_result")] + [JsonDerivedType(typeof(ToolExecutionProgressEvent), "tool.execution_progress")] [JsonDerivedType(typeof(ToolExecutionStartEvent), "tool.execution_start")] [JsonDerivedType(typeof(ToolUserRequestedEvent), "tool.user_requested")] [JsonDerivedType(typeof(UserMessageEvent), "user.message")] @@ -389,6 +390,18 @@ public partial class ToolExecutionPartialResultEvent : SessionEvent public required ToolExecutionPartialResultData Data { get; set; } } + /// + /// Event: tool.execution_progress + /// + public partial class ToolExecutionProgressEvent : SessionEvent + { + [JsonIgnore] + public override string Type => "tool.execution_progress"; + + [JsonPropertyName("data")] + public required ToolExecutionProgressData Data { get; set; } + } + /// /// Event: tool.execution_complete /// @@ -850,6 +863,15 @@ public partial class ToolExecutionPartialResultData public required string PartialOutput { get; set; } } + public partial class ToolExecutionProgressData + { + [JsonPropertyName("toolCallId")] + public required string ToolCallId { get; set; } + + [JsonPropertyName("progressMessage")] + public required string ProgressMessage { get; set; } + } + public partial class ToolExecutionCompleteData { [JsonPropertyName("toolCallId")] diff --git a/dotnet/src/SdkProtocolVersion.cs b/dotnet/src/SdkProtocolVersion.cs index cbf0f7d6..bb47dfeb 100644 --- a/dotnet/src/SdkProtocolVersion.cs +++ b/dotnet/src/SdkProtocolVersion.cs @@ -11,7 +11,7 @@ internal static class SdkProtocolVersion /// /// The SDK protocol version. /// - public const int Version = 1; + public const int Version = 2; /// /// Gets the SDK protocol version. diff --git a/dotnet/src/Types.cs b/dotnet/src/Types.cs index 91fcbcb2..68a24217 100644 --- a/dotnet/src/Types.cs +++ b/dotnet/src/Types.cs @@ -413,3 +413,150 @@ public class PingResponse public long Timestamp { get; set; } public int? ProtocolVersion { get; set; } } + +/// +/// Response from status.get +/// +public class GetStatusResponse +{ + /// Package version (e.g., "1.0.0") + [JsonPropertyName("version")] + public string Version { get; set; } = string.Empty; + + /// Protocol version for SDK compatibility + [JsonPropertyName("protocolVersion")] + public int ProtocolVersion { get; set; } +} + +/// +/// Response from auth.getStatus +/// +public class GetAuthStatusResponse +{ + /// Whether the user is authenticated + [JsonPropertyName("isAuthenticated")] + public bool IsAuthenticated { get; set; } + + /// Authentication type (user, env, gh-cli, hmac, api-key, token) + [JsonPropertyName("authType")] + public string? AuthType { get; set; } + + /// GitHub host URL + [JsonPropertyName("host")] + public string? Host { get; set; } + + /// User login name + [JsonPropertyName("login")] + public string? Login { get; set; } + + /// Human-readable status message + [JsonPropertyName("statusMessage")] + public string? StatusMessage { get; set; } +} + +/// +/// Model vision-specific limits +/// +public class ModelVisionLimits +{ + [JsonPropertyName("supported_media_types")] + public List SupportedMediaTypes { get; set; } = new(); + + [JsonPropertyName("max_prompt_images")] + public int MaxPromptImages { get; set; } + + [JsonPropertyName("max_prompt_image_size")] + public int MaxPromptImageSize { get; set; } +} + +/// +/// Model limits +/// +public class ModelLimits +{ + [JsonPropertyName("max_prompt_tokens")] + public int? MaxPromptTokens { get; set; } + + [JsonPropertyName("max_context_window_tokens")] + public int MaxContextWindowTokens { get; set; } + + [JsonPropertyName("vision")] + public ModelVisionLimits? Vision { get; set; } +} + +/// +/// Model support flags +/// +public class ModelSupports +{ + [JsonPropertyName("vision")] + public bool Vision { get; set; } +} + +/// +/// Model capabilities and limits +/// +public class ModelCapabilities +{ + [JsonPropertyName("supports")] + public ModelSupports Supports { get; set; } = new(); + + [JsonPropertyName("limits")] + public ModelLimits Limits { get; set; } = new(); +} + +/// +/// Model policy state +/// +public class ModelPolicy +{ + [JsonPropertyName("state")] + public string State { get; set; } = string.Empty; + + [JsonPropertyName("terms")] + public string Terms { get; set; } = string.Empty; +} + +/// +/// Model billing information +/// +public class ModelBilling +{ + [JsonPropertyName("multiplier")] + public double Multiplier { get; set; } +} + +/// +/// Information about an available model +/// +public class ModelInfo +{ + /// Model identifier (e.g., "claude-sonnet-4.5") + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// Display name + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Model capabilities and limits + [JsonPropertyName("capabilities")] + public ModelCapabilities Capabilities { get; set; } = new(); + + /// Policy state + [JsonPropertyName("policy")] + public ModelPolicy? Policy { get; set; } + + /// Billing information + [JsonPropertyName("billing")] + public ModelBilling? Billing { get; set; } +} + +/// +/// Response from models.list +/// +public class GetModelsResponse +{ + [JsonPropertyName("models")] + public List Models { get; set; } = new(); +} diff --git a/dotnet/test/ClientTests.cs b/dotnet/test/ClientTests.cs index 4617ae94..23b0d9d9 100644 --- a/dotnet/test/ClientTests.cs +++ b/dotnet/test/ClientTests.cs @@ -89,4 +89,87 @@ public async Task Should_Force_Stop_Without_Cleanup() Assert.Equal(ConnectionState.Disconnected, client.State); } + + [Fact] + public async Task Should_Get_Status_With_Version_And_Protocol_Info() + { + using var client = new CopilotClient(new CopilotClientOptions { CliPath = _cliPath, UseStdio = true }); + + try + { + await client.StartAsync(); + + var status = await client.GetStatusAsync(); + Assert.NotNull(status.Version); + Assert.NotEmpty(status.Version); + Assert.True(status.ProtocolVersion >= 1); + + await client.StopAsync(); + } + finally + { + await client.ForceStopAsync(); + } + } + + [Fact] + public async Task Should_Get_Auth_Status() + { + using var client = new CopilotClient(new CopilotClientOptions { CliPath = _cliPath, UseStdio = true }); + + try + { + await client.StartAsync(); + + var authStatus = await client.GetAuthStatusAsync(); + // isAuthenticated is a bool, just verify we got a response + if (authStatus.IsAuthenticated) + { + Assert.NotNull(authStatus.AuthType); + Assert.NotNull(authStatus.StatusMessage); + } + + await client.StopAsync(); + } + finally + { + await client.ForceStopAsync(); + } + } + + [Fact] + public async Task Should_List_Models_When_Authenticated() + { + using var client = new CopilotClient(new CopilotClientOptions { CliPath = _cliPath, UseStdio = true }); + + try + { + await client.StartAsync(); + + var authStatus = await client.GetAuthStatusAsync(); + if (!authStatus.IsAuthenticated) + { + // Skip if not authenticated - models.list requires auth + await client.StopAsync(); + return; + } + + var models = await client.ListModelsAsync(); + Assert.NotNull(models); + if (models.Count > 0) + { + var model = models[0]; + Assert.NotNull(model.Id); + Assert.NotEmpty(model.Id); + Assert.NotNull(model.Name); + Assert.NotNull(model.Capabilities); + } + + await client.StopAsync(); + } + finally + { + await client.ForceStopAsync(); + } + } } diff --git a/go/client.go b/go/client.go index 07d18251..fe62d9f7 100644 --- a/go/client.go +++ b/go/client.go @@ -765,6 +765,84 @@ func (c *Client) Ping(message string) (*PingResponse, error) { return response, nil } +// GetStatus returns CLI status including version and protocol information +func (c *Client) GetStatus() (*GetStatusResponse, error) { + if c.client == nil { + return nil, fmt.Errorf("client not connected") + } + + result, err := c.client.Request("status.get", map[string]interface{}{}) + if err != nil { + return nil, err + } + + response := &GetStatusResponse{} + if v, ok := result["version"].(string); ok { + response.Version = v + } + if pv, ok := result["protocolVersion"].(float64); ok { + response.ProtocolVersion = int(pv) + } + + return response, nil +} + +// GetAuthStatus returns current authentication status +func (c *Client) GetAuthStatus() (*GetAuthStatusResponse, error) { + if c.client == nil { + return nil, fmt.Errorf("client not connected") + } + + result, err := c.client.Request("auth.getStatus", map[string]interface{}{}) + if err != nil { + return nil, err + } + + response := &GetAuthStatusResponse{} + if v, ok := result["isAuthenticated"].(bool); ok { + response.IsAuthenticated = v + } + if v, ok := result["authType"].(string); ok { + response.AuthType = &v + } + if v, ok := result["host"].(string); ok { + response.Host = &v + } + if v, ok := result["login"].(string); ok { + response.Login = &v + } + if v, ok := result["statusMessage"].(string); ok { + response.StatusMessage = &v + } + + return response, nil +} + +// ListModels returns available models with their metadata +func (c *Client) ListModels() ([]ModelInfo, error) { + if c.client == nil { + return nil, fmt.Errorf("client not connected") + } + + result, err := c.client.Request("models.list", map[string]interface{}{}) + if err != nil { + return nil, err + } + + // Marshal and unmarshal to convert map to struct + jsonBytes, err := json.Marshal(result) + if err != nil { + return nil, fmt.Errorf("failed to marshal models response: %w", err) + } + + var response GetModelsResponse + if err := json.Unmarshal(jsonBytes, &response); err != nil { + return nil, fmt.Errorf("failed to unmarshal models response: %w", err) + } + + return response.Models, nil +} + // verifyProtocolVersion verifies that the server's protocol version matches the SDK's expected version func (c *Client) verifyProtocolVersion() error { expectedVersion := GetSdkProtocolVersion() diff --git a/go/e2e/client_test.go b/go/e2e/client_test.go index 75033639..9d829213 100644 --- a/go/e2e/client_test.go +++ b/go/e2e/client_test.go @@ -130,4 +130,100 @@ func TestClient(t *testing.T) { t.Errorf("Expected state to be 'disconnected', got %q", client.GetState()) } }) + + t.Run("should get status with version and protocol info", func(t *testing.T) { + client := copilot.NewClient(&copilot.ClientOptions{ + CLIPath: cliPath, + UseStdio: true, + }) + t.Cleanup(func() { client.ForceStop() }) + + if err := client.Start(); err != nil { + t.Fatalf("Failed to start client: %v", err) + } + + status, err := client.GetStatus() + if err != nil { + t.Fatalf("Failed to get status: %v", err) + } + + if status.Version == "" { + t.Error("Expected status.Version to be non-empty") + } + + if status.ProtocolVersion < 1 { + t.Errorf("Expected status.ProtocolVersion >= 1, got %d", status.ProtocolVersion) + } + + client.Stop() + }) + + t.Run("should get auth status", func(t *testing.T) { + client := copilot.NewClient(&copilot.ClientOptions{ + CLIPath: cliPath, + UseStdio: true, + }) + t.Cleanup(func() { client.ForceStop() }) + + if err := client.Start(); err != nil { + t.Fatalf("Failed to start client: %v", err) + } + + authStatus, err := client.GetAuthStatus() + if err != nil { + t.Fatalf("Failed to get auth status: %v", err) + } + + // isAuthenticated is a bool, just verify we got a response + if authStatus.IsAuthenticated { + if authStatus.AuthType == nil { + t.Error("Expected authType to be set when authenticated") + } + if authStatus.StatusMessage == nil { + t.Error("Expected statusMessage to be set when authenticated") + } + } + + client.Stop() + }) + + t.Run("should list models when authenticated", func(t *testing.T) { + client := copilot.NewClient(&copilot.ClientOptions{ + CLIPath: cliPath, + UseStdio: true, + }) + t.Cleanup(func() { client.ForceStop() }) + + if err := client.Start(); err != nil { + t.Fatalf("Failed to start client: %v", err) + } + + authStatus, err := client.GetAuthStatus() + if err != nil { + t.Fatalf("Failed to get auth status: %v", err) + } + + if !authStatus.IsAuthenticated { + // Skip if not authenticated - models.list requires auth + client.Stop() + return + } + + models, err := client.ListModels() + if err != nil { + t.Fatalf("Failed to list models: %v", err) + } + + if len(models) > 0 { + model := models[0] + if model.ID == "" { + t.Error("Expected model.ID to be non-empty") + } + if model.Name == "" { + t.Error("Expected model.Name to be non-empty") + } + } + + client.Stop() + }) } diff --git a/go/generated_session_events.go b/go/generated_session_events.go index 80bd1dc1..64feeade 100644 --- a/go/generated_session_events.go +++ b/go/generated_session_events.go @@ -2,7 +2,7 @@ // // Generated from: @github/copilot/session-events.schema.json // Generated by: scripts/generate-session-types.ts -// Generated at: 2026-01-20T12:53:00.653Z +// Generated at: 2026-01-22T04:11:05.365Z // // To update these types: // 1. Update the schema in copilot-agent-runtime @@ -109,6 +109,7 @@ type Data struct { ToolCallID *string `json:"toolCallId,omitempty"` ToolName *string `json:"toolName,omitempty"` PartialOutput *string `json:"partialOutput,omitempty"` + ProgressMessage *string `json:"progressMessage,omitempty"` IsUserRequested *bool `json:"isUserRequested,omitempty"` Result *Result `json:"result,omitempty"` ToolTelemetry map[string]interface{} `json:"toolTelemetry,omitempty"` @@ -244,6 +245,7 @@ const ( SystemMessage SessionEventType = "system.message" ToolExecutionComplete SessionEventType = "tool.execution_complete" ToolExecutionPartialResult SessionEventType = "tool.execution_partial_result" + ToolExecutionProgress SessionEventType = "tool.execution_progress" ToolExecutionStart SessionEventType = "tool.execution_start" ToolUserRequested SessionEventType = "tool.user_requested" UserMessage SessionEventType = "user.message" diff --git a/go/sdk_protocol_version.go b/go/sdk_protocol_version.go index 34b416d3..52b1ebe0 100644 --- a/go/sdk_protocol_version.go +++ b/go/sdk_protocol_version.go @@ -4,7 +4,7 @@ package copilot // SdkProtocolVersion is the SDK protocol version. // This must match the version expected by the copilot-agent-runtime server. -const SdkProtocolVersion = 1 +const SdkProtocolVersion = 2 // GetSdkProtocolVersion returns the SDK protocol version. func GetSdkProtocolVersion() int { diff --git a/go/types.go b/go/types.go index 1a79d363..2fde2920 100644 --- a/go/types.go +++ b/go/types.go @@ -287,3 +287,68 @@ type SessionSendResponse struct { type SessionGetMessagesResponse struct { Events []SessionEvent `json:"events"` } + +// GetStatusResponse is the response from status.get +type GetStatusResponse struct { + Version string `json:"version"` + ProtocolVersion int `json:"protocolVersion"` +} + +// GetAuthStatusResponse is the response from auth.getStatus +type GetAuthStatusResponse struct { + IsAuthenticated bool `json:"isAuthenticated"` + AuthType *string `json:"authType,omitempty"` + Host *string `json:"host,omitempty"` + Login *string `json:"login,omitempty"` + StatusMessage *string `json:"statusMessage,omitempty"` +} + +// ModelVisionLimits contains vision-specific limits +type ModelVisionLimits struct { + SupportedMediaTypes []string `json:"supported_media_types"` + MaxPromptImages int `json:"max_prompt_images"` + MaxPromptImageSize int `json:"max_prompt_image_size"` +} + +// ModelLimits contains model limits +type ModelLimits struct { + MaxPromptTokens *int `json:"max_prompt_tokens,omitempty"` + MaxContextWindowTokens int `json:"max_context_window_tokens"` + Vision *ModelVisionLimits `json:"vision,omitempty"` +} + +// ModelSupports contains model support flags +type ModelSupports struct { + Vision bool `json:"vision"` +} + +// ModelCapabilities contains model capabilities and limits +type ModelCapabilities struct { + Supports ModelSupports `json:"supports"` + Limits ModelLimits `json:"limits"` +} + +// ModelPolicy contains model policy state +type ModelPolicy struct { + State string `json:"state"` + Terms string `json:"terms"` +} + +// ModelBilling contains model billing information +type ModelBilling struct { + Multiplier float64 `json:"multiplier"` +} + +// ModelInfo contains information about an available model +type ModelInfo struct { + ID string `json:"id"` + Name string `json:"name"` + Capabilities ModelCapabilities `json:"capabilities"` + Policy *ModelPolicy `json:"policy,omitempty"` + Billing *ModelBilling `json:"billing,omitempty"` +} + +// GetModelsResponse is the response from models.list +type GetModelsResponse struct { + Models []ModelInfo `json:"models"` +} diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index 3aa08b81..588eee35 100644 --- a/nodejs/package-lock.json +++ b/nodejs/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.8", "license": "MIT", "dependencies": { - "@github/copilot": "^0.0.388-1", + "@github/copilot": "^0.0.389", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.5" }, @@ -662,29 +662,26 @@ } }, "node_modules/@github/copilot": { - "version": "0.0.388-1", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-0.0.388-1.tgz", - "integrity": "sha512-cWpqmktEfv6VEAgBtWxCipujdDAPr6oXsaor46ii2GCkptEaIQNsuVppei+mAsnUD8vXyiBWsGX43zYN/CLsng==", - "license": "SEE LICENSE IN LICENSE.md", + "version": "0.0.389", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-0.0.389.tgz", + "integrity": "sha512-XCHMCd8fu7g9WAp+ZepXBF1ud8vdfxDG4ajstGJqHfbdz0RxQktB35R5s/vKizpYXSZogFqwjxl41qX8DypY6g==", + "license": "MIT", "bin": { "copilot": "npm-loader.js" }, - "engines": { - "node": ">=22" - }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "0.0.388-1", - "@github/copilot-darwin-x64": "0.0.388-1", - "@github/copilot-linux-arm64": "0.0.388-1", - "@github/copilot-linux-x64": "0.0.388-1", - "@github/copilot-win32-arm64": "0.0.388-1", - "@github/copilot-win32-x64": "0.0.388-1" + "@github/copilot-darwin-arm64": "0.0.389", + "@github/copilot-darwin-x64": "0.0.389", + "@github/copilot-linux-arm64": "0.0.389", + "@github/copilot-linux-x64": "0.0.389", + "@github/copilot-win32-arm64": "0.0.389", + "@github/copilot-win32-x64": "0.0.389" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "0.0.388-1", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-0.0.388-1.tgz", - "integrity": "sha512-KQX8J2zfU0a5cTBOQviStQQNtN3s8H1HK81gBOUm0cm7nge53Bq64yiWuROjgN8JQ0nxp7aWuPywpXQNMvg3VA==", + "version": "0.0.389", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-0.0.389.tgz", + "integrity": "sha512-4Crm/C9//ZPsK+NP5E5BEjltAGuij9XkvRILvZ/mqlaiDXRncFvUtdOoV+/Of+i4Zva/1sWnc7CrS7PHGJDyFg==", "cpu": [ "arm64" ], @@ -698,9 +695,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "0.0.388-1", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-0.0.388-1.tgz", - "integrity": "sha512-9/a3wzCEJ5yU/jdw2h8Ufc1wvXw7+vNcMO0/SkS+1s2YtgqaCgF8LitrTaPHqBPAS2iEW7IbffugT8QKCH3tIw==", + "version": "0.0.389", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-0.0.389.tgz", + "integrity": "sha512-w0LB+lw29UmRS9oW8ENyZhrf3S5LQ3Pz796dQY8LZybp7WxEGtQhvXN48mye9gGzOHNoHxQ2+10+OzsjC/mLUQ==", "cpu": [ "x64" ], @@ -714,9 +711,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "0.0.388-1", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-0.0.388-1.tgz", - "integrity": "sha512-ZvDfpEBqlBPJk0WaNCFWCDGgOOrK6E98dr5B5BKs0bs2nD9NGS17RY4Bk8lllUT6GqVEDuUykscLxwPp7pdi6Q==", + "version": "0.0.389", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-0.0.389.tgz", + "integrity": "sha512-8QNvfs4r6nrbQrT4llu0CbJHcCJosyj+ZgLSpA+lqIiO/TiTQ48kV41uNjzTz1RmR6/qBKcz81FB7HcHXpT3xw==", "cpu": [ "arm64" ], @@ -730,9 +727,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "0.0.388-1", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-0.0.388-1.tgz", - "integrity": "sha512-b7RpV0xFpBMwa6lepT3aqSOPir74NGrSv5FGqX9WRAHgbAv1UzvmVrpfY0n3NgoA51bMF9yDd/5MeEgsd53nHQ==", + "version": "0.0.389", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-0.0.389.tgz", + "integrity": "sha512-ls42wSzspC7sLiweoqu2zT75mqMsLWs+IZBfCqcuH1BV+C/j/XSEHsSrJxAI3TPtIsOTolPbTAa8jye1nGDxeg==", "cpu": [ "x64" ], @@ -746,9 +743,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "0.0.388-1", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-0.0.388-1.tgz", - "integrity": "sha512-Yp5f3webniqDjp5glnqAVtOPTbweR2FbsJcpp9yJjki75RBhOdleN/w9Y1Iw1rzaBpf3R1k2B5CDvpSyYVCagg==", + "version": "0.0.389", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-0.0.389.tgz", + "integrity": "sha512-loniaCnrty9okQMl3EhxeeyDhnrJ/lJK0Q0r7wkLf1d/TM2swp3tsGZyIRlhDKx5lgcnCPm1m0BqauMo8Vs34g==", "cpu": [ "arm64" ], @@ -762,9 +759,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "0.0.388-1", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-0.0.388-1.tgz", - "integrity": "sha512-j7WRegdWzFgo+lJa86Lbf5cdJriHJPQXfUcfBAkok7GZKu0WqMR9QVqwuhRqEE1P23W3Rr+KUTch0r21EMD3mQ==", + "version": "0.0.389", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-0.0.389.tgz", + "integrity": "sha512-L1ZzwV/vsxnrz0WO4qLDUlXXFQQ9fOFuBGKWy6TXS7aniaxI/7mdRQR1YjIEqy+AzRw9BaXR2UUUUDk0gb1+kw==", "cpu": [ "x64" ], diff --git a/nodejs/package.json b/nodejs/package.json index 878a3d6f..37cb1b1a 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -40,7 +40,7 @@ "author": "GitHub", "license": "MIT", "dependencies": { - "@github/copilot": "^0.0.388-1", + "@github/copilot": "^0.0.389", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.5" }, diff --git a/nodejs/scripts/generate-session-types.ts b/nodejs/scripts/generate-session-types.ts index 961d6bae..8a0063a3 100644 --- a/nodejs/scripts/generate-session-types.ts +++ b/nodejs/scripts/generate-session-types.ts @@ -211,6 +211,22 @@ async function generatePythonTypes(schemaPath: string) { // dataclass rules. We post-process to add "= None" to these unconstrained "Any" fields. generatedCode = generatedCode.replace(/: Any$/gm, ": Any = None"); + // Add UNKNOWN enum value and _missing_ handler for forward compatibility + // This ensures that new event types from the server don't cause errors + generatedCode = generatedCode.replace( + /^(class SessionEventType\(Enum\):.*?)(^\s*\n@dataclass)/ms, + `$1 # UNKNOWN is used for forward compatibility - new event types from the server + # will map to this value instead of raising an error + UNKNOWN = "unknown" + + @classmethod + def _missing_(cls, value: object) -> "SessionEventType": + """Handle unknown event types gracefully for forward compatibility.""" + return cls.UNKNOWN + +$2` + ); + const banner = `""" AUTO-GENERATED FILE - DO NOT EDIT diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index 8a2698d5..93e16d37 100644 --- a/nodejs/src/client.ts +++ b/nodejs/src/client.ts @@ -24,6 +24,9 @@ import { CopilotSession } from "./session.js"; import type { ConnectionState, CopilotClientOptions, + GetAuthStatusResponse, + GetStatusResponse, + ModelInfo, ResumeSessionConfig, SessionConfig, SessionEvent, @@ -568,6 +571,44 @@ export class CopilotClient { }; } + /** + * Get CLI status including version and protocol information + */ + async getStatus(): Promise { + if (!this.connection) { + throw new Error("Client not connected"); + } + + const result = await this.connection.sendRequest("status.get", {}); + return result as GetStatusResponse; + } + + /** + * Get current authentication status + */ + async getAuthStatus(): Promise { + if (!this.connection) { + throw new Error("Client not connected"); + } + + const result = await this.connection.sendRequest("auth.getStatus", {}); + return result as GetAuthStatusResponse; + } + + /** + * List available models with their metadata + * @throws Error if not authenticated + */ + async listModels(): Promise { + if (!this.connection) { + throw new Error("Client not connected"); + } + + const result = await this.connection.sendRequest("models.list", {}); + const response = result as { models: ModelInfo[] }; + return response.models; + } + /** * Verify that the server's protocol version matches the SDK's expected version */ diff --git a/nodejs/src/generated/session-events.ts b/nodejs/src/generated/session-events.ts index da8aa4e6..b86e97d5 100644 --- a/nodejs/src/generated/session-events.ts +++ b/nodejs/src/generated/session-events.ts @@ -3,7 +3,7 @@ * * Generated from: @github/copilot/session-events.schema.json * Generated by: scripts/generate-session-types.ts - * Generated at: 2026-01-20T04:18:06.227Z + * Generated at: 2026-01-22T04:11:04.988Z * * To update these types: * 1. Update the schema in copilot-agent-runtime @@ -354,6 +354,17 @@ export type SessionEvent = partialOutput: string; }; } + | { + id: string; + timestamp: string; + parentId: string | null; + ephemeral: true; + type: "tool.execution_progress"; + data: { + toolCallId: string; + progressMessage: string; + }; + } | { id: string; timestamp: string; diff --git a/nodejs/src/index.ts b/nodejs/src/index.ts index e5943bed..cfbd13b1 100644 --- a/nodejs/src/index.ts +++ b/nodejs/src/index.ts @@ -15,10 +15,16 @@ export type { ConnectionState, CopilotClientOptions, CustomAgentConfig, + GetAuthStatusResponse, + GetStatusResponse, MCPLocalServerConfig, MCPRemoteServerConfig, MCPServerConfig, MessageOptions, + ModelBilling, + ModelCapabilities, + ModelInfo, + ModelPolicy, PermissionHandler, PermissionRequest, PermissionRequestResult, diff --git a/nodejs/src/sdkProtocolVersion.ts b/nodejs/src/sdkProtocolVersion.ts index a6fe1c20..9485bc00 100644 --- a/nodejs/src/sdkProtocolVersion.ts +++ b/nodejs/src/sdkProtocolVersion.ts @@ -8,7 +8,7 @@ * The SDK protocol version. * This must match the version expected by the copilot-agent-runtime server. */ -export const SDK_PROTOCOL_VERSION = 1; +export const SDK_PROTOCOL_VERSION = 2; /** * Gets the SDK protocol version. diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index c9fe0414..bcc247a2 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -499,3 +499,78 @@ export interface SessionMetadata { summary?: string; isRemote: boolean; } + +/** + * Response from status.get + */ +export interface GetStatusResponse { + /** Package version (e.g., "1.0.0") */ + version: string; + /** Protocol version for SDK compatibility */ + protocolVersion: number; +} + +/** + * Response from auth.getStatus + */ +export interface GetAuthStatusResponse { + /** Whether the user is authenticated */ + isAuthenticated: boolean; + /** Authentication type */ + authType?: "user" | "env" | "gh-cli" | "hmac" | "api-key" | "token"; + /** GitHub host URL */ + host?: string; + /** User login name */ + login?: string; + /** Human-readable status message */ + statusMessage?: string; +} + +/** + * Model capabilities and limits + */ +export interface ModelCapabilities { + supports: { + vision: boolean; + }; + limits: { + max_prompt_tokens?: number; + max_context_window_tokens: number; + vision?: { + supported_media_types: string[]; + max_prompt_images: number; + max_prompt_image_size: number; + }; + }; +} + +/** + * Model policy state + */ +export interface ModelPolicy { + state: "enabled" | "disabled" | "unconfigured"; + terms: string; +} + +/** + * Model billing information + */ +export interface ModelBilling { + multiplier: number; +} + +/** + * Information about an available model + */ +export interface ModelInfo { + /** Model identifier (e.g., "claude-sonnet-4.5") */ + id: string; + /** Display name */ + name: string; + /** Model capabilities and limits */ + capabilities: ModelCapabilities; + /** Policy state */ + policy?: ModelPolicy; + /** Billing information */ + billing?: ModelBilling; +} diff --git a/nodejs/test/e2e/client.test.ts b/nodejs/test/e2e/client.test.ts index a08abe60..24992f66 100644 --- a/nodejs/test/e2e/client.test.ts +++ b/nodejs/test/e2e/client.test.ts @@ -74,4 +74,63 @@ describe("Client", () => { await client.forceStop(); expect(client.getState()).toBe("disconnected"); }); + + it("should get status with version and protocol info", async () => { + const client = new CopilotClient({ cliPath: CLI_PATH, useStdio: true }); + onTestFinishedForceStop(client); + + await client.start(); + + const status = await client.getStatus(); + expect(status.version).toBeDefined(); + expect(typeof status.version).toBe("string"); + expect(status.protocolVersion).toBeDefined(); + expect(typeof status.protocolVersion).toBe("number"); + expect(status.protocolVersion).toBeGreaterThanOrEqual(1); + + await client.stop(); + }); + + it("should get auth status", async () => { + const client = new CopilotClient({ cliPath: CLI_PATH, useStdio: true }); + onTestFinishedForceStop(client); + + await client.start(); + + const authStatus = await client.getAuthStatus(); + expect(typeof authStatus.isAuthenticated).toBe("boolean"); + if (authStatus.isAuthenticated) { + expect(authStatus.authType).toBeDefined(); + expect(authStatus.statusMessage).toBeDefined(); + } + + await client.stop(); + }); + + it("should list models when authenticated", async () => { + const client = new CopilotClient({ cliPath: CLI_PATH, useStdio: true }); + onTestFinishedForceStop(client); + + await client.start(); + + const authStatus = await client.getAuthStatus(); + if (!authStatus.isAuthenticated) { + // Skip if not authenticated - models.list requires auth + await client.stop(); + return; + } + + const models = await client.listModels(); + expect(Array.isArray(models)).toBe(true); + if (models.length > 0) { + const model = models[0]; + expect(model.id).toBeDefined(); + expect(model.name).toBeDefined(); + expect(model.capabilities).toBeDefined(); + expect(model.capabilities.supports).toBeDefined(); + expect(model.capabilities.limits).toBeDefined(); + } + + await client.stop(); + }); }); diff --git a/python/copilot/__init__.py b/python/copilot/__init__.py index 73f6d350..47a4ab6d 100644 --- a/python/copilot/__init__.py +++ b/python/copilot/__init__.py @@ -11,10 +11,16 @@ AzureProviderOptions, ConnectionState, CustomAgentConfig, + GetAuthStatusResponse, + GetStatusResponse, MCPLocalServerConfig, MCPRemoteServerConfig, MCPServerConfig, MessageOptions, + ModelBilling, + ModelCapabilities, + ModelInfo, + ModelPolicy, PermissionHandler, PermissionRequest, PermissionRequestResult, @@ -36,10 +42,16 @@ "CopilotSession", "ConnectionState", "CustomAgentConfig", + "GetAuthStatusResponse", + "GetStatusResponse", "MCPLocalServerConfig", "MCPRemoteServerConfig", "MCPServerConfig", "MessageOptions", + "ModelBilling", + "ModelCapabilities", + "ModelInfo", + "ModelPolicy", "PermissionHandler", "PermissionRequest", "PermissionRequestResult", diff --git a/python/copilot/client.py b/python/copilot/client.py index dfd949e9..030ee4f2 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -28,6 +28,9 @@ from .types import ( ConnectionState, CopilotClientOptions, + GetAuthStatusResponse, + GetStatusResponse, + ModelInfo, ResumeSessionConfig, SessionConfig, ToolHandler, @@ -569,6 +572,67 @@ async def ping(self, message: Optional[str] = None) -> dict: return await self._client.request("ping", {"message": message}) + async def get_status(self) -> "GetStatusResponse": + """ + Get CLI status including version and protocol information. + + Returns: + A GetStatusResponse containing version and protocolVersion. + + Raises: + RuntimeError: If the client is not connected. + + Example: + >>> status = await client.get_status() + >>> print(f"CLI version: {status['version']}") + """ + if not self._client: + raise RuntimeError("Client not connected") + + return await self._client.request("status.get", {}) + + async def get_auth_status(self) -> "GetAuthStatusResponse": + """ + Get current authentication status. + + Returns: + A GetAuthStatusResponse containing authentication state. + + Raises: + RuntimeError: If the client is not connected. + + Example: + >>> auth = await client.get_auth_status() + >>> if auth['isAuthenticated']: + ... print(f"Logged in as {auth.get('login')}") + """ + if not self._client: + raise RuntimeError("Client not connected") + + return await self._client.request("auth.getStatus", {}) + + async def list_models(self) -> List["ModelInfo"]: + """ + List available models with their metadata. + + Returns: + A list of ModelInfo objects with model details. + + Raises: + RuntimeError: If the client is not connected. + Exception: If not authenticated. + + Example: + >>> models = await client.list_models() + >>> for model in models: + ... print(f"{model['id']}: {model['name']}") + """ + if not self._client: + raise RuntimeError("Client not connected") + + response = await self._client.request("models.list", {}) + return response.get("models", []) + async def _verify_protocol_version(self) -> None: """Verify that the server's protocol version matches the SDK's expected version.""" expected_version = get_sdk_protocol_version() diff --git a/python/copilot/generated/session_events.py b/python/copilot/generated/session_events.py index fea87957..f8d8f4f2 100644 --- a/python/copilot/generated/session_events.py +++ b/python/copilot/generated/session_events.py @@ -3,7 +3,7 @@ Generated from: @github/copilot/session-events.schema.json Generated by: scripts/generate-session-types.ts -Generated at: 2026-01-20T04:18:06.607Z +Generated at: 2026-01-22T04:11:05.267Z To update these types: 1. Update the schema in copilot-agent-runtime @@ -390,6 +390,7 @@ class Data: tool_call_id: Optional[str] = None tool_name: Optional[str] = None partial_output: Optional[str] = None + progress_message: Optional[str] = None is_user_requested: Optional[bool] = None result: Optional[Result] = None tool_telemetry: Optional[Dict[str, Any]] = None @@ -475,6 +476,7 @@ def from_dict(obj: Any) -> 'Data': tool_call_id = from_union([from_str, from_none], obj.get("toolCallId")) tool_name = from_union([from_str, from_none], obj.get("toolName")) partial_output = from_union([from_str, from_none], obj.get("partialOutput")) + progress_message = from_union([from_str, from_none], obj.get("progressMessage")) is_user_requested = from_union([from_bool, from_none], obj.get("isUserRequested")) result = from_union([Result.from_dict, from_none], obj.get("result")) tool_telemetry = from_union([lambda x: from_dict(lambda x: x, x), from_none], obj.get("toolTelemetry")) @@ -489,7 +491,7 @@ def from_dict(obj: Any) -> 'Data': metadata = from_union([Metadata.from_dict, from_none], obj.get("metadata")) name = from_union([from_str, from_none], obj.get("name")) role = from_union([Role, from_none], obj.get("role")) - return Data(context, copilot_version, producer, selected_model, session_id, start_time, version, event_count, resume_time, error_type, message, stack, info_type, new_model, previous_model, handoff_time, remote_session_id, repository, source_type, summary, messages_removed_during_truncation, performed_by, post_truncation_messages_length, post_truncation_tokens_in_messages, pre_truncation_messages_length, pre_truncation_tokens_in_messages, token_limit, tokens_removed_during_truncation, current_tokens, messages_length, compaction_tokens_used, error, messages_removed, post_compaction_tokens, pre_compaction_messages_length, pre_compaction_tokens, success, summary_content, tokens_removed, attachments, content, source, transformed_content, turn_id, intent, reasoning_id, delta_content, message_id, parent_tool_call_id, tool_requests, total_response_size_bytes, api_call_id, cache_read_tokens, cache_write_tokens, cost, duration, initiator, input_tokens, model, output_tokens, provider_call_id, quota_snapshots, reason, arguments, tool_call_id, tool_name, partial_output, is_user_requested, result, tool_telemetry, agent_description, agent_display_name, agent_name, tools, hook_invocation_id, hook_type, input, output, metadata, name, role) + return Data(context, copilot_version, producer, selected_model, session_id, start_time, version, event_count, resume_time, error_type, message, stack, info_type, new_model, previous_model, handoff_time, remote_session_id, repository, source_type, summary, messages_removed_during_truncation, performed_by, post_truncation_messages_length, post_truncation_tokens_in_messages, pre_truncation_messages_length, pre_truncation_tokens_in_messages, token_limit, tokens_removed_during_truncation, current_tokens, messages_length, compaction_tokens_used, error, messages_removed, post_compaction_tokens, pre_compaction_messages_length, pre_compaction_tokens, success, summary_content, tokens_removed, attachments, content, source, transformed_content, turn_id, intent, reasoning_id, delta_content, message_id, parent_tool_call_id, tool_requests, total_response_size_bytes, api_call_id, cache_read_tokens, cache_write_tokens, cost, duration, initiator, input_tokens, model, output_tokens, provider_call_id, quota_snapshots, reason, arguments, tool_call_id, tool_name, partial_output, progress_message, is_user_requested, result, tool_telemetry, agent_description, agent_display_name, agent_name, tools, hook_invocation_id, hook_type, input, output, metadata, name, role) def to_dict(self) -> dict: result: dict = {} @@ -627,6 +629,8 @@ def to_dict(self) -> dict: result["toolName"] = from_union([from_str, from_none], self.tool_name) if self.partial_output is not None: result["partialOutput"] = from_union([from_str, from_none], self.partial_output) + if self.progress_message is not None: + result["progressMessage"] = from_union([from_str, from_none], self.progress_message) if self.is_user_requested is not None: result["isUserRequested"] = from_union([from_bool, from_none], self.is_user_requested) if self.result is not None: @@ -689,6 +693,7 @@ class SessionEventType(Enum): SYSTEM_MESSAGE = "system.message" TOOL_EXECUTION_COMPLETE = "tool.execution_complete" TOOL_EXECUTION_PARTIAL_RESULT = "tool.execution_partial_result" + TOOL_EXECUTION_PROGRESS = "tool.execution_progress" TOOL_EXECUTION_START = "tool.execution_start" TOOL_USER_REQUESTED = "tool.user_requested" USER_MESSAGE = "user.message" @@ -702,6 +707,7 @@ def _missing_(cls, value: object) -> "SessionEventType": return cls.UNKNOWN + @dataclass class SessionEvent: data: Data diff --git a/python/copilot/sdk_protocol_version.py b/python/copilot/sdk_protocol_version.py index 0410dd91..77008267 100644 --- a/python/copilot/sdk_protocol_version.py +++ b/python/copilot/sdk_protocol_version.py @@ -6,7 +6,7 @@ This must match the version expected by the copilot-agent-runtime server. """ -SDK_PROTOCOL_VERSION = 1 +SDK_PROTOCOL_VERSION = 2 def get_sdk_protocol_version() -> int: diff --git a/python/copilot/types.py b/python/copilot/types.py index f8ca3908..6a4d0b8d 100644 --- a/python/copilot/types.py +++ b/python/copilot/types.py @@ -279,3 +279,83 @@ class MessageOptions(TypedDict): # Event handler type SessionEventHandler = Callable[[SessionEvent], None] + + +# Response from status.get +class GetStatusResponse(TypedDict): + """Response from status.get""" + + version: str # Package version (e.g., "1.0.0") + protocolVersion: int # Protocol version for SDK compatibility + + +# Response from auth.getStatus +class GetAuthStatusResponse(TypedDict): + """Response from auth.getStatus""" + + isAuthenticated: bool # Whether the user is authenticated + authType: NotRequired[ + Literal["user", "env", "gh-cli", "hmac", "api-key", "token"] + ] # Authentication type + host: NotRequired[str] # GitHub host URL + login: NotRequired[str] # User login name + statusMessage: NotRequired[str] # Human-readable status message + + +# Model capabilities +class ModelVisionLimits(TypedDict, total=False): + """Vision-specific limits""" + + supported_media_types: List[str] + max_prompt_images: int + max_prompt_image_size: int + + +class ModelLimits(TypedDict, total=False): + """Model limits""" + + max_prompt_tokens: int + max_context_window_tokens: int + vision: ModelVisionLimits + + +class ModelSupports(TypedDict): + """Model support flags""" + + vision: bool + + +class ModelCapabilities(TypedDict): + """Model capabilities and limits""" + + supports: ModelSupports + limits: ModelLimits + + +class ModelPolicy(TypedDict): + """Model policy state""" + + state: Literal["enabled", "disabled", "unconfigured"] + terms: str + + +class ModelBilling(TypedDict): + """Model billing information""" + + multiplier: float + + +class ModelInfo(TypedDict): + """Information about an available model""" + + id: str # Model identifier (e.g., "claude-sonnet-4.5") + name: str # Display name + capabilities: ModelCapabilities # Model capabilities and limits + policy: NotRequired[ModelPolicy] # Policy state + billing: NotRequired[ModelBilling] # Billing information + + +class GetModelsResponse(TypedDict): + """Response from models.list""" + + models: List[ModelInfo] diff --git a/python/e2e/test_client.py b/python/e2e/test_client.py index 6d24616a..5cb681ce 100644 --- a/python/e2e/test_client.py +++ b/python/e2e/test_client.py @@ -72,3 +72,66 @@ async def test_should_force_stop_without_cleanup(self): await client.create_session() await client.force_stop() assert client.get_state() == "disconnected" + + @pytest.mark.asyncio + async def test_should_get_status_with_version_and_protocol_info(self): + client = CopilotClient({"cli_path": CLI_PATH, "use_stdio": True}) + + try: + await client.start() + + status = await client.get_status() + assert "version" in status + assert isinstance(status["version"], str) + assert "protocolVersion" in status + assert isinstance(status["protocolVersion"], int) + assert status["protocolVersion"] >= 1 + + await client.stop() + finally: + await client.force_stop() + + @pytest.mark.asyncio + async def test_should_get_auth_status(self): + client = CopilotClient({"cli_path": CLI_PATH, "use_stdio": True}) + + try: + await client.start() + + auth_status = await client.get_auth_status() + assert "isAuthenticated" in auth_status + assert isinstance(auth_status["isAuthenticated"], bool) + if auth_status["isAuthenticated"]: + assert "authType" in auth_status + assert "statusMessage" in auth_status + + await client.stop() + finally: + await client.force_stop() + + @pytest.mark.asyncio + async def test_should_list_models_when_authenticated(self): + client = CopilotClient({"cli_path": CLI_PATH, "use_stdio": True}) + + try: + await client.start() + + auth_status = await client.get_auth_status() + if not auth_status["isAuthenticated"]: + # Skip if not authenticated - models.list requires auth + await client.stop() + return + + models = await client.list_models() + assert isinstance(models, list) + if len(models) > 0: + model = models[0] + assert "id" in model + assert "name" in model + assert "capabilities" in model + assert "supports" in model["capabilities"] + assert "limits" in model["capabilities"] + + await client.stop() + finally: + await client.force_stop() diff --git a/sdk-protocol-version.json b/sdk-protocol-version.json index a20af2bd..4bb5680c 100644 --- a/sdk-protocol-version.json +++ b/sdk-protocol-version.json @@ -1,3 +1,3 @@ { - "version": 1 + "version": 2 } diff --git a/test/snapshots/session/should_abort_a_session.yaml b/test/snapshots/session/should_abort_a_session.yaml index 2d268cf1..24b97b2d 100644 --- a/test/snapshots/session/should_abort_a_session.yaml +++ b/test/snapshots/session/should_abort_a_session.yaml @@ -6,8 +6,6 @@ conversations: content: ${system} - role: user content: run the shell command 'sleep 100' (note this works on both bash and PowerShell) - - role: assistant - content: I'll run the sleep command for 100 seconds. - role: assistant tool_calls: - id: toolcall_0 @@ -21,14 +19,13 @@ conversations: type: function function: name: ${shell} - arguments: '{"command":"sleep 100","description":"Run sleep 100 command","mode":"sync","initial_wait":105}' + arguments: '{"description":"Run sleep 100 command","command":"sleep 100","mode":"sync","initial_wait":30}' - messages: - role: system content: ${system} - role: user content: run the shell command 'sleep 100' (note this works on both bash and PowerShell) - role: assistant - content: I'll run the sleep command for 100 seconds. tool_calls: - id: toolcall_0 type: function @@ -39,7 +36,7 @@ conversations: type: function function: name: ${shell} - arguments: '{"command":"sleep 100","description":"Run sleep 100 command","mode":"sync","initial_wait":105}' + arguments: '{"description":"Run sleep 100 command","command":"sleep 100","mode":"sync","initial_wait":30}' - role: tool tool_call_id: toolcall_0 content: Intent logged @@ -49,4 +46,4 @@ conversations: - role: user content: What is 2+2? - role: assistant - content: 2 + 2 = 4 + content: 2+2 = 4