diff --git a/package-lock.json b/package-lock.json index 7e70dc63..f2a6f2d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "Apache-2.0", "dependencies": { "@agentclientprotocol/sdk": "0.24.0", - "@anthropic-ai/claude-agent-sdk": "0.3.160", + "@anthropic-ai/claude-agent-sdk": "0.3.162", "zod": "^3.25.0 || ^4.0.0" }, "bin": { @@ -41,22 +41,22 @@ } }, "node_modules/@anthropic-ai/claude-agent-sdk": { - "version": "0.3.160", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.160.tgz", - "integrity": "sha512-kJjgIkb09TIhYXbOEngQV+iERhYMbxQwjciXEfVYP/Zo27tacdWYS5MaIwzKxmPW+Yd1VL2BcvCIo1ES4F1tow==", + "version": "0.3.162", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.162.tgz", + "integrity": "sha512-piAlpc1h6FUMCNU+bnYNNLX1lOgMlFnqZmIhn6Myv48MaNRaecFaZEuVLY+UxV0kjGiQFYHBLR4fk/rRwi2SAA==", "license": "SEE LICENSE IN README.md", "engines": { "node": ">=18.0.0" }, "optionalDependencies": { - "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.160", - "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.160", - "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.160", - "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.160", - "@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.160", - "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.160", - "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.160", - "@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.160" + "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.162", + "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.162", + "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.162", + "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.162", + "@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.162", + "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.162", + "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.162", + "@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.162" }, "peerDependencies": { "@anthropic-ai/sdk": ">=0.93.0", @@ -65,9 +65,9 @@ } }, "node_modules/@anthropic-ai/claude-agent-sdk-darwin-arm64": { - "version": "0.3.160", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.160.tgz", - "integrity": "sha512-lGd7aySWuFO9MmxXj8plnRobUPVSXztk+EYdews2sDiC28483SLeJ4Ft086GYCaCb/ILeYAsK6aKo25R7Gl9QA==", + "version": "0.3.162", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.162.tgz", + "integrity": "sha512-hafbfEtDeYko1rYCgIBAQbYnFXzd/hHf3IcoaD8mmlCQOAQhKA8gT/RPdLuuzQHdOjEgqDGTNOU+IjwL+msYcw==", "cpu": [ "arm64" ], @@ -78,9 +78,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-darwin-x64": { - "version": "0.3.160", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.160.tgz", - "integrity": "sha512-ck9r+zCg8mvOnEMZhwWSsizkuAW1evB+UzRBFqOlTQCij7KJK5Nw2YRTVVdp4knesM7/q82/cGGZgFtAVxq6IQ==", + "version": "0.3.162", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.162.tgz", + "integrity": "sha512-BNg2Mh/4zc2Jsgpq7Mj8+UH8iJ9xzHS/0CmusBMik0H2Bn7Hra5R/f+csAfZOzSflQl4CNQdGOB2bir7d2n8Ww==", "cpu": [ "x64" ], @@ -91,9 +91,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64": { - "version": "0.3.160", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.160.tgz", - "integrity": "sha512-gzC5CGG2jiSoFEfj4MPc4+QkfQbvGKj8pKN6kpH9TLJeexKnoPNzDEGwy0T/+VGGn2zQwOxIl9sh0QBb+v01DA==", + "version": "0.3.162", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.162.tgz", + "integrity": "sha512-zCXYSimaXWQKZASfDJkoKXQr//toYDGIi16wKDh02Rqcr4mqFi9f5SBw/UCyimkGyYkNx3e+bmC+o/tFrLSTWw==", "cpu": [ "arm64" ], @@ -104,9 +104,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64-musl": { - "version": "0.3.160", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.160.tgz", - "integrity": "sha512-17rq9mNH26jndqIYKkCwNoZrThj/0wJosiixuGEGPEFSzjxv4LDNfLp+Rnpt553HvLBpW8a0IWhYyOyruvpsZQ==", + "version": "0.3.162", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.162.tgz", + "integrity": "sha512-Jr4cyfqzb5V2+p/1PynIfGROOI0JNtV3vBMAgckAdtDzpIFk4mV2T8936tsZzgZr04y40YH1HlQpnJDr92I+Fw==", "cpu": [ "arm64" ], @@ -117,9 +117,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-linux-x64": { - "version": "0.3.160", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.160.tgz", - "integrity": "sha512-WvAuHDekEwTr8O192WOVrCpDHO+cH2a1Dgm5DQAobiPMZxD64mQ9Bbo8otlSnuN3ziPGv/RbouyJHf6JFrotMA==", + "version": "0.3.162", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.162.tgz", + "integrity": "sha512-FO2+zDuSTsZ/5MqxIwExLxG0c2auA5wO6iICwZdUOWtboC6yU2AEgp4mzF0jbe1UOP0J27uODFpvz7uRpWipkg==", "cpu": [ "x64" ], @@ -130,9 +130,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-linux-x64-musl": { - "version": "0.3.160", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.160.tgz", - "integrity": "sha512-85FfW+Heaop8U2Xq5LxIZzunVYcua8X3XSmcWgUX3DXFiAPMfXKyKwJOUDJC5PrVqlWduDZA2K30yTl2JvapcQ==", + "version": "0.3.162", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.162.tgz", + "integrity": "sha512-gW9Gpk7W3w3zGFHBDyY8uer/PE6T0pB+emN1aafZAomfseIH1ixJ6ya5Fw2cIS/K0/4oR2pvu4AprlbRBtr45Q==", "cpu": [ "x64" ], @@ -143,9 +143,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-win32-arm64": { - "version": "0.3.160", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.160.tgz", - "integrity": "sha512-CEpNzmazlgAl/hCaY3Hc12QxuMdvG+kOhi0V0Cp706p5zUiHUojX/e0ahqURn4yBfTddLoAlGAq0VzlMriwsgA==", + "version": "0.3.162", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.162.tgz", + "integrity": "sha512-TZQifFDBdhzt1u6wbbpQ2AZcRkhNVYx1iZVfydAbs3L7ZMK264LjRPytBK4n4S413Is1XyLHbGMvEUNA3N+Tng==", "cpu": [ "arm64" ], @@ -156,9 +156,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-win32-x64": { - "version": "0.3.160", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.160.tgz", - "integrity": "sha512-EVjDyTUem5xAjbPfYu/uBAeCK/qrZNjociW9u8wMvwxF1f0mmXTH+CdCVYeQ78RZBTG5SxV+OXAsgRX/OeWxOQ==", + "version": "0.3.162", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.162.tgz", + "integrity": "sha512-8ZYDgNxkGp47xwpcEZ6/qUXd4BByA8hTnNf4fJD6+P1wWi7Ofxc/d5jwXSYwajYvBvbbktQDthmGzjtoK3lXUw==", "cpu": [ "x64" ], diff --git a/package.json b/package.json index 756eeb49..9d88d2bc 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "license": "Apache-2.0", "dependencies": { "@agentclientprotocol/sdk": "0.24.0", - "@anthropic-ai/claude-agent-sdk": "0.3.160", + "@anthropic-ai/claude-agent-sdk": "0.3.162", "zod": "^3.25.0 || ^4.0.0" }, "devDependencies": { diff --git a/src/acp-agent.ts b/src/acp-agent.ts index 7cec37b9..c32851c7 100644 --- a/src/acp-agent.ts +++ b/src/acp-agent.ts @@ -790,6 +790,12 @@ export class ClaudeAcpAgent implements Agent { // forward it to clients as structured `data`, sparing them from // pattern-matching on the human-readable message text. let lastAssistantError: SDKAssistantMessageError | undefined; + // When a streaming classifier refuses a turn, the assistant message carries + // stop_reason "refusal" and structured stop_details. We capture the + // human-readable explanation here so the terminal `result` can surface it + // to the user (the refused assistant message itself usually has no content) + // and report ACP's dedicated `refusal` stop reason. + let lastRefusalExplanation: string | null = null; // Tracks whether we're inside a compaction. The SDK emits the terminal // `status` (compact_result success/failed) twice for a single failed // compaction, and the two messages are indistinguishable — so we report the @@ -976,6 +982,22 @@ export class ClaudeAcpAgent implements Agent { }); break; } + case "commands_changed": { + // Push the full slash-command list after a mid-session change + // (e.g. skills discovered dynamically as the agent works in a + // subdirectory). The client should REPLACE its cached command + // list with this payload: supportedCommands() is captured once + // at initialize and never reflects mid-session changes, so we + // forward message.commands directly rather than re-querying. + await this.client.sessionUpdate({ + sessionId: message.session_id, + update: { + sessionUpdate: "available_commands_update", + availableCommands: getAvailableSlashCommands(message.commands), + }, + }); + break; + } case "hook_started": case "hook_progress": case "hook_response": @@ -1048,6 +1070,26 @@ export class ClaudeAcpAgent implements Agent { break; } + // A refusal can arrive on any result subtype (and may even set + // is_error), so handle it before the subtype switch — otherwise the + // is_error throw below would surface it as an internal error. The + // refused assistant message carries no visible content, so surface + // the classifier's explanation (when available) and report ACP's + // dedicated `refusal` stop reason. + if (message.stop_reason === "refusal" && !isTaskNotification) { + if (lastRefusalExplanation) { + await this.client.sessionUpdate({ + sessionId: params.sessionId, + update: { + sessionUpdate: "agent_message_chunk", + content: { type: "text", text: lastRefusalExplanation }, + }, + }); + } + stopReason = "refusal"; + break; + } + switch (message.subtype) { case "success": { if (message.result.includes("Please run /login")) { @@ -1228,6 +1270,9 @@ export class ClaudeAcpAgent implements Agent { if (message.error) { lastAssistantError = message.error; } + if (message.message.stop_reason === "refusal") { + lastRefusalExplanation = message.message.stop_details?.explanation ?? null; + } } // Strip / markers and render any