mirror of
https://github.com/agentclientprotocol/claude-agent-acp.git
synced 2026-11-03 16:01:46 +00:00
Compare commits
1 Commits
v0.53.0
...
sd-can-use
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
025038e8b5 |
@@ -1,3 +1,3 @@
|
||||
{
|
||||
".": "0.53.0"
|
||||
".": "0.51.0"
|
||||
}
|
||||
|
||||
23
CHANGELOG.md
23
CHANGELOG.md
@@ -1,28 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## [0.53.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.52.0...v0.53.0) (2026-06-29)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add ACP logout support ([#816](https://github.com/agentclientprotocol/claude-agent-acp/issues/816)) ([0a0468c](https://github.com/agentclientprotocol/claude-agent-acp/commit/0a0468c72092b7ceccc551b4353dd92f9c22cbb8))
|
||||
* **deps:** bump @anthropic-ai/claude-agent-sdk to 0.3.195 ([#818](https://github.com/agentclientprotocol/claude-agent-acp/issues/818)) ([5dd8746](https://github.com/agentclientprotocol/claude-agent-acp/commit/5dd87462376597eb7b695200f4c716af46e764d6))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Emit tool_call before permission request ([#820](https://github.com/agentclientprotocol/claude-agent-acp/issues/820)) ([c95fc88](https://github.com/agentclientprotocol/claude-agent-acp/commit/c95fc884e82e5a297d1b17b8965690260867835b))
|
||||
|
||||
## [0.52.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.51.0...v0.52.0) (2026-06-25)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add version flag handling ([#813](https://github.com/agentclientprotocol/claude-agent-acp/issues/813)) ([9616bda](https://github.com/agentclientprotocol/claude-agent-acp/commit/9616bdac47505e4a14c36d667fcffc9ae97e1f2a)), closes [#809](https://github.com/agentclientprotocol/claude-agent-acp/issues/809)
|
||||
* **deps-dev:** bump expect-type from 1.3.0 to 1.4.0 in the minor group ([#814](https://github.com/agentclientprotocol/claude-agent-acp/issues/814)) ([61272ac](https://github.com/agentclientprotocol/claude-agent-acp/commit/61272acb30dcafaa2455d334b11ce2ad97339707))
|
||||
* **deps:** Update @anthropic-ai/claude-agent-sdk to 0.3.191 ([#810](https://github.com/agentclientprotocol/claude-agent-acp/issues/810)) ([228f02e](https://github.com/agentclientprotocol/claude-agent-acp/commit/228f02ecfb23be16e59e121c6b42c0f2b2f40a4e))
|
||||
* Push session title updates at turn end ([#812](https://github.com/agentclientprotocol/claude-agent-acp/issues/812)) ([1fe7ec0](https://github.com/agentclientprotocol/claude-agent-acp/commit/1fe7ec09a3a7bcb7501231dae4b0ffe6ef9b70a4))
|
||||
|
||||
## [0.51.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.50.0...v0.51.0) (2026-06-24)
|
||||
|
||||
|
||||
|
||||
126
package-lock.json
generated
126
package-lock.json
generated
@@ -1,16 +1,16 @@
|
||||
{
|
||||
"name": "@agentclientprotocol/claude-agent-acp",
|
||||
"version": "0.53.0",
|
||||
"version": "0.51.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@agentclientprotocol/claude-agent-acp",
|
||||
"version": "0.53.0",
|
||||
"version": "0.51.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "1.0.0",
|
||||
"@anthropic-ai/claude-agent-sdk": "0.3.195",
|
||||
"@anthropic-ai/claude-agent-sdk": "0.3.191",
|
||||
"zod": "^3.25.0 || ^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
@@ -23,10 +23,10 @@
|
||||
"@types/node": "26.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "8.62.0",
|
||||
"@typescript-eslint/parser": "8.62.0",
|
||||
"eslint": "10.6.0",
|
||||
"eslint": "10.5.0",
|
||||
"eslint-config-prettier": "10.1.8",
|
||||
"globals": "17.7.0",
|
||||
"prettier": "3.9.1",
|
||||
"prettier": "3.8.4",
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "6.0.3",
|
||||
"vitest": "4.1.9"
|
||||
@@ -45,22 +45,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk": {
|
||||
"version": "0.3.195",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.195.tgz",
|
||||
"integrity": "sha512-FVmXu9pvOMbuBKWrF8YsYQdQ/upOpv5rS8lFAnFO5jbyXT/2hN7kEPd2vd2GJpaMvNcO/KptyQUK5AxjjTz3+w==",
|
||||
"version": "0.3.191",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.191.tgz",
|
||||
"integrity": "sha512-DM9oYbt+WAb/CiD8l/CO6akqPKfyZ4sL1e+lP2O2eS9IR0f58sf90xWeji7V+/zfnIp+jyH3hJ3V6r7odCdB8g==",
|
||||
"license": "SEE LICENSE IN README.md",
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.195",
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.195",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.195",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.195",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.195",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.195",
|
||||
"@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.195",
|
||||
"@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.195"
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.191",
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.191",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.191",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.191",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.191",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.191",
|
||||
"@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.191",
|
||||
"@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.191"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@anthropic-ai/sdk": ">=0.93.0",
|
||||
@@ -69,9 +69,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-darwin-arm64": {
|
||||
"version": "0.3.195",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.195.tgz",
|
||||
"integrity": "sha512-WIMM/8HRCLsTDHFTIwQvvE8WCA/oaMJtdQxsP7iNyfzIGwXbuOyU95V8vYIhZfaO2yaSpbBRncunq4CtR5H4ng==",
|
||||
"version": "0.3.191",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.191.tgz",
|
||||
"integrity": "sha512-rmpi5ONfuuw7oXdzyr9QJB5sK9Rr1MmdOUsiGutVFpcAqqSXpVYLmMvfaarmXvItgfwgGprzrGDfcAeAQHSUGw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -82,9 +82,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-darwin-x64": {
|
||||
"version": "0.3.195",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.195.tgz",
|
||||
"integrity": "sha512-RY7DB+4LXosE0MJ+XELmakfPrDN1YX4lkk9CTDm28jGCVcESRz9kAEqbyaiC48dZcmN9V1NCLutzINGdcr1TBg==",
|
||||
"version": "0.3.191",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.191.tgz",
|
||||
"integrity": "sha512-b/4pfSFTC2gkc0RoGSJ5B3DWo77qZFMi2hRlx8kwaRDiefeIOnbJu42gDBrs4F8nSJJug77HM9AsG6XHXvt5Fg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -95,9 +95,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64": {
|
||||
"version": "0.3.195",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.195.tgz",
|
||||
"integrity": "sha512-JuIq5Fnz/F1snl0aqi1gcuRZqPWoPNrL9dJ0DuievCxKkO8hnEz/Mmn5Zos7x1X8HE//ZnEvmQXoEQEZXonJew==",
|
||||
"version": "0.3.191",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.191.tgz",
|
||||
"integrity": "sha512-vm526e3InpItmh+Ua8a49eGTpFJvZTYLURX9ppKrtFb+Wo7rOgoXmK/Hzf/crEU7h72xtaD7PIOXm4P+psxO3w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -111,9 +111,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64-musl": {
|
||||
"version": "0.3.195",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.195.tgz",
|
||||
"integrity": "sha512-ZmyBA/AFzhgutcxb7dbhCm6GTjJytwNYXTxJoKE2B3A409WCYccjMqeji6vCMNxyyfylglGo5D8dVMIxW9aoug==",
|
||||
"version": "0.3.191",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.191.tgz",
|
||||
"integrity": "sha512-+Gg7lKwYDr8uiA37EBMYI63if+YGdbrFt2tcRFBB4IhdVf5r1IkKwQ8rEYV6Y0nqGTKoXLh3bRc5QWOe5JwgbQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -127,9 +127,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-linux-x64": {
|
||||
"version": "0.3.195",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.195.tgz",
|
||||
"integrity": "sha512-s1lNi1cL93luoqsItH+fNO4KpIhdkvnVhWGGQUQ/8ftwa2gfmcIQnOg1hG8Ks+KzeD3UUQ8L9YEVHVADnFI/9A==",
|
||||
"version": "0.3.191",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.191.tgz",
|
||||
"integrity": "sha512-wj43/l0Behb4UWYt6Lomq2W7jhmuU7ARsEP0el6gLGCR348PDeqLGcB4dMOcYMGRZv8TYKfnhDwJnX4pJBT1vQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -143,9 +143,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-linux-x64-musl": {
|
||||
"version": "0.3.195",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.195.tgz",
|
||||
"integrity": "sha512-nf8Q/LauB+ZOC6QDjxNhbsvwUtYjKYnaWJLTYFwhkmsLujePnety1AtT/1ubaUoq5AM1j297DhMlYTasa79OUA==",
|
||||
"version": "0.3.191",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.191.tgz",
|
||||
"integrity": "sha512-tUwwIXpPBrzJge3E4SQJQX8PuC+lxPb5xQBmyLsWxyjttlgpFpTG/jWPVQO5qH0HgYaEJS5JALHFiYcXwVrEkg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -159,9 +159,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-win32-arm64": {
|
||||
"version": "0.3.195",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.195.tgz",
|
||||
"integrity": "sha512-hbkDE+xPIZzRWm+D+BKrH9uJH6USIZdDIlsyrIlGi3JFHoieYoA1vdUNyldSS9+F3ZqQtfPjr2Qy08IVB6akYA==",
|
||||
"version": "0.3.191",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.191.tgz",
|
||||
"integrity": "sha512-J8aTOi7n6AM7tHFlKt6hlJ4rue0TbY2glqcI2r6K3iK7Fmzvt9zfg00+l8fWKOais4K+8jqJCC4WSCUlz6+tKQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -172,9 +172,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-win32-x64": {
|
||||
"version": "0.3.195",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.195.tgz",
|
||||
"integrity": "sha512-av0piEB3X1Dzhpr8A+DqHVZ9y8s1jpn8enzwX0TKKUPBn5IqLTWC7wD6v66aoUgu4f+g4ThZirmDZA6shyPEZQ==",
|
||||
"version": "0.3.191",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.191.tgz",
|
||||
"integrity": "sha512-1rYlPuM9PS37IPCE/sAG7A7oCrWnIb0J7EY6eQBdImdXHEjwBngZ3E8KbtEM79fUTA3C810LqrnFpjOmNExLMA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1441,9 +1441,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "5.0.7",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
|
||||
"integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
|
||||
"integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -1701,9 +1701,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/es-module-lexer": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.2.0.tgz",
|
||||
"integrity": "sha512-3lGxdTXCLfe1MYfTz1y2ksAAUM4NAOP6rPEjxGJVKO7TZ5+tvHCaQWGpC4Y3IXvW3ece0Cz1cIP4FWBxOnGCTQ==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz",
|
||||
"integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@@ -1741,9 +1741,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "10.6.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-10.6.0.tgz",
|
||||
"integrity": "sha512-6lVbcqSodALYo+4ELD0heG6lFiFxnLMuLkiMi2qV8LMp54N8tE8FT1GMH+ev4Ti00nFjNze2+Su6DsV5OQW3Dg==",
|
||||
"version": "10.5.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-10.5.0.tgz",
|
||||
"integrity": "sha512-1y+7C+vi12bUK1IpZeaV3gsH9fHLBmPvYmPx42pvT/E9yG0IC8g3PUZZgp0+JLJl7ZDK0flc2gc+Aw9dpCvIsQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
@@ -2015,9 +2015,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/expect-type": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz",
|
||||
"integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==",
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
|
||||
"integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
@@ -3188,9 +3188,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.16",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz",
|
||||
"integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==",
|
||||
"version": "8.5.15",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
|
||||
"integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -3227,9 +3227,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.9.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.1.tgz",
|
||||
"integrity": "sha512-ppiDo2CSwexck1eyZUwJHg/N3nf1+6IRCv7W/VJ5vaLnVCmB7+3CdRfMwoCHBBX6xTrREDTksZ4OZl5SSf4zXA==",
|
||||
"version": "3.8.4",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.4.tgz",
|
||||
"integrity": "sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
@@ -3284,17 +3284,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/range-parser": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz",
|
||||
"integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==",
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body": {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"version": "0.53.0",
|
||||
"version": "0.51.0",
|
||||
"description": "An ACP-compatible coding agent powered by the Claude Agent SDK (TypeScript)",
|
||||
"main": "dist/lib.js",
|
||||
"types": "dist/lib.d.ts",
|
||||
@@ -64,7 +64,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "1.0.0",
|
||||
"@anthropic-ai/claude-agent-sdk": "0.3.195",
|
||||
"@anthropic-ai/claude-agent-sdk": "0.3.191",
|
||||
"zod": "^3.25.0 || ^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -74,10 +74,10 @@
|
||||
"@types/node": "26.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "8.62.0",
|
||||
"@typescript-eslint/parser": "8.62.0",
|
||||
"eslint": "10.6.0",
|
||||
"eslint": "10.5.0",
|
||||
"eslint-config-prettier": "10.1.8",
|
||||
"globals": "17.7.0",
|
||||
"prettier": "3.9.1",
|
||||
"prettier": "3.8.4",
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "6.0.3",
|
||||
"vitest": "4.1.9"
|
||||
|
||||
270
src/acp-agent.ts
270
src/acp-agent.ts
@@ -17,7 +17,6 @@ import {
|
||||
ListSessionsResponse,
|
||||
LoadSessionRequest,
|
||||
LoadSessionResponse,
|
||||
LogoutRequest,
|
||||
methods,
|
||||
ndJsonStream,
|
||||
NewSessionRequest,
|
||||
@@ -75,13 +74,11 @@ import {
|
||||
} from "@anthropic-ai/claude-agent-sdk";
|
||||
import { ContentBlockParam } from "@anthropic-ai/sdk/resources";
|
||||
import { BetaContentBlock, BetaRawContentBlockDelta } from "@anthropic-ai/sdk/resources/beta.mjs";
|
||||
import { execFile } from "node:child_process";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import type { Stats } from "node:fs";
|
||||
import * as fs from "node:fs/promises";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { promisify } from "node:util";
|
||||
import packageJson from "../package.json" with { type: "json" };
|
||||
import {
|
||||
applyAskElicitationResponse,
|
||||
@@ -112,8 +109,6 @@ import { nodeToWebReadable, nodeToWebWritable, Pushable, unreachable } from "./u
|
||||
export const CLAUDE_CONFIG_DIR =
|
||||
process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), ".claude");
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
|
||||
const MAX_TITLE_LENGTH = 256;
|
||||
|
||||
function sanitizeTitle(text: string): string {
|
||||
@@ -280,14 +275,11 @@ type Session = {
|
||||
* `tool_result` time so a long-running session doesn't accumulate every
|
||||
* tool call for its whole lifetime. */
|
||||
toolUseCache: ToolUseCache;
|
||||
/** Tracks which tool_use ids we've already emitted a `tool_call` for, so the
|
||||
* second source to encounter a tool call sends a `tool_call_update` instead
|
||||
* of a duplicate `tool_call`. The SDK can invoke `canUseTool` (→ a permission
|
||||
* request, which emits the tool_call eagerly so the client has it before
|
||||
* being asked to approve it) either before or after the assistant message's
|
||||
* tool_use block streams; this set makes the two paths converge regardless of
|
||||
* order. Pruned at `tool_result` time alongside `toolUseCache`. */
|
||||
emittedToolCalls: Set<string>;
|
||||
/**
|
||||
* This lets canUseTool wait for the first tool call message from the SDK
|
||||
* to ensure it emits a tool_call_update only after the first tool_call message.
|
||||
*/
|
||||
waitingForToolCall: WaitingForToolCall;
|
||||
/** Maps the ACP `messageId` we expose to clients (see `messageIdForGrouping`)
|
||||
* to the SDK message uuid that the Agent SDK's rewind/resume APIs key on
|
||||
* (`Query.rewindFiles` takes a user-message uuid; `resumeSessionAt` takes an
|
||||
@@ -402,6 +394,8 @@ export type ToolUpdateMeta = {
|
||||
};
|
||||
};
|
||||
|
||||
export type WaitingForToolCall = Map<string, () => void>;
|
||||
|
||||
export type ToolUseCache = {
|
||||
[key: string]: {
|
||||
type: "tool_use" | "server_tool_use" | "mcp_tool_use";
|
||||
@@ -455,7 +449,8 @@ function isMuslLibc(): boolean {
|
||||
// process.report.getReport().header.glibcVersionRuntime is populated when
|
||||
// Node is dynamically linked against glibc, and absent on musl.
|
||||
const report = process.report?.getReport() as
|
||||
{ header?: { glibcVersionRuntime?: string } } | undefined;
|
||||
| { header?: { glibcVersionRuntime?: string } }
|
||||
| undefined;
|
||||
return !report?.header?.glibcVersionRuntime;
|
||||
}
|
||||
|
||||
@@ -867,9 +862,6 @@ export class ClaudeAcpAgent {
|
||||
http: true,
|
||||
sse: true,
|
||||
},
|
||||
auth: {
|
||||
logout: {},
|
||||
},
|
||||
loadSession: true,
|
||||
sessionCapabilities: {
|
||||
additionalDirectories: {},
|
||||
@@ -1007,30 +999,6 @@ export class ClaudeAcpAgent {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
async logout(_params: LogoutRequest): Promise<void> {
|
||||
// Clear in-memory gateway credentials supplied via `authenticate`. The
|
||||
// gateway method never touches the on-disk credential store, so dropping
|
||||
// this reference is the whole logout for that path.
|
||||
this.gatewayAuthRequest = undefined;
|
||||
|
||||
// For the Claude/Console login methods the credentials live in the native
|
||||
// CLI's store (keychain or config dir), which only the binary can clear.
|
||||
// `claude auth logout` is non-interactive and idempotent.
|
||||
const cliPath = await claudeCliPath();
|
||||
try {
|
||||
await execFileAsync(cliPath, ["auth", "logout"]);
|
||||
} catch (error) {
|
||||
const stderr =
|
||||
typeof error === "object" && error && "stderr" in error
|
||||
? String((error as { stderr: unknown }).stderr).trim()
|
||||
: undefined;
|
||||
throw RequestError.internalError(
|
||||
{ stderr: stderr || undefined },
|
||||
`claude auth logout failed: ${error instanceof Error ? error.message : String(error)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async prompt(params: PromptRequest): Promise<PromptResponse> {
|
||||
const session = this.sessions[params.sessionId];
|
||||
if (!session) {
|
||||
@@ -1605,7 +1573,6 @@ export class ClaudeAcpAgent {
|
||||
case "api_retry":
|
||||
case "thinking_tokens":
|
||||
case "model_refusal_fallback":
|
||||
case "model_refusal_no_fallback":
|
||||
// Todo: process via status api: https://docs.claude.com/en/docs/claude-code/hooks#hook-output
|
||||
break;
|
||||
default:
|
||||
@@ -1729,6 +1696,7 @@ export class ClaudeAcpAgent {
|
||||
"assistant",
|
||||
params.sessionId,
|
||||
session.toolUseCache,
|
||||
session.waitingForToolCall,
|
||||
this.client,
|
||||
this.logger,
|
||||
)) {
|
||||
@@ -1899,13 +1867,13 @@ export class ClaudeAcpAgent {
|
||||
message,
|
||||
params.sessionId,
|
||||
session.toolUseCache,
|
||||
session.waitingForToolCall,
|
||||
this.client,
|
||||
this.logger,
|
||||
{
|
||||
clientCapabilities: this.clientCapabilities,
|
||||
cwd: session.cwd,
|
||||
taskState: session.taskState,
|
||||
emittedToolCalls: session.emittedToolCalls,
|
||||
messageId: currentStreamMessageId,
|
||||
},
|
||||
)) {
|
||||
@@ -2000,6 +1968,7 @@ export class ClaudeAcpAgent {
|
||||
message.message.role,
|
||||
params.sessionId,
|
||||
session.toolUseCache,
|
||||
session.waitingForToolCall,
|
||||
this.client,
|
||||
this.logger,
|
||||
{
|
||||
@@ -2130,6 +2099,7 @@ export class ClaudeAcpAgent {
|
||||
message.message.role,
|
||||
params.sessionId,
|
||||
session.toolUseCache,
|
||||
session.waitingForToolCall,
|
||||
this.client,
|
||||
this.logger,
|
||||
{
|
||||
@@ -2137,7 +2107,6 @@ export class ClaudeAcpAgent {
|
||||
parentToolUseId: message.parent_tool_use_id,
|
||||
cwd: session.cwd,
|
||||
taskState: session.taskState,
|
||||
emittedToolCalls: session.emittedToolCalls,
|
||||
messageId: messageIdForGrouping(message),
|
||||
},
|
||||
)) {
|
||||
@@ -2498,6 +2467,7 @@ export class ClaudeAcpAgent {
|
||||
|
||||
private async replaySessionHistory(sessionId: string): Promise<void> {
|
||||
const toolUseCache: ToolUseCache = {};
|
||||
const waitingForToolCall: WaitingForToolCall = new Map();
|
||||
const messages = await getSessionMessages(sessionId);
|
||||
|
||||
for (const message of messages) {
|
||||
@@ -2526,6 +2496,7 @@ export class ClaudeAcpAgent {
|
||||
message.message.role,
|
||||
sessionId,
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
this.client,
|
||||
this.logger,
|
||||
{
|
||||
@@ -2560,21 +2531,8 @@ export class ClaudeAcpAgent {
|
||||
* longer leaves the `await` hanging. */
|
||||
private async requestPermissionFromClient(
|
||||
params: RequestPermissionRequest,
|
||||
toolName: string,
|
||||
signal: AbortSignal,
|
||||
): Promise<RequestPermissionResponse> {
|
||||
// The SDK may invoke `canUseTool` (and therefore this permission request)
|
||||
// before the assistant message's tool_use block streams to us. Some ACP clients
|
||||
// expect the `tool_call` a permission request references to already exist,
|
||||
// so emit it now if it hasn't been sent yet. The streamed tool_use chunk
|
||||
// later refines it with a `tool_call_update` rather than emitting a
|
||||
// duplicate (see `emittedToolCalls` in `toAcpNotifications`).
|
||||
await this.ensureToolCallEmitted(
|
||||
params.sessionId,
|
||||
toolName,
|
||||
params.toolCall.toolCallId,
|
||||
params.toolCall.rawInput,
|
||||
);
|
||||
try {
|
||||
return await this.client.requestPermission(params, signal);
|
||||
} catch (error) {
|
||||
@@ -2585,43 +2543,8 @@ export class ClaudeAcpAgent {
|
||||
}
|
||||
}
|
||||
|
||||
/** Emit the `tool_call` a permission request references if it hasn't been sent
|
||||
* yet, so the client has the tool call before being asked to approve it. The
|
||||
* matching streamed tool_use chunk later refines it with a `tool_call_update`
|
||||
* instead of emitting a duplicate (see `emittedToolCalls`). Built via the same
|
||||
* `toolCallNotification` helper as the streamed path so the two are identical.
|
||||
* Tools the stream renders as a plan (TodoWrite) or suppresses (Task*) are
|
||||
* skipped so a permission prompt for them never surfaces a stray tool_call. */
|
||||
private async ensureToolCallEmitted(
|
||||
sessionId: string,
|
||||
toolName: string,
|
||||
toolCallId: string,
|
||||
toolInput: unknown,
|
||||
): Promise<void> {
|
||||
const session = this.sessions[sessionId];
|
||||
if (!session || !shouldEmitToolCall(toolName)) {
|
||||
return;
|
||||
}
|
||||
if (session.emittedToolCalls.has(toolCallId)) {
|
||||
return;
|
||||
}
|
||||
session.emittedToolCalls.add(toolCallId);
|
||||
const supportsTerminalOutput = this.clientCapabilities?._meta?.["terminal_output"] === true;
|
||||
await this.client.sessionUpdate({
|
||||
sessionId,
|
||||
update: toolCallNotification(
|
||||
{ id: toolCallId, name: toolName, input: toolInput },
|
||||
toolInput,
|
||||
supportsTerminalOutput,
|
||||
session.cwd,
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
canUseTool(sessionId: string): CanUseTool {
|
||||
return async (toolName, toolInput, { signal, suggestions, toolUseID }) => {
|
||||
const alwaysAllowLabel = describeAlwaysAllow(suggestions, toolName);
|
||||
const supportsTerminalOutput = this.clientCapabilities?._meta?.["terminal_output"] === true;
|
||||
const session = this.sessions[sessionId];
|
||||
if (!session) {
|
||||
return {
|
||||
@@ -2629,15 +2552,19 @@ export class ClaudeAcpAgent {
|
||||
message: "Session not found",
|
||||
};
|
||||
}
|
||||
if (!session.toolUseCache[toolUseID]) {
|
||||
const { promise, resolve } = Promise.withResolvers<void>();
|
||||
session.waitingForToolCall.set(toolUseID, resolve);
|
||||
await promise;
|
||||
}
|
||||
const alwaysAllowLabel = describeAlwaysAllow(suggestions, toolName);
|
||||
const supportsTerminalOutput = this.clientCapabilities?._meta?.["terminal_output"] === true;
|
||||
|
||||
// AskUserQuestion is surfaced to us as a normal permission check (the SDK
|
||||
// routes it through canUseTool whenever a callback is registered, rather
|
||||
// than the interactive dialog). Present it as an ACP form elicitation and
|
||||
// feed the answers back as updatedInput for the tool's own call() to read.
|
||||
if (toolName === "AskUserQuestion" && this.clientCapabilities?.elicitation?.form) {
|
||||
// Like permission requests, the elicitation references this toolUseID, so
|
||||
// make sure the tool_call has surfaced to the client before we send it.
|
||||
await this.ensureToolCallEmitted(sessionId, toolName, toolUseID, toolInput);
|
||||
return this.handleAskUserQuestion(sessionId, toolInput, toolUseID, signal);
|
||||
}
|
||||
|
||||
@@ -2682,7 +2609,6 @@ export class ClaudeAcpAgent {
|
||||
),
|
||||
},
|
||||
},
|
||||
toolName,
|
||||
signal,
|
||||
);
|
||||
|
||||
@@ -2755,7 +2681,6 @@ export class ClaudeAcpAgent {
|
||||
),
|
||||
},
|
||||
},
|
||||
toolName,
|
||||
signal,
|
||||
);
|
||||
if (signal.aborted || response.outcome?.outcome === "cancelled") {
|
||||
@@ -3494,7 +3419,7 @@ export class ClaudeAcpAgent {
|
||||
) ?? DEFAULT_CONTEXT_WINDOW,
|
||||
taskState,
|
||||
toolUseCache: {},
|
||||
emittedToolCalls: new Set(),
|
||||
waitingForToolCall: new Map(),
|
||||
messageIdToUuid: new Map(),
|
||||
};
|
||||
|
||||
@@ -4211,64 +4136,6 @@ function applyMessageId(
|
||||
}
|
||||
}
|
||||
|
||||
/** Built-in tools that drive the task list (headless/SDK sessions use these
|
||||
* instead of TodoWrite). Their tool_use/tool_result are surfaced as `plan`
|
||||
* snapshots rather than as tool_calls. */
|
||||
function isTaskTool(toolName: string): boolean {
|
||||
return (
|
||||
toolName === "TaskCreate" ||
|
||||
toolName === "TaskUpdate" ||
|
||||
toolName === "TaskList" ||
|
||||
toolName === "TaskGet"
|
||||
);
|
||||
}
|
||||
|
||||
/** Whether a tool's tool_use surfaces to the client as a standalone
|
||||
* `tool_call`. TodoWrite is rendered as a `plan` and Task* tools are
|
||||
* suppressed (their plan snapshot is emitted at tool_result time), so neither
|
||||
* produces a tool_call. */
|
||||
function shouldEmitToolCall(toolName: string): boolean {
|
||||
return toolName !== "TodoWrite" && !isTaskTool(toolName);
|
||||
}
|
||||
|
||||
/** Build the `tool_call` (or, with `refine`, the `tool_call_update`)
|
||||
* notification for a tool_use. Shared by every site that surfaces a tool call:
|
||||
* the streamed tool_use path (first encounter → tool_call, later encounter →
|
||||
* refine) and the permission flow (`ensureToolCallEmitted`), so they can't
|
||||
* drift. The initial `tool_call` carries `status: "pending"` and, for Bash, the
|
||||
* `terminal_info` _meta that the later `terminal_output`/`terminal_exit`
|
||||
* updates key off of; a refining `tool_call_update` carries neither. */
|
||||
function toolCallNotification(
|
||||
toolUse: { id: string; name: string; input: unknown },
|
||||
rawInput: unknown,
|
||||
supportsTerminalOutput: boolean,
|
||||
cwd?: string,
|
||||
refine = false,
|
||||
): SessionNotification["update"] {
|
||||
if (refine) {
|
||||
return {
|
||||
_meta: { claudeCode: { toolName: toolUse.name } } satisfies ToolUpdateMeta,
|
||||
toolCallId: toolUse.id,
|
||||
sessionUpdate: "tool_call_update",
|
||||
rawInput,
|
||||
...toolInfoFromToolUse(toolUse, supportsTerminalOutput, cwd),
|
||||
};
|
||||
}
|
||||
return {
|
||||
_meta: {
|
||||
claudeCode: { toolName: toolUse.name },
|
||||
...(toolUse.name === "Bash" && supportsTerminalOutput
|
||||
? { terminal_info: { terminal_id: toolUse.id } }
|
||||
: {}),
|
||||
} satisfies ToolUpdateMeta,
|
||||
toolCallId: toolUse.id,
|
||||
sessionUpdate: "tool_call",
|
||||
rawInput,
|
||||
status: "pending",
|
||||
...toolInfoFromToolUse(toolUse, supportsTerminalOutput, cwd),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an SDKAssistantMessage (Claude) to a SessionNotification (ACP).
|
||||
* Only handles text, image, and thinking chunks for now.
|
||||
@@ -4278,6 +4145,7 @@ export function toAcpNotifications(
|
||||
role: "assistant" | "user",
|
||||
sessionId: string,
|
||||
toolUseCache: ToolUseCache,
|
||||
waitingForToolCall: WaitingForToolCall,
|
||||
client: AcpClient,
|
||||
logger: Logger,
|
||||
options?: {
|
||||
@@ -4286,13 +4154,6 @@ export function toAcpNotifications(
|
||||
parentToolUseId?: string | null;
|
||||
cwd?: string;
|
||||
taskState?: TaskState;
|
||||
// Tracks tool_use ids already emitted as a `tool_call` so a permission
|
||||
// request (which emits the tool_call eagerly) and the streamed tool_use
|
||||
// chunk don't both emit one — whichever arrives second emits a
|
||||
// `tool_call_update` instead. Mutated in place. When omitted, the
|
||||
// tool_call/update decision falls back to `toolUseCache` presence (the
|
||||
// historical single-source behavior).
|
||||
emittedToolCalls?: Set<string>;
|
||||
// Opaque id identifying the message these chunks belong to (ACP message ids
|
||||
// are opaque strings — no particular format is required). Attached to
|
||||
// user/agent message and thought chunks so clients can group streamed chunks
|
||||
@@ -4380,7 +4241,12 @@ export function toAcpNotifications(
|
||||
entries: planEntries(chunk.input as { todos: ClaudePlanEntry[] }),
|
||||
};
|
||||
}
|
||||
} else if (isTaskTool(chunk.name)) {
|
||||
} else if (
|
||||
chunk.name === "TaskCreate" ||
|
||||
chunk.name === "TaskUpdate" ||
|
||||
chunk.name === "TaskList" ||
|
||||
chunk.name === "TaskGet"
|
||||
) {
|
||||
// Task* tool_use is suppressed; the plan update is emitted at
|
||||
// tool_result time once we have the task ID (for TaskCreate) and
|
||||
// confirmation that the change took effect.
|
||||
@@ -4433,31 +4299,46 @@ export function toAcpNotifications(
|
||||
// ignore if we can't turn it to JSON
|
||||
}
|
||||
|
||||
// Emit a `tool_call` only the first time this id surfaces to the
|
||||
// client; afterwards refine it with a `tool_call_update`. The first
|
||||
// surface may be this stream chunk OR an earlier permission request
|
||||
// (see `ensureToolCallEmitted`), so emission is tracked separately
|
||||
// from `toolUseCache`. Without an `emittedToolCalls` set we fall back
|
||||
// to cache presence — the historical streaming-only behavior.
|
||||
const emittedToolCalls = options?.emittedToolCalls;
|
||||
const alreadyEmitted = emittedToolCalls ? emittedToolCalls.has(chunk.id) : alreadyCached;
|
||||
emittedToolCalls?.add(chunk.id);
|
||||
|
||||
if (alreadyEmitted) {
|
||||
// Already surfaced (full assistant message after streaming, or a
|
||||
// permission request emitted it first) — refine with a
|
||||
// tool_call_update rather than emitting a duplicate tool_call.
|
||||
update = toolCallNotification(
|
||||
chunk,
|
||||
if (alreadyCached) {
|
||||
// Second encounter (full assistant message after streaming) —
|
||||
// send as tool_call_update to refine the existing tool_call
|
||||
// rather than emitting a duplicate tool_call.
|
||||
update = {
|
||||
_meta: {
|
||||
claudeCode: {
|
||||
toolName: chunk.name,
|
||||
},
|
||||
} satisfies ToolUpdateMeta,
|
||||
toolCallId: chunk.id,
|
||||
sessionUpdate: "tool_call_update",
|
||||
rawInput,
|
||||
supportsTerminalOutput,
|
||||
options?.cwd,
|
||||
true,
|
||||
);
|
||||
...toolInfoFromToolUse(chunk, supportsTerminalOutput, options?.cwd),
|
||||
};
|
||||
} else {
|
||||
// First surface (streaming content_block_start or replay) — send as
|
||||
// tool_call (with terminal_info for Bash).
|
||||
update = toolCallNotification(chunk, rawInput, supportsTerminalOutput, options?.cwd);
|
||||
if (waitingForToolCall.has(chunk.id)) {
|
||||
// If a canUseTool is waiting for the first tool_use chunk,
|
||||
// we can resolve it now.
|
||||
const resolve = waitingForToolCall.get(chunk.id)!;
|
||||
resolve();
|
||||
waitingForToolCall.delete(chunk.id);
|
||||
}
|
||||
// First encounter (streaming content_block_start or replay) —
|
||||
// send as tool_call with terminal_info for Bash tools.
|
||||
update = {
|
||||
_meta: {
|
||||
claudeCode: {
|
||||
toolName: chunk.name,
|
||||
},
|
||||
...(chunk.name === "Bash" && supportsTerminalOutput
|
||||
? { terminal_info: { terminal_id: chunk.id } }
|
||||
: {}),
|
||||
} satisfies ToolUpdateMeta,
|
||||
toolCallId: chunk.id,
|
||||
sessionUpdate: "tool_call",
|
||||
rawInput,
|
||||
status: "pending",
|
||||
...toolInfoFromToolUse(chunk, supportsTerminalOutput, options?.cwd),
|
||||
};
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -4471,7 +4352,6 @@ export function toAcpNotifications(
|
||||
case "bash_code_execution_tool_result":
|
||||
case "text_editor_code_execution_tool_result":
|
||||
case "mcp_tool_result": {
|
||||
options?.emittedToolCalls?.delete(chunk.tool_use_id);
|
||||
const toolUse = toolUseCache[chunk.tool_use_id];
|
||||
if (!toolUse) {
|
||||
logger.error(
|
||||
@@ -4480,7 +4360,12 @@ export function toAcpNotifications(
|
||||
break;
|
||||
}
|
||||
|
||||
if (isTaskTool(toolUse.name)) {
|
||||
if (
|
||||
toolUse.name === "TaskCreate" ||
|
||||
toolUse.name === "TaskUpdate" ||
|
||||
toolUse.name === "TaskList" ||
|
||||
toolUse.name === "TaskGet"
|
||||
) {
|
||||
// Headless/SDK sessions emit Task* tools instead of TodoWrite.
|
||||
// TaskCreate / TaskUpdate mutate the accumulated task list; TaskList
|
||||
// and TaskGet are read-only so we just suppress their tool_call /
|
||||
@@ -4594,13 +4479,13 @@ export function streamEventToAcpNotifications(
|
||||
message: SDKPartialAssistantMessage,
|
||||
sessionId: string,
|
||||
toolUseCache: ToolUseCache,
|
||||
waitingForToolCall: WaitingForToolCall,
|
||||
client: AcpClient,
|
||||
logger: Logger,
|
||||
options?: {
|
||||
clientCapabilities?: ClientCapabilities;
|
||||
cwd?: string;
|
||||
taskState?: TaskState;
|
||||
emittedToolCalls?: Set<string>;
|
||||
messageId?: string;
|
||||
},
|
||||
): SessionNotification[] {
|
||||
@@ -4612,6 +4497,7 @@ export function streamEventToAcpNotifications(
|
||||
"assistant",
|
||||
sessionId,
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
client,
|
||||
logger,
|
||||
{
|
||||
@@ -4619,7 +4505,6 @@ export function streamEventToAcpNotifications(
|
||||
parentToolUseId: message.parent_tool_use_id,
|
||||
cwd: options?.cwd,
|
||||
taskState: options?.taskState,
|
||||
emittedToolCalls: options?.emittedToolCalls,
|
||||
messageId: options?.messageId,
|
||||
},
|
||||
);
|
||||
@@ -4629,6 +4514,7 @@ export function streamEventToAcpNotifications(
|
||||
"assistant",
|
||||
sessionId,
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
client,
|
||||
logger,
|
||||
{
|
||||
@@ -4636,7 +4522,6 @@ export function streamEventToAcpNotifications(
|
||||
parentToolUseId: message.parent_tool_use_id,
|
||||
cwd: options?.cwd,
|
||||
taskState: options?.taskState,
|
||||
emittedToolCalls: options?.emittedToolCalls,
|
||||
messageId: options?.messageId,
|
||||
},
|
||||
);
|
||||
@@ -4717,7 +4602,6 @@ export function runAcp() {
|
||||
agent.setSessionConfigOption(ctx.params),
|
||||
)
|
||||
.onRequest(methods.agent.authenticate, (ctx) => agent.authenticate(ctx.params))
|
||||
.onRequest(methods.agent.logout, (ctx) => agent.logout(ctx.params))
|
||||
.onRequest(methods.agent.session.prompt, (ctx) =>
|
||||
runPromptWithCancellation(agent, ctx.params, ctx.signal),
|
||||
)
|
||||
|
||||
@@ -232,7 +232,8 @@ export function askUserQuestionsToCreateRequest(
|
||||
|
||||
/** Outcome of an AskUserQuestion elicitation, decoupled from any transport. */
|
||||
export type AskUserQuestionOutcome =
|
||||
{ action: "answered"; updatedInput: Record<string, unknown> } | { action: "cancel" };
|
||||
| { action: "answered"; updatedInput: Record<string, unknown> }
|
||||
| { action: "cancel" };
|
||||
|
||||
/**
|
||||
* Fold an ACP elicitation response into the AskUserQuestion tool's input.
|
||||
|
||||
11
src/index.ts
11
src/index.ts
@@ -4,10 +4,10 @@ import { resolveSettings } from "@anthropic-ai/claude-agent-sdk";
|
||||
import { claudeCliPath, runAcp } from "./acp-agent.js";
|
||||
import packageJson from "../package.json" with { type: "json" };
|
||||
|
||||
// `--cli` is checked first so that `--version`/`-v` (and any other flags) are
|
||||
// forwarded to the wrapped native CLI rather than swallowed by our own version
|
||||
// handler below. Our version flag only applies when not delegating.
|
||||
if (process.argv.includes("--cli")) {
|
||||
if (process.argv.includes("--version") || process.argv.includes("-v")) {
|
||||
console.log(packageJson.version);
|
||||
process.exit(0);
|
||||
} else if (process.argv.includes("--cli")) {
|
||||
const { spawn } = await import("node:child_process");
|
||||
const args = process.argv.slice(2).filter((arg) => arg !== "--cli");
|
||||
const child = spawn(await claudeCliPath(), args, { stdio: "inherit" });
|
||||
@@ -37,9 +37,6 @@ if (process.argv.includes("--cli")) {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
} else if (process.argv.includes("--version") || process.argv.includes("-v")) {
|
||||
console.log(packageJson.version);
|
||||
process.exit(0);
|
||||
} else {
|
||||
// Apply env vars from the managed-policy tier before any SDK call so the
|
||||
// SDK subprocess inherits them. Going through resolveSettings (vs. a raw
|
||||
|
||||
@@ -118,7 +118,7 @@ function mockSessionState(overrides: Record<string, any> = {}) {
|
||||
contextWindowSize: 200000,
|
||||
taskState: new Map(),
|
||||
toolUseCache: {},
|
||||
emittedToolCalls: new Set(),
|
||||
waitingForToolCall: new Map(),
|
||||
messageIdToUuid: new Map(),
|
||||
...overrides,
|
||||
} as any;
|
||||
@@ -873,6 +873,7 @@ describe("tool conversions", () => {
|
||||
received.message.role,
|
||||
"test",
|
||||
{},
|
||||
new Map(),
|
||||
{} as AcpClient,
|
||||
console,
|
||||
),
|
||||
@@ -1815,12 +1816,51 @@ describe("permission request cancellation", () => {
|
||||
contextWindowSize: 200000,
|
||||
taskState: new Map(),
|
||||
toolUseCache: {},
|
||||
emittedToolCalls: new Set(),
|
||||
waitingForToolCall: new Map(),
|
||||
messageIdToUuid: new Map(),
|
||||
} as any;
|
||||
return agent.sessions[sessionId]!;
|
||||
}
|
||||
|
||||
it("waits for the first tool_call before requesting permission", async () => {
|
||||
const requestPermission = vi.fn(async () => ({
|
||||
outcome: { outcome: "selected", optionId: "allow" },
|
||||
}));
|
||||
const mockClient = {
|
||||
sessionUpdate: async () => {},
|
||||
requestPermission,
|
||||
} as unknown as AcpClient;
|
||||
const agent = new ClaudeAcpAgent(mockClient, { log: () => {}, error: () => {} });
|
||||
const session = injectSession(agent, "session-1");
|
||||
|
||||
const pending = agent.canUseTool("session-1")("Bash", { command: "echo hello" }, {
|
||||
signal: new AbortController().signal,
|
||||
suggestions: [],
|
||||
toolUseID: "tool-1",
|
||||
} as any);
|
||||
|
||||
await Promise.resolve();
|
||||
expect(requestPermission).not.toHaveBeenCalled();
|
||||
expect(session.waitingForToolCall.has("tool-1")).toBe(true);
|
||||
|
||||
const notifications = toAcpNotifications(
|
||||
[{ type: "tool_use", id: "tool-1", name: "Bash", input: { command: "echo hello" } }],
|
||||
"assistant",
|
||||
"session-1",
|
||||
session.toolUseCache,
|
||||
session.waitingForToolCall,
|
||||
mockClient,
|
||||
console,
|
||||
);
|
||||
|
||||
expect(notifications[0].update.sessionUpdate).toBe("tool_call");
|
||||
expect(requestPermission).not.toHaveBeenCalled();
|
||||
|
||||
await expect(pending).resolves.toMatchObject({ behavior: "allow" });
|
||||
expect(requestPermission).toHaveBeenCalledTimes(1);
|
||||
expect(session.waitingForToolCall.has("tool-1")).toBe(false);
|
||||
});
|
||||
|
||||
it("forwards the tool-call signal so a pending permission request is cancelled on abort", async () => {
|
||||
let receivedSignal: AbortSignal | undefined;
|
||||
const mockClient = {
|
||||
@@ -1838,10 +1878,12 @@ describe("permission request cancellation", () => {
|
||||
} as unknown as AcpClient;
|
||||
const agent = new ClaudeAcpAgent(mockClient, { log: () => {}, error: () => {} });
|
||||
const session = injectSession(agent, "session-1");
|
||||
// The tool_call was already surfaced by the streamed tool_use chunk, so the
|
||||
// permission request goes straight to requestPermission without first
|
||||
// emitting one.
|
||||
session.emittedToolCalls.add("tool-1");
|
||||
session.toolUseCache["tool-1"] = {
|
||||
type: "tool_use",
|
||||
id: "tool-1",
|
||||
name: "Bash",
|
||||
input: { command: "ls" },
|
||||
};
|
||||
|
||||
const controller = new AbortController();
|
||||
const pending = agent.canUseTool("session-1")("Bash", { command: "ls" }, {
|
||||
@@ -1866,7 +1908,13 @@ describe("permission request cancellation", () => {
|
||||
requestPermission: async () => ({ outcome: { outcome: "cancelled" } }),
|
||||
} as unknown as AcpClient;
|
||||
const agent = new ClaudeAcpAgent(mockClient, { log: () => {}, error: () => {} });
|
||||
injectSession(agent, "session-1");
|
||||
const session = injectSession(agent, "session-1");
|
||||
session.toolUseCache["tool-1"] = {
|
||||
type: "tool_use",
|
||||
id: "tool-1",
|
||||
name: "Bash",
|
||||
input: { command: "ls" },
|
||||
};
|
||||
|
||||
await expect(
|
||||
agent.canUseTool("session-1")("Bash", { command: "ls" }, {
|
||||
@@ -1878,136 +1926,6 @@ describe("permission request cancellation", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("tool_call emitted before permission request", () => {
|
||||
// The SDK can invoke canUseTool before the assistant message's tool_use block
|
||||
// streams to us. ACP clients expect the tool_call a permission request
|
||||
// references to already exist, so the permission flow emits it eagerly and the
|
||||
// streamed chunk later refines it with a tool_call_update (deduped via
|
||||
// session.emittedToolCalls) rather than emitting a duplicate.
|
||||
function setup(overrides: Record<string, any> = {}) {
|
||||
const events: string[] = [];
|
||||
const updates: SessionNotification[] = [];
|
||||
const mockClient = {
|
||||
sessionUpdate: async (n: SessionNotification) => {
|
||||
events.push(`update:${n.update.sessionUpdate}`);
|
||||
updates.push(n);
|
||||
},
|
||||
requestPermission: async () => {
|
||||
events.push("permission");
|
||||
return { outcome: { outcome: "selected", optionId: "allow" } };
|
||||
},
|
||||
} as unknown as AcpClient;
|
||||
const agent = new ClaudeAcpAgent(mockClient, { log: () => {}, error: () => {} });
|
||||
agent.sessions["session-1"] = mockSessionState(overrides);
|
||||
return { agent, events, updates, session: agent.sessions["session-1"]! };
|
||||
}
|
||||
|
||||
it("emits the tool_call (then asks permission) when the stream hasn't yet", async () => {
|
||||
const { agent, events, updates, session } = setup();
|
||||
|
||||
const result = await agent.canUseTool("session-1")("Bash", { command: "ls" }, {
|
||||
signal: new AbortController().signal,
|
||||
suggestions: [],
|
||||
toolUseID: "tool-1",
|
||||
} as any);
|
||||
|
||||
// tool_call is sent before the permission request is raised.
|
||||
expect(events).toEqual(["update:tool_call", "permission"]);
|
||||
expect(updates[0].update).toMatchObject({
|
||||
sessionUpdate: "tool_call",
|
||||
toolCallId: "tool-1",
|
||||
status: "pending",
|
||||
});
|
||||
expect(session.emittedToolCalls.has("tool-1")).toBe(true);
|
||||
expect(result).toMatchObject({ behavior: "allow" });
|
||||
});
|
||||
|
||||
it("does not re-emit the tool_call when the stream already surfaced it", async () => {
|
||||
const { agent, events } = setup();
|
||||
agent.sessions["session-1"]!.emittedToolCalls.add("tool-1");
|
||||
|
||||
await agent.canUseTool("session-1")("Bash", { command: "ls" }, {
|
||||
signal: new AbortController().signal,
|
||||
suggestions: [],
|
||||
toolUseID: "tool-1",
|
||||
} as any);
|
||||
|
||||
expect(events).toEqual(["permission"]);
|
||||
});
|
||||
|
||||
it("refines the eagerly-emitted tool_call with a tool_call_update when the chunk streams", () => {
|
||||
const { session } = setup();
|
||||
// Permission flow already emitted the tool_call for this id.
|
||||
session.emittedToolCalls.add("tool-1");
|
||||
|
||||
const notifications = toAcpNotifications(
|
||||
[{ type: "tool_use", id: "tool-1", name: "Bash", input: { command: "ls" } }],
|
||||
"assistant",
|
||||
"session-1",
|
||||
session.toolUseCache,
|
||||
{} as AcpClient,
|
||||
console,
|
||||
{ emittedToolCalls: session.emittedToolCalls },
|
||||
);
|
||||
|
||||
expect(notifications).toHaveLength(1);
|
||||
expect(notifications[0].update.sessionUpdate).toBe("tool_call_update");
|
||||
});
|
||||
|
||||
it("does not emit a tool_call for suppressed tools (TodoWrite) on a permission request", async () => {
|
||||
const { agent, events, session } = setup();
|
||||
|
||||
await agent.canUseTool("session-1")(
|
||||
"TodoWrite",
|
||||
{ todos: [{ content: "x", status: "pending" }] },
|
||||
{ signal: new AbortController().signal, suggestions: [], toolUseID: "todo-1" } as any,
|
||||
);
|
||||
|
||||
expect(events).toEqual(["permission"]);
|
||||
expect(session.emittedToolCalls.has("todo-1")).toBe(false);
|
||||
});
|
||||
|
||||
it("includes Bash terminal_info _meta in the eager tool_call so terminal output can attach", async () => {
|
||||
const { agent, updates } = setup();
|
||||
// Terminal-capable client (e.g. Zed). The eager tool_call must carry
|
||||
// terminal_info.terminal_id, otherwise the later terminal_output/terminal_exit
|
||||
// updates (keyed by terminal_id) have nothing to attach to.
|
||||
(agent as any).clientCapabilities = { _meta: { terminal_output: true } };
|
||||
|
||||
await agent.canUseTool("session-1")("Bash", { command: "ls" }, {
|
||||
signal: new AbortController().signal,
|
||||
suggestions: [],
|
||||
toolUseID: "tool-1",
|
||||
} as any);
|
||||
|
||||
expect(updates[0].update).toMatchObject({
|
||||
sessionUpdate: "tool_call",
|
||||
toolCallId: "tool-1",
|
||||
_meta: { terminal_info: { terminal_id: "tool-1" } },
|
||||
});
|
||||
});
|
||||
|
||||
it("prunes the emission marker on a tool_result even when the tool_use was never cached", () => {
|
||||
const { session } = setup();
|
||||
// Eager-emitted via the permission flow, but the tool_use chunk never
|
||||
// streamed (e.g. cancelled), so toolUseCache has no entry for it.
|
||||
session.emittedToolCalls.add("tool-1");
|
||||
|
||||
toAcpNotifications(
|
||||
[{ type: "tool_result", tool_use_id: "tool-1", content: [{ type: "text", text: "x" }] }],
|
||||
"user",
|
||||
"session-1",
|
||||
session.toolUseCache,
|
||||
{} as AcpClient,
|
||||
// Silence the expected "tool result for tool use that wasn't tracked" log.
|
||||
{ log: () => {}, error: () => {} },
|
||||
{ emittedToolCalls: session.emittedToolCalls },
|
||||
);
|
||||
|
||||
expect(session.emittedToolCalls.has("tool-1")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("runPromptWithCancellation", () => {
|
||||
function deferred<T>() {
|
||||
let resolve!: (value: T) => void;
|
||||
@@ -2659,24 +2577,6 @@ describe("stop reason propagation", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("logout", () => {
|
||||
function createMockAgent() {
|
||||
const mockClient = {
|
||||
sessionUpdate: async () => {},
|
||||
} as unknown as AcpClient;
|
||||
return new ClaudeAcpAgent(mockClient, { log: () => {}, error: () => {} });
|
||||
}
|
||||
|
||||
it("advertises the logout capability during initialize", async () => {
|
||||
const agent = createMockAgent();
|
||||
const response = await agent.initialize({
|
||||
protocolVersion: 1,
|
||||
clientCapabilities: {},
|
||||
});
|
||||
expect(response.agentCapabilities?.auth?.logout).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe("session/close", () => {
|
||||
function createMockAgent() {
|
||||
const mockClient = {
|
||||
@@ -2718,7 +2618,7 @@ describe("session/close", () => {
|
||||
contextWindowSize: 200000,
|
||||
taskState: new Map(),
|
||||
toolUseCache: {},
|
||||
emittedToolCalls: new Set(),
|
||||
waitingForToolCall: new Map(),
|
||||
messageIdToUuid: new Map(),
|
||||
};
|
||||
return agent.sessions[sessionId]!;
|
||||
@@ -2804,7 +2704,7 @@ describe("session/delete", () => {
|
||||
contextWindowSize: 200000,
|
||||
taskState: new Map(),
|
||||
toolUseCache: {},
|
||||
emittedToolCalls: new Set(),
|
||||
waitingForToolCall: new Map(),
|
||||
messageIdToUuid: new Map(),
|
||||
};
|
||||
return agent.sessions[sessionId]!;
|
||||
@@ -2907,7 +2807,7 @@ describe("getOrCreateSession param change detection", () => {
|
||||
contextWindowSize: 200000,
|
||||
taskState: new Map(),
|
||||
toolUseCache: {},
|
||||
emittedToolCalls: new Set(),
|
||||
waitingForToolCall: new Map(),
|
||||
messageIdToUuid: new Map(),
|
||||
};
|
||||
return agent.sessions[sessionId]!;
|
||||
@@ -5122,7 +5022,7 @@ describe("post-error recovery", () => {
|
||||
contextWindowSize: 200000,
|
||||
taskState: new Map(),
|
||||
toolUseCache: {},
|
||||
emittedToolCalls: new Set(),
|
||||
waitingForToolCall: new Map(),
|
||||
messageIdToUuid: new Map(),
|
||||
};
|
||||
return { interrupt };
|
||||
@@ -5693,7 +5593,7 @@ describe("session/cancel wedge recovery (issue #680)", () => {
|
||||
contextWindowSize: 200000,
|
||||
taskState: new Map(),
|
||||
toolUseCache: {},
|
||||
emittedToolCalls: new Set(),
|
||||
waitingForToolCall: new Map(),
|
||||
messageIdToUuid: new Map(),
|
||||
};
|
||||
return { interrupt };
|
||||
@@ -5825,9 +5725,8 @@ describe("streamEventToAcpNotifications", () => {
|
||||
pingMessage,
|
||||
"test-session",
|
||||
{},
|
||||
{ sessionUpdate: async () => {} } as unknown as Parameters<
|
||||
typeof streamEventToAcpNotifications
|
||||
>[3],
|
||||
new Map(),
|
||||
{ sessionUpdate: async () => {} } as unknown as AcpClient,
|
||||
logger,
|
||||
);
|
||||
|
||||
@@ -5849,9 +5748,17 @@ describe("streamEventToAcpNotifications", () => {
|
||||
},
|
||||
} as Parameters<typeof streamEventToAcpNotifications>[0];
|
||||
|
||||
const result = streamEventToAcpNotifications(message, "test", {}, {} as AcpClient, console, {
|
||||
messageId,
|
||||
});
|
||||
const result = streamEventToAcpNotifications(
|
||||
message,
|
||||
"test",
|
||||
{},
|
||||
new Map(),
|
||||
{} as AcpClient,
|
||||
console,
|
||||
{
|
||||
messageId,
|
||||
},
|
||||
);
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
@@ -5875,6 +5782,7 @@ describe("toAcpNotifications messageId", () => {
|
||||
"assistant",
|
||||
"test",
|
||||
{},
|
||||
new Map(),
|
||||
{} as AcpClient,
|
||||
console,
|
||||
{ messageId },
|
||||
@@ -5898,6 +5806,7 @@ describe("toAcpNotifications messageId", () => {
|
||||
"user",
|
||||
"test",
|
||||
{},
|
||||
new Map(),
|
||||
{} as AcpClient,
|
||||
console,
|
||||
{ messageId },
|
||||
@@ -5912,6 +5821,7 @@ describe("toAcpNotifications messageId", () => {
|
||||
"assistant",
|
||||
"test",
|
||||
{},
|
||||
new Map(),
|
||||
{} as AcpClient,
|
||||
console,
|
||||
{ messageId },
|
||||
@@ -5923,7 +5833,15 @@ describe("toAcpNotifications messageId", () => {
|
||||
});
|
||||
|
||||
it("omits messageId when none is supplied", () => {
|
||||
const result = toAcpNotifications("hello", "assistant", "test", {}, {} as AcpClient, console);
|
||||
const result = toAcpNotifications(
|
||||
"hello",
|
||||
"assistant",
|
||||
"test",
|
||||
{},
|
||||
new Map(),
|
||||
{} as AcpClient,
|
||||
console,
|
||||
);
|
||||
expect(result[0].update).not.toHaveProperty("messageId");
|
||||
});
|
||||
|
||||
@@ -5940,6 +5858,7 @@ describe("toAcpNotifications messageId", () => {
|
||||
"assistant",
|
||||
"test",
|
||||
{},
|
||||
new Map(),
|
||||
{} as AcpClient,
|
||||
console,
|
||||
{ messageId, registerHooks: false },
|
||||
@@ -5956,6 +5875,7 @@ describe("toAcpNotifications thinking chunks", () => {
|
||||
"assistant",
|
||||
"test",
|
||||
{},
|
||||
new Map(),
|
||||
{} as AcpClient,
|
||||
console,
|
||||
);
|
||||
@@ -5977,6 +5897,7 @@ describe("toAcpNotifications thinking chunks", () => {
|
||||
"assistant",
|
||||
"test",
|
||||
{},
|
||||
new Map(),
|
||||
{} as AcpClient,
|
||||
console,
|
||||
);
|
||||
@@ -5990,6 +5911,7 @@ describe("toAcpNotifications thinking chunks", () => {
|
||||
"assistant",
|
||||
"test",
|
||||
{},
|
||||
new Map(),
|
||||
{} as AcpClient,
|
||||
console,
|
||||
);
|
||||
@@ -6147,7 +6069,7 @@ describe("agent selection config option", () => {
|
||||
contextWindowSize: 200000,
|
||||
taskState: new Map(),
|
||||
toolUseCache: {},
|
||||
emittedToolCalls: new Set(),
|
||||
waitingForToolCall: new Map(),
|
||||
messageIdToUuid: new Map(),
|
||||
};
|
||||
return { session: agent.sessions[sessionId]!, applyFlagSettings };
|
||||
|
||||
@@ -113,17 +113,19 @@ describe("session config options", () => {
|
||||
settingsManager: {},
|
||||
modes: structuredClone(MOCK_MODES),
|
||||
models: structuredClone(MOCK_MODELS),
|
||||
modelInfos: MOCK_MODELS.availableModels.map((m): ModelInfo => ({
|
||||
value: m.modelId,
|
||||
displayName: m.name,
|
||||
description: m.description,
|
||||
supportsEffort: true,
|
||||
supportedEffortLevels: ["low", "medium", "high"],
|
||||
})),
|
||||
modelInfos: MOCK_MODELS.availableModels.map(
|
||||
(m): ModelInfo => ({
|
||||
value: m.modelId,
|
||||
displayName: m.name,
|
||||
description: m.description,
|
||||
supportsEffort: true,
|
||||
supportedEffortLevels: ["low", "medium", "high"],
|
||||
}),
|
||||
),
|
||||
configOptions: structuredClone(MOCK_CONFIG_OPTIONS),
|
||||
contextWindowSize: 200000,
|
||||
toolUseCache: {},
|
||||
emittedToolCalls: new Set(),
|
||||
waitingForToolCall: new Map(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1112,10 +1114,12 @@ describe("session config options", () => {
|
||||
{ id: "dontAsk", name: "Don't Ask", description: "Deny if not pre-approved" },
|
||||
],
|
||||
};
|
||||
|
||||
// The tool_call was already surfaced (by the streamed tool_use chunk), so
|
||||
// the permission request won't re-emit one — keep this focused on options.
|
||||
session.emittedToolCalls.add("toolu_1");
|
||||
session.toolUseCache["toolu_1"] = {
|
||||
type: "tool_use",
|
||||
id: "toolu_1",
|
||||
name: "ExitPlanMode",
|
||||
input: { plan: "do stuff" },
|
||||
};
|
||||
|
||||
const canUseTool = (agent as any).canUseTool(SESSION_ID);
|
||||
const signal = new AbortController().signal;
|
||||
@@ -1148,10 +1152,12 @@ describe("session config options", () => {
|
||||
],
|
||||
};
|
||||
permissionResponse = { outcome: { outcome: "selected", optionId: "auto" } };
|
||||
// The tool_call was already surfaced (by the streamed tool_use chunk), so
|
||||
// the permission request won't re-emit one — the deny path below should
|
||||
// produce no session updates at all.
|
||||
session.emittedToolCalls.add("toolu_2");
|
||||
session.toolUseCache["toolu_2"] = {
|
||||
type: "tool_use",
|
||||
id: "toolu_2",
|
||||
name: "ExitPlanMode",
|
||||
input: { plan: "do stuff" },
|
||||
};
|
||||
|
||||
const canUseTool = (agent as any).canUseTool(SESSION_ID);
|
||||
const result = await canUseTool(
|
||||
@@ -1179,10 +1185,12 @@ describe("session config options", () => {
|
||||
{ id: "dontAsk", name: "Don't Ask", description: "Deny if not pre-approved" },
|
||||
],
|
||||
};
|
||||
|
||||
// The tool_call was already surfaced (by the streamed tool_use chunk), so
|
||||
// the permission request won't re-emit one — keep this focused on options.
|
||||
session.emittedToolCalls.add("toolu_3");
|
||||
session.toolUseCache["toolu_3"] = {
|
||||
type: "tool_use",
|
||||
id: "toolu_3",
|
||||
name: "ExitPlanMode",
|
||||
input: { plan: "do stuff" },
|
||||
};
|
||||
|
||||
const canUseTool = (agent as any).canUseTool(SESSION_ID);
|
||||
const signal = new AbortController().signal;
|
||||
|
||||
@@ -10,7 +10,13 @@ import {
|
||||
BetaBashCodeExecutionResultBlock,
|
||||
BetaBashCodeExecutionToolResultBlockParam,
|
||||
} from "@anthropic-ai/sdk/resources/beta.mjs";
|
||||
import { AcpClient, toAcpNotifications, ToolUseCache, Logger } from "../acp-agent.js";
|
||||
import {
|
||||
AcpClient,
|
||||
toAcpNotifications,
|
||||
ToolUseCache,
|
||||
Logger,
|
||||
WaitingForToolCall,
|
||||
} from "../acp-agent.js";
|
||||
import {
|
||||
toolUpdateFromToolResult,
|
||||
createPostToolUseHook,
|
||||
@@ -37,6 +43,7 @@ describe("rawOutput in tool call updates", () => {
|
||||
input: { command: "echo hello" },
|
||||
},
|
||||
};
|
||||
const waitingForToolCall: WaitingForToolCall = new Map();
|
||||
|
||||
const toolResult: ToolResultBlockParam = {
|
||||
type: "tool_result",
|
||||
@@ -50,6 +57,7 @@ describe("rawOutput in tool call updates", () => {
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
);
|
||||
@@ -72,6 +80,7 @@ describe("rawOutput in tool call updates", () => {
|
||||
input: { file_path: "/test/file.txt" },
|
||||
},
|
||||
};
|
||||
const waitingForToolCall: WaitingForToolCall = new Map();
|
||||
|
||||
// ToolResultBlockParam content can be string or array of TextBlockParam
|
||||
const toolResult: ToolResultBlockParam = {
|
||||
@@ -86,6 +95,7 @@ describe("rawOutput in tool call updates", () => {
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
);
|
||||
@@ -108,6 +118,7 @@ describe("rawOutput in tool call updates", () => {
|
||||
input: { query: "test" },
|
||||
},
|
||||
};
|
||||
const waitingForToolCall: WaitingForToolCall = new Map();
|
||||
|
||||
// BetaMCPToolResultBlock content can be string or Array<BetaTextBlock>
|
||||
const toolResult: BetaMCPToolResultBlock = {
|
||||
@@ -122,6 +133,7 @@ describe("rawOutput in tool call updates", () => {
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
);
|
||||
@@ -144,6 +156,7 @@ describe("rawOutput in tool call updates", () => {
|
||||
input: { term: "test" },
|
||||
},
|
||||
};
|
||||
const waitingForToolCall: WaitingForToolCall = new Map();
|
||||
|
||||
// BetaTextBlock requires citations field
|
||||
const arrayContent: BetaTextBlock[] = [
|
||||
@@ -163,6 +176,7 @@ describe("rawOutput in tool call updates", () => {
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
);
|
||||
@@ -185,6 +199,7 @@ describe("rawOutput in tool call updates", () => {
|
||||
input: { query: "test search" },
|
||||
},
|
||||
};
|
||||
const waitingForToolCall: WaitingForToolCall = new Map();
|
||||
|
||||
// BetaWebSearchResultBlock from SDK
|
||||
const searchResults: BetaWebSearchResultBlock[] = [
|
||||
@@ -208,6 +223,7 @@ describe("rawOutput in tool call updates", () => {
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
);
|
||||
@@ -230,6 +246,7 @@ describe("rawOutput in tool call updates", () => {
|
||||
input: { command: "ls -la" },
|
||||
},
|
||||
};
|
||||
const waitingForToolCall: WaitingForToolCall = new Map();
|
||||
|
||||
// BetaBashCodeExecutionResultBlock from SDK
|
||||
const bashResult: BetaBashCodeExecutionResultBlock = {
|
||||
@@ -251,6 +268,7 @@ describe("rawOutput in tool call updates", () => {
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
);
|
||||
@@ -273,6 +291,7 @@ describe("rawOutput in tool call updates", () => {
|
||||
input: { command: "invalid_command" },
|
||||
},
|
||||
};
|
||||
const waitingForToolCall: WaitingForToolCall = new Map();
|
||||
|
||||
const toolResult: ToolResultBlockParam = {
|
||||
type: "tool_result",
|
||||
@@ -286,6 +305,7 @@ describe("rawOutput in tool call updates", () => {
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
);
|
||||
@@ -308,6 +328,7 @@ describe("rawOutput in tool call updates", () => {
|
||||
input: { todos: [{ content: "Test task", status: "pending" }] },
|
||||
},
|
||||
};
|
||||
const waitingForToolCall: WaitingForToolCall = new Map();
|
||||
|
||||
const toolResult: ToolResultBlockParam = {
|
||||
type: "tool_result",
|
||||
@@ -321,6 +342,7 @@ describe("rawOutput in tool call updates", () => {
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
);
|
||||
@@ -351,11 +373,14 @@ describe("rawOutput in tool call updates", () => {
|
||||
is_error: false,
|
||||
};
|
||||
|
||||
const waitingForToolCall: WaitingForToolCall = new Map();
|
||||
|
||||
const notifications = toAcpNotifications(
|
||||
[toolResult],
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
);
|
||||
@@ -383,6 +408,7 @@ describe("rawOutput in tool call updates", () => {
|
||||
input: { file_path: "/test/image.png" },
|
||||
},
|
||||
};
|
||||
const waitingForToolCall: WaitingForToolCall = new Map();
|
||||
|
||||
const imageBlock: ImageBlockParam = {
|
||||
type: "image",
|
||||
@@ -401,6 +427,7 @@ describe("rawOutput in tool call updates", () => {
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
);
|
||||
@@ -779,6 +806,7 @@ describe("Bash terminal output", () => {
|
||||
// Reset before each test: toAcpNotifications prunes the cache entry once it
|
||||
// maps the tool_result, so a shared object would be empty by the 2nd test.
|
||||
let toolUseCache: ToolUseCache;
|
||||
const waitingForToolCall: WaitingForToolCall = new Map();
|
||||
beforeEach(() => {
|
||||
toolUseCache = {
|
||||
toolu_bash: {
|
||||
@@ -814,6 +842,7 @@ describe("Bash terminal output", () => {
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
{ clientCapabilities },
|
||||
@@ -854,6 +883,7 @@ describe("Bash terminal output", () => {
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
);
|
||||
@@ -880,6 +910,7 @@ describe("Bash terminal output", () => {
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
{ clientCapabilities },
|
||||
@@ -895,6 +926,7 @@ describe("Bash terminal output", () => {
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
{ clientCapabilities: { _meta: { terminal_output: true } } },
|
||||
@@ -915,6 +947,7 @@ describe("Bash terminal output", () => {
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCacheWithoutSupport,
|
||||
waitingForToolCall,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
);
|
||||
@@ -947,6 +980,7 @@ describe("Bash terminal output", () => {
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
{ clientCapabilities },
|
||||
@@ -969,6 +1003,7 @@ describe("Bash terminal output", () => {
|
||||
describe("toolUseCache pruning", () => {
|
||||
it("retains the tool_use entry until its result, then prunes it", () => {
|
||||
const toolUseCache: ToolUseCache = {};
|
||||
const waitingForToolCall: WaitingForToolCall = new Map();
|
||||
const toolUse = {
|
||||
type: "tool_use" as const,
|
||||
id: "toolu_read",
|
||||
@@ -977,7 +1012,15 @@ describe("Bash terminal output", () => {
|
||||
};
|
||||
|
||||
// tool_use is cached and kept (the matching result hasn't arrived yet).
|
||||
toAcpNotifications([toolUse], "assistant", "s", toolUseCache, mockClient, mockLogger);
|
||||
toAcpNotifications(
|
||||
[toolUse],
|
||||
"assistant",
|
||||
"s",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
);
|
||||
expect(toolUseCache.toolu_read).toBeDefined();
|
||||
|
||||
// tool_result resolves it, so the entry is pruned to bound memory.
|
||||
@@ -986,7 +1029,15 @@ describe("Bash terminal output", () => {
|
||||
tool_use_id: "toolu_read",
|
||||
content: [{ type: "text", text: "hello" }],
|
||||
};
|
||||
toAcpNotifications([toolResult], "assistant", "s", toolUseCache, mockClient, mockLogger);
|
||||
toAcpNotifications(
|
||||
[toolResult],
|
||||
"assistant",
|
||||
"s",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
);
|
||||
expect(toolUseCache.toolu_read).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -994,6 +1045,7 @@ describe("Bash terminal output", () => {
|
||||
describe("post-tool-use hook sends diff content for Edit tool", () => {
|
||||
it("should include content and locations from structuredPatch in hook update", async () => {
|
||||
const toolUseCache: ToolUseCache = {};
|
||||
const waitingForToolCall: WaitingForToolCall = new Map();
|
||||
|
||||
const hookUpdates: any[] = [];
|
||||
const mockClientWithUpdate = {
|
||||
@@ -1019,6 +1071,7 @@ describe("Bash terminal output", () => {
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClientWithUpdate,
|
||||
mockLogger,
|
||||
);
|
||||
@@ -1073,6 +1126,7 @@ describe("Bash terminal output", () => {
|
||||
|
||||
it("should include multiple diff blocks for replaceAll with multiple hunks", async () => {
|
||||
const toolUseCache: ToolUseCache = {};
|
||||
const waitingForToolCall: WaitingForToolCall = new Map();
|
||||
|
||||
const hookUpdates: any[] = [];
|
||||
const mockClientWithUpdate = {
|
||||
@@ -1098,6 +1152,7 @@ describe("Bash terminal output", () => {
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClientWithUpdate,
|
||||
mockLogger,
|
||||
);
|
||||
@@ -1158,6 +1213,7 @@ describe("Bash terminal output", () => {
|
||||
|
||||
it("should not include content/locations for non-Edit tools", async () => {
|
||||
const toolUseCache: ToolUseCache = {};
|
||||
const waitingForToolCall: WaitingForToolCall = new Map();
|
||||
|
||||
const hookUpdates: any[] = [];
|
||||
const mockClientWithUpdate = {
|
||||
@@ -1178,6 +1234,7 @@ describe("Bash terminal output", () => {
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClientWithUpdate,
|
||||
mockLogger,
|
||||
);
|
||||
@@ -1213,6 +1270,7 @@ describe("Bash terminal output", () => {
|
||||
// tool_use time and was never corrected after the tool ran.
|
||||
it("should emit a real diff for Write of an existing file (type: update)", async () => {
|
||||
const toolUseCache: ToolUseCache = {};
|
||||
const waitingForToolCall: WaitingForToolCall = new Map();
|
||||
const hookUpdates: any[] = [];
|
||||
const mockClientWithUpdate = {
|
||||
sessionUpdate: async (notification: any) => {
|
||||
@@ -1235,6 +1293,7 @@ describe("Bash terminal output", () => {
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClientWithUpdate,
|
||||
mockLogger,
|
||||
);
|
||||
@@ -1289,6 +1348,7 @@ describe("Bash terminal output", () => {
|
||||
|
||||
it("should still emit a sensible diff for Write of a brand-new file (type: create)", async () => {
|
||||
const toolUseCache: ToolUseCache = {};
|
||||
const waitingForToolCall: WaitingForToolCall = new Map();
|
||||
const hookUpdates: any[] = [];
|
||||
const mockClientWithUpdate = {
|
||||
sessionUpdate: async (notification: any) => {
|
||||
@@ -1311,6 +1371,7 @@ describe("Bash terminal output", () => {
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClientWithUpdate,
|
||||
mockLogger,
|
||||
);
|
||||
@@ -1368,6 +1429,7 @@ describe("Bash terminal output", () => {
|
||||
};
|
||||
|
||||
const toolUseCache: ToolUseCache = {};
|
||||
const waitingForToolCall: WaitingForToolCall = new Map();
|
||||
|
||||
// Capture session updates sent by the hook callback
|
||||
const hookUpdates: any[] = [];
|
||||
@@ -1389,6 +1451,7 @@ describe("Bash terminal output", () => {
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClientWithUpdate,
|
||||
mockLogger,
|
||||
{ clientCapabilities },
|
||||
@@ -1418,6 +1481,7 @@ describe("Bash terminal output", () => {
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClientWithUpdate,
|
||||
mockLogger,
|
||||
{ clientCapabilities },
|
||||
@@ -1469,6 +1533,7 @@ describe("Bash terminal output", () => {
|
||||
|
||||
it("should not include terminal _meta in hook update when client lacks terminal_output support", async () => {
|
||||
const toolUseCache: ToolUseCache = {};
|
||||
const waitingForToolCall: WaitingForToolCall = new Map();
|
||||
|
||||
const hookUpdates: any[] = [];
|
||||
const mockClientWithUpdate = {
|
||||
@@ -1490,6 +1555,7 @@ describe("Bash terminal output", () => {
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClientWithUpdate,
|
||||
mockLogger,
|
||||
// No clientCapabilities — terminal_output not supported
|
||||
@@ -1514,6 +1580,7 @@ describe("Bash terminal output", () => {
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClientWithUpdate,
|
||||
mockLogger,
|
||||
);
|
||||
@@ -1645,6 +1712,7 @@ describe("toAcpNotifications - TodoWrite with undefined input regression", () =>
|
||||
|
||||
it("should not throw when TodoWrite tool_use has undefined input", () => {
|
||||
const toolUseCache: ToolUseCache = {};
|
||||
const waitingForToolCall: WaitingForToolCall = new Map();
|
||||
|
||||
const notifications = toAcpNotifications(
|
||||
[
|
||||
@@ -1658,6 +1726,7 @@ describe("toAcpNotifications - TodoWrite with undefined input regression", () =>
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
);
|
||||
@@ -1669,6 +1738,7 @@ describe("toAcpNotifications - TodoWrite with undefined input regression", () =>
|
||||
|
||||
it("should still emit plan update when TodoWrite has valid input", () => {
|
||||
const toolUseCache: ToolUseCache = {};
|
||||
const waitingForToolCall: WaitingForToolCall = new Map();
|
||||
|
||||
const notifications = toAcpNotifications(
|
||||
[
|
||||
@@ -1682,6 +1752,7 @@ describe("toAcpNotifications - TodoWrite with undefined input regression", () =>
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
);
|
||||
@@ -1780,6 +1851,7 @@ describe("toAcpNotifications - Task* tools", () => {
|
||||
|
||||
it("suppresses tool_call for TaskCreate/TaskUpdate/TaskList/TaskGet on tool_use", () => {
|
||||
const toolUseCache: ToolUseCache = {};
|
||||
const waitingForToolCall: WaitingForToolCall = new Map();
|
||||
const taskState: TaskState = new Map();
|
||||
|
||||
const notifications = toAcpNotifications(
|
||||
@@ -1797,6 +1869,7 @@ describe("toAcpNotifications - Task* tools", () => {
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
{ taskState },
|
||||
@@ -1808,6 +1881,7 @@ describe("toAcpNotifications - Task* tools", () => {
|
||||
|
||||
it("emits a plan snapshot after a TaskCreate tool_result and accumulates state", () => {
|
||||
const toolUseCache: ToolUseCache = {};
|
||||
const waitingForToolCall: WaitingForToolCall = new Map();
|
||||
const taskState: TaskState = new Map();
|
||||
|
||||
toAcpNotifications(
|
||||
@@ -1822,6 +1896,7 @@ describe("toAcpNotifications - Task* tools", () => {
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
{ taskState },
|
||||
@@ -1839,6 +1914,7 @@ describe("toAcpNotifications - Task* tools", () => {
|
||||
"user",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
{ taskState },
|
||||
@@ -1863,6 +1939,7 @@ describe("toAcpNotifications - Task* tools", () => {
|
||||
"assistant",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
{ taskState },
|
||||
@@ -1880,6 +1957,7 @@ describe("toAcpNotifications - Task* tools", () => {
|
||||
"user",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
{ taskState },
|
||||
@@ -1896,6 +1974,7 @@ describe("toAcpNotifications - Task* tools", () => {
|
||||
|
||||
it("emits a plan snapshot reflecting status changes after a TaskUpdate tool_result", () => {
|
||||
const toolUseCache: ToolUseCache = {};
|
||||
const waitingForToolCall: WaitingForToolCall = new Map();
|
||||
const taskState: TaskState = new Map([["1", { subject: "First", status: "pending" as const }]]);
|
||||
toolUseCache["update-1"] = {
|
||||
type: "tool_use",
|
||||
@@ -1920,6 +1999,7 @@ describe("toAcpNotifications - Task* tools", () => {
|
||||
"user",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
{ taskState },
|
||||
@@ -1937,6 +2017,7 @@ describe("toAcpNotifications - Task* tools", () => {
|
||||
"list-1": { type: "tool_use", id: "list-1", name: "TaskList", input: {} },
|
||||
"get-1": { type: "tool_use", id: "get-1", name: "TaskGet", input: { taskId: "1" } },
|
||||
};
|
||||
const waitingForToolCall: WaitingForToolCall = new Map();
|
||||
const taskState: TaskState = new Map([
|
||||
["1", { subject: "Existing", status: "in_progress" as const }],
|
||||
]);
|
||||
@@ -1949,6 +2030,7 @@ describe("toAcpNotifications - Task* tools", () => {
|
||||
"user",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
{ taskState },
|
||||
@@ -1967,6 +2049,7 @@ describe("toAcpNotifications - Task* tools", () => {
|
||||
input: { subject: "A", description: "" },
|
||||
},
|
||||
};
|
||||
const waitingForToolCall: WaitingForToolCall = new Map();
|
||||
const taskState: TaskState = new Map();
|
||||
|
||||
const notifications = toAcpNotifications(
|
||||
@@ -1981,6 +2064,7 @@ describe("toAcpNotifications - Task* tools", () => {
|
||||
"user",
|
||||
"test-session",
|
||||
toolUseCache,
|
||||
waitingForToolCall,
|
||||
mockClient,
|
||||
mockLogger,
|
||||
{ taskState },
|
||||
|
||||
Reference in New Issue
Block a user