Compare commits

...

2 Commits

Author SHA1 Message Date
Ben Brandt
5133e2dec7 Update to claude-agent-sdk 0.1.42 (#160) 2025-11-17 10:24:02 +00:00
Tim Visher
ed035da9d8 Add title field to tool permission requests (#159)
🤖 Co-authored-by: Claude (Anthropic AI Assistant)

## Problem

Tool approval dialogs in ACP clients (like Emacs
[agent-shell](https://github.com/xenodium/agent-shell)) were showing up
empty because the `toolCall` object in `session/request_permission`
requests was missing the `title` field.

This caused dialogs to appear with just buttons and no description of
what tool is being approved, making it impossible for users to make
informed decisions.

## Solution

Add the `title` field to both `requestPermission` calls (lines 553 and
612) by using the existing `toolInfoFromToolUse()` function which
already generates appropriate titles for all tool types.

## Before
```
  ╭─
      ⚠ Tool Permission ⚠
      
      
       Allow (y)   Reject (n)   Always Allow (!)
  ╰─
```

## After
```
  ╭─
      ⚠ Tool Permission ⚠
      
      Write /path/to/file.txt
      
       Allow (y)   Reject (n)   Always Allow (!)
  ╰─
```

## References

- This agent-shell.el issue shows (as has been shown above) what the
expected interaction would look like and what this changeset supports:
https://github.com/xenodium/agent-shell/issues/123
- The ACP protocol already supports the `title` field and agent-shell
displays it correctly when present (confirmed with OpenCode Agent which
does send titles)

## Testing

- **Manual testing**: Confirmed with agent-shell that permission dialogs
now show descriptive titles like "Write /path/to/file.txt"
- **Unit tests**: Added test coverage that verifies the `title` field is
present in permission request structures for multiple tool types (Write,
Bash, Read)
- **Regression testing**: Verified the new test fails when the `title`
field is omitted, confirming it catches the bug this PR fixes
- **Existing tests**: All 61 existing unit tests pass

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
2025-11-17 11:19:22 +01:00
5 changed files with 139 additions and 74 deletions

View File

@@ -1,5 +1,10 @@
# Changelog
## 0.10.4
- Fix tool call titles not appearing during approval in some cases
- Update to @anthropic-ai/claude-agent-sdk@v0.1.42
## 0.10.3
- Fix for experimental terminal auth support

133
package-lock.json generated
View File

@@ -1,18 +1,18 @@
{
"name": "@zed-industries/claude-code-acp",
"version": "0.10.3",
"version": "0.10.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@zed-industries/claude-code-acp",
"version": "0.10.3",
"version": "0.10.4",
"license": "Apache-2.0",
"dependencies": {
"@agentclientprotocol/sdk": "0.5.1",
"@anthropic-ai/claude-agent-sdk": "0.1.37",
"@anthropic-ai/claude-code": "2.0.37",
"@modelcontextprotocol/sdk": "1.21.1",
"@anthropic-ai/claude-agent-sdk": "0.1.42",
"@anthropic-ai/claude-code": "2.0.42",
"@modelcontextprotocol/sdk": "1.22.0",
"diff": "8.0.2",
"uuid": "13.0.0"
},
@@ -20,19 +20,19 @@
"claude-code-acp": "dist/index.js"
},
"devDependencies": {
"@anthropic-ai/sdk": "0.68.0",
"@anthropic-ai/sdk": "0.69.0",
"@types/node": "24.10.1",
"@types/uuid": "10.0.0",
"@typescript-eslint/eslint-plugin": "8.46.4",
"@typescript-eslint/parser": "8.46.4",
"@vitest/ui": "4.0.8",
"@vitest/ui": "4.0.9",
"eslint": "9.39.1",
"eslint-config-prettier": "10.1.8",
"globals": "16.5.0",
"prettier": "3.6.2",
"ts-node": "10.9.2",
"typescript": "5.9.3",
"vitest": "4.0.8"
"vitest": "4.0.9"
}
},
"node_modules/@agentclientprotocol/sdk": {
@@ -45,9 +45,9 @@
}
},
"node_modules/@anthropic-ai/claude-agent-sdk": {
"version": "0.1.37",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.1.37.tgz",
"integrity": "sha512-LMfqMIPLTz0vRhpcO7hpPJ5L6Bg24y5/PaqZvwAUNZ/GR3OAl7xmJR7IryIR6m8Pyd/6Hs2yBU8j86Os+wHFQQ==",
"version": "0.1.42",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.1.42.tgz",
"integrity": "sha512-snz2CTHKxkk4rqgvi2D4oMgITXpgmghWC4XvFXcuYjSxgLCBmvDW9TjBk6MTv0apC5fwW4kLE4ydamkBs+BdPg==",
"license": "SEE LICENSE IN README.md",
"engines": {
"node": ">=18.0.0"
@@ -65,9 +65,9 @@
}
},
"node_modules/@anthropic-ai/claude-code": {
"version": "2.0.37",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-code/-/claude-code-2.0.37.tgz",
"integrity": "sha512-8d4qRGY9/LGqEbYNfno00wg0tprDC0dPNBBZHk8CVvl5vlB8PX2HVwlI6kok3ZNFBMDL+Hqjbp26xXC1hTL6Hw==",
"version": "2.0.42",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-code/-/claude-code-2.0.42.tgz",
"integrity": "sha512-poK4Z+mviehjjCIQe+KTU40kSTgnUitxROg79rAwN3L3A7FyvQE8v6jdakvtxYb452/8qBSf+FbvImHvuXn0rw==",
"license": "SEE LICENSE IN README.md",
"bin": {
"claude": "cli.js"
@@ -85,9 +85,9 @@
}
},
"node_modules/@anthropic-ai/sdk": {
"version": "0.68.0",
"resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.68.0.tgz",
"integrity": "sha512-SMYAmbbiprG8k1EjEPMTwaTqssDT7Ae+jxcR5kWXiqTlbwMR2AthXtscEVWOHkRfyAV5+y3PFYTJRNa3OJWIEw==",
"version": "0.69.0",
"resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.69.0.tgz",
"integrity": "sha512-L92d2q47BSq+7slUqHBL1d2DwloulZotYGCTDt9AYRtPmYF+iK6rnwq9JaZwPPJgk+LenbcbQ/nj6gfaDFsl9w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1086,9 +1086,9 @@
}
},
"node_modules/@modelcontextprotocol/sdk": {
"version": "1.21.1",
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.21.1.tgz",
"integrity": "sha512-UyLFcJLDvUuZbGnaQqXFT32CpPpGj7VS19roLut6gkQVhb439xUzYWbsUvdI3ZPL+2hnFosuugtYWE0Mcs1rmQ==",
"version": "1.22.0",
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.22.0.tgz",
"integrity": "sha512-VUpl106XVTCpDmTBil2ehgJZjhyLY2QZikzF8NvTXtLRF1CvO5iEE2UNZdVIUer35vFOwMKYeUGbjJtvPWan3g==",
"license": "MIT",
"dependencies": {
"ajv": "^8.17.1",
@@ -1792,16 +1792,16 @@
}
},
"node_modules/@vitest/expect": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.8.tgz",
"integrity": "sha512-Rv0eabdP/xjAHQGr8cjBm+NnLHNoL268lMDK85w2aAGLFoVKLd8QGnVon5lLtkXQCoYaNL0wg04EGnyKkkKhPA==",
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.9.tgz",
"integrity": "sha512-C2vyXf5/Jfj1vl4DQYxjib3jzyuswMi/KHHVN2z+H4v16hdJ7jMZ0OGe3uOVIt6LyJsAofDdaJNIFEpQcrSTFw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@standard-schema/spec": "^1.0.0",
"@types/chai": "^5.2.2",
"@vitest/spy": "4.0.8",
"@vitest/utils": "4.0.8",
"@vitest/spy": "4.0.9",
"@vitest/utils": "4.0.9",
"chai": "^6.2.0",
"tinyrainbow": "^3.0.3"
},
@@ -1810,13 +1810,13 @@
}
},
"node_modules/@vitest/mocker": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.8.tgz",
"integrity": "sha512-9FRM3MZCedXH3+pIh+ME5Up2NBBHDq0wqwhOKkN4VnvCiKbVxddqH9mSGPZeawjd12pCOGnl+lo/ZGHt0/dQSg==",
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.9.tgz",
"integrity": "sha512-PUyaowQFHW+9FKb4dsvvBM4o025rWMlEDXdWRxIOilGaHREYTi5Q2Rt9VCgXgPy/hHZu1LeuXtrA/GdzOatP2g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/spy": "4.0.8",
"@vitest/spy": "4.0.9",
"estree-walker": "^3.0.3",
"magic-string": "^0.30.21"
},
@@ -1837,9 +1837,9 @@
}
},
"node_modules/@vitest/pretty-format": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.8.tgz",
"integrity": "sha512-qRrjdRkINi9DaZHAimV+8ia9Gq6LeGz2CgIEmMLz3sBDYV53EsnLZbJMR1q84z1HZCMsf7s0orDgZn7ScXsZKg==",
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.9.tgz",
"integrity": "sha512-Hor0IBTwEi/uZqB7pvGepyElaM8J75pYjrrqbC8ZYMB9/4n5QA63KC15xhT+sqHpdGWfdnPo96E8lQUxs2YzSQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1850,13 +1850,13 @@
}
},
"node_modules/@vitest/runner": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.8.tgz",
"integrity": "sha512-mdY8Sf1gsM8hKJUQfiPT3pn1n8RF4QBcJYFslgWh41JTfrK1cbqY8whpGCFzBl45LN028g0njLCYm0d7XxSaQQ==",
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.9.tgz",
"integrity": "sha512-aF77tsXdEvIJRkj9uJZnHtovsVIx22Ambft9HudC+XuG/on1NY/bf5dlDti1N35eJT+QZLb4RF/5dTIG18s98w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/utils": "4.0.8",
"@vitest/utils": "4.0.9",
"pathe": "^2.0.3"
},
"funding": {
@@ -1864,13 +1864,13 @@
}
},
"node_modules/@vitest/snapshot": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.8.tgz",
"integrity": "sha512-Nar9OTU03KGiubrIOFhcfHg8FYaRaNT+bh5VUlNz8stFhCZPNrJvmZkhsr1jtaYvuefYFwK2Hwrq026u4uPWCw==",
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.9.tgz",
"integrity": "sha512-r1qR4oYstPbnOjg0Vgd3E8ADJbi4ditCzqr+Z9foUrRhIy778BleNyZMeAJ2EjV+r4ASAaDsdciC9ryMy8xMMg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/pretty-format": "4.0.8",
"@vitest/pretty-format": "4.0.9",
"magic-string": "^0.30.21",
"pathe": "^2.0.3"
},
@@ -1879,9 +1879,9 @@
}
},
"node_modules/@vitest/spy": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.8.tgz",
"integrity": "sha512-nvGVqUunyCgZH7kmo+Ord4WgZ7lN0sOULYXUOYuHr55dvg9YvMz3izfB189Pgp28w0vWFbEEfNc/c3VTrqrXeA==",
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.9.tgz",
"integrity": "sha512-J9Ttsq0hDXmxmT8CUOWUr1cqqAj2FJRGTdyEjSR+NjoOGKEqkEWj+09yC0HhI8t1W6t4Ctqawl1onHgipJve1A==",
"dev": true,
"license": "MIT",
"funding": {
@@ -1889,14 +1889,14 @@
}
},
"node_modules/@vitest/ui": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.0.8.tgz",
"integrity": "sha512-F9jI5rSstNknPlTlPN2gcc4gpbaagowuRzw/OJzl368dvPun668Q182S8Q8P9PITgGCl5LAKXpzuue106eM4wA==",
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.0.9.tgz",
"integrity": "sha512-6HV2HHl9aRJ09TlYj/WAQxaa797Ezb5u0LpgabthlASAUAWKgw/W1DSPX7t848mMZmIUvzZgnUHGIylAoYHP0w==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@vitest/utils": "4.0.8",
"@vitest/utils": "4.0.9",
"fflate": "^0.8.2",
"flatted": "^3.3.3",
"pathe": "^2.0.3",
@@ -1908,17 +1908,17 @@
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
"vitest": "4.0.8"
"vitest": "4.0.9"
}
},
"node_modules/@vitest/utils": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.8.tgz",
"integrity": "sha512-pdk2phO5NDvEFfUTxcTP8RFYjVj/kfLSPIN5ebP2Mu9kcIMeAQTbknqcFEyBcC4z2pJlJI9aS5UQjcYfhmKAow==",
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.9.tgz",
"integrity": "sha512-cEol6ygTzY4rUPvNZM19sDf7zGa35IYTm9wfzkHoT/f5jX10IOY7QleWSOh5T0e3I3WVozwK5Asom79qW8DiuQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/pretty-format": "4.0.8",
"@vitest/pretty-format": "4.0.9",
"tinyrainbow": "^3.0.3"
},
"funding": {
@@ -4497,7 +4497,6 @@
"integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.5.0",
@@ -4600,20 +4599,20 @@
}
},
"node_modules/vitest": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.8.tgz",
"integrity": "sha512-urzu3NCEV0Qa0Y2PwvBtRgmNtxhj5t5ULw7cuKhIHh3OrkKTLlut0lnBOv9qe5OvbkMH2g38G7KPDCTpIytBVg==",
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.9.tgz",
"integrity": "sha512-E0Ja2AX4th+CG33yAFRC+d1wFx2pzU5r6HtG6LiPSE04flaE0qB6YyjSw9ZcpJAtVPfsvZGtJlKWZpuW7EHRxg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@vitest/expect": "4.0.8",
"@vitest/mocker": "4.0.8",
"@vitest/pretty-format": "4.0.8",
"@vitest/runner": "4.0.8",
"@vitest/snapshot": "4.0.8",
"@vitest/spy": "4.0.8",
"@vitest/utils": "4.0.8",
"@vitest/expect": "4.0.9",
"@vitest/mocker": "4.0.9",
"@vitest/pretty-format": "4.0.9",
"@vitest/runner": "4.0.9",
"@vitest/snapshot": "4.0.9",
"@vitest/spy": "4.0.9",
"@vitest/utils": "4.0.9",
"debug": "^4.4.3",
"es-module-lexer": "^1.7.0",
"expect-type": "^1.2.2",
@@ -4641,10 +4640,10 @@
"@edge-runtime/vm": "*",
"@types/debug": "^4.1.12",
"@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
"@vitest/browser-playwright": "4.0.8",
"@vitest/browser-preview": "4.0.8",
"@vitest/browser-webdriverio": "4.0.8",
"@vitest/ui": "4.0.8",
"@vitest/browser-playwright": "4.0.9",
"@vitest/browser-preview": "4.0.9",
"@vitest/browser-webdriverio": "4.0.9",
"@vitest/ui": "4.0.9",
"happy-dom": "*",
"jsdom": "*"
},

View File

@@ -3,7 +3,7 @@
"publishConfig": {
"access": "public"
},
"version": "0.10.3",
"version": "0.10.4",
"description": "An ACP-compatible coding agent powered by the Claude Code SDK (TypeScript)",
"main": "dist/lib.js",
"bin": {
@@ -52,25 +52,25 @@
"license": "Apache-2.0",
"dependencies": {
"@agentclientprotocol/sdk": "0.5.1",
"@anthropic-ai/claude-agent-sdk": "0.1.37",
"@anthropic-ai/claude-code": "2.0.37",
"@modelcontextprotocol/sdk": "1.21.1",
"@anthropic-ai/claude-agent-sdk": "0.1.42",
"@anthropic-ai/claude-code": "2.0.42",
"@modelcontextprotocol/sdk": "1.22.0",
"diff": "8.0.2",
"uuid": "13.0.0"
},
"devDependencies": {
"@anthropic-ai/sdk": "0.68.0",
"@anthropic-ai/sdk": "0.69.0",
"@types/node": "24.10.1",
"@types/uuid": "10.0.0",
"@typescript-eslint/eslint-plugin": "8.46.4",
"@typescript-eslint/parser": "8.46.4",
"@vitest/ui": "4.0.8",
"@vitest/ui": "4.0.9",
"eslint": "9.39.1",
"eslint-config-prettier": "10.1.8",
"globals": "16.5.0",
"prettier": "3.6.2",
"ts-node": "10.9.2",
"typescript": "5.9.3",
"vitest": "4.0.8"
"vitest": "4.0.9"
}
}

View File

@@ -551,6 +551,8 @@ export class ClaudeAcpAgent implements Agent {
toolCall: {
toolCallId: toolUseID,
rawInput: toolInput,
title: toolInfoFromToolUse({ name: toolName, input: toolInput }, this.fileContentCache)
.title,
},
});
@@ -610,6 +612,8 @@ export class ClaudeAcpAgent implements Agent {
toolCall: {
toolCallId: toolUseID,
rawInput: toolInput,
title: toolInfoFromToolUse({ name: toolName, input: toolInput }, this.fileContentCache)
.title,
},
});
if (

View File

@@ -841,3 +841,60 @@ describe("SDK behavior", () => {
10000,
);
});
describe("permission requests", () => {
it("should include title field in tool permission request structure", () => {
// Test various tool types to ensure title is correctly generated
const testCases = [
{
toolUse: {
type: "tool_use" as const,
id: "test-1",
name: "Write",
input: { file_path: "/test/file.txt", content: "test" },
},
expectedTitlePart: "/test/file.txt",
},
{
toolUse: {
type: "tool_use" as const,
id: "test-2",
name: "Bash",
input: { command: "ls -la", description: "List files" },
},
expectedTitlePart: "`ls -la`",
},
{
toolUse: {
type: "tool_use" as const,
id: "test-3",
name: "mcp__acp__Read",
input: { file_path: "/test/data.json" },
},
expectedTitlePart: "/test/data.json",
},
];
for (const testCase of testCases) {
// Get the tool info that would be used in requestPermission
const toolInfo = toolInfoFromToolUse(testCase.toolUse, {});
// Verify toolInfo has a title
expect(toolInfo.title).toBeDefined();
expect(toolInfo.title).toContain(testCase.expectedTitlePart);
// Verify the structure that our fix creates for requestPermission
const requestStructure = {
toolCall: {
toolCallId: testCase.toolUse.id,
rawInput: testCase.toolUse.input,
title: toolInfo.title, // This is what commit 1785d86 adds
},
};
// Ensure the title field is present and populated
expect(requestStructure.toolCall.title).toBeDefined();
expect(requestStructure.toolCall.title).toContain(testCase.expectedTitlePart);
}
});
});