From f664ced76df00674d88aedd018a4261da3bc3f3a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Jul 2026 13:07:01 +0200 Subject: [PATCH] feat(deps): update to @anthropic-ai/claude-agent-sdk 0.3.205 (#854) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the minor group with 12 updates: | Package | From | To | | --- | --- | --- | | [@anthropic-ai/claude-agent-sdk](https://github.com/anthropics/claude-agent-sdk-typescript) | `0.3.202` | `0.3.204` | | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `26.1.0` | `26.1.1` | | @anthropic-ai/claude-agent-sdk-darwin-arm64 | `0.3.202` | `0.3.204` | | @anthropic-ai/claude-agent-sdk-darwin-x64 | `0.3.202` | `0.3.204` | | @anthropic-ai/claude-agent-sdk-linux-arm64 | `0.3.202` | `0.3.204` | | @anthropic-ai/claude-agent-sdk-linux-arm64-musl | `0.3.202` | `0.3.204` | | @anthropic-ai/claude-agent-sdk-linux-x64 | `0.3.202` | `0.3.204` | | @anthropic-ai/claude-agent-sdk-linux-x64-musl | `0.3.202` | `0.3.204` | | @anthropic-ai/claude-agent-sdk-win32-arm64 | `0.3.202` | `0.3.204` | | @anthropic-ai/claude-agent-sdk-win32-x64 | `0.3.202` | `0.3.204` | | [rolldown](https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown) | `1.1.4` | `1.1.5` | | [std-env](https://github.com/unjs/std-env) | `4.1.0` | `4.2.0` | Updates `@anthropic-ai/claude-agent-sdk` from 0.3.202 to 0.3.204
Release notes

Sourced from @​anthropic-ai/claude-agent-sdk's releases.

v0.3.204

What's changed

Update

npm install @anthropic-ai/claude-agent-sdk@0.3.204
# or
yarn add @anthropic-ai/claude-agent-sdk@0.3.204
# or
pnpm add @anthropic-ai/claude-agent-sdk@0.3.204
# or
bun add @anthropic-ai/claude-agent-sdk@0.3.204

v0.3.203

What's changed

Update

npm install @anthropic-ai/claude-agent-sdk@0.3.203
# or
yarn add @anthropic-ai/claude-agent-sdk@0.3.203
# or
pnpm add @anthropic-ai/claude-agent-sdk@0.3.203
# or
bun add @anthropic-ai/claude-agent-sdk@0.3.203
Changelog

Sourced from @​anthropic-ai/claude-agent-sdk's changelog.

0.3.204

0.3.203

Commits

Updates `@types/node` from 26.1.0 to 26.1.1
Commits

Updates `@anthropic-ai/claude-agent-sdk-darwin-arm64` from 0.3.202 to 0.3.204 Updates `@anthropic-ai/claude-agent-sdk-darwin-x64` from 0.3.202 to 0.3.204 Updates `@anthropic-ai/claude-agent-sdk-linux-arm64` from 0.3.202 to 0.3.204 Updates `@anthropic-ai/claude-agent-sdk-linux-arm64-musl` from 0.3.202 to 0.3.204 Updates `@anthropic-ai/claude-agent-sdk-linux-x64` from 0.3.202 to 0.3.204 Updates `@anthropic-ai/claude-agent-sdk-linux-x64-musl` from 0.3.202 to 0.3.204 Updates `@anthropic-ai/claude-agent-sdk-win32-arm64` from 0.3.202 to 0.3.204 Updates `@anthropic-ai/claude-agent-sdk-win32-x64` from 0.3.202 to 0.3.204 Updates `rolldown` from 1.1.4 to 1.1.5
Release notes

Sourced from rolldown's releases.

v1.1.5

[1.1.5] - 2026-07-08

🚀 Features

🐛 Bug Fixes

🚜 Refactor

... (truncated)

Changelog

Sourced from rolldown's changelog.

[1.1.5] - 2026-07-08

🚀 Features

🐛 Bug Fixes

🚜 Refactor

... (truncated)

Commits

Updates `std-env` from 4.1.0 to 4.2.0
Release notes

Sourced from std-env's releases.

v4.2.0

🚀 Enhancements

🩹 Fixes

❤️Contributors

Changelog

Sourced from std-env's changelog.

v4.2.0

compare changes

🚀 Enhancements

🩹 Fixes

🏡 Chore

❤️ Contributors

Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
--------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ben Brandt --- package-lock.json | 230 ++++++++++++++++++------------------ package.json | 4 +- src/acp-agent.ts | 49 ++++++-- src/sdk-shims.d.ts | 38 ------ src/tests/acp-agent.test.ts | 139 ++++++++++++++++++++++ 5 files changed, 296 insertions(+), 164 deletions(-) delete mode 100644 src/sdk-shims.d.ts diff --git a/package-lock.json b/package-lock.json index e881db9a..ae7ae5fb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "Apache-2.0", "dependencies": { "@agentclientprotocol/sdk": "1.2.1", - "@anthropic-ai/claude-agent-sdk": "0.3.202", + "@anthropic-ai/claude-agent-sdk": "0.3.205", "zod": "^3.25.0 || ^4.0.0" }, "bin": { @@ -20,7 +20,7 @@ "@anthropic-ai/sdk": "0.110.0", "@eslint/js": "10.0.1", "@tsconfig/node22": "22.0.5", - "@types/node": "26.1.0", + "@types/node": "26.1.1", "@typescript-eslint/eslint-plugin": "8.63.0", "@typescript-eslint/parser": "8.63.0", "eslint": "10.6.0", @@ -45,22 +45,22 @@ } }, "node_modules/@anthropic-ai/claude-agent-sdk": { - "version": "0.3.202", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.202.tgz", - "integrity": "sha512-LnaLxDtsZP7J6g++xRSnnpTX7CHNe4v+cvBRIlD2ar+N+xi0aqY2YDaCsxPsl+haVUB9kqlUMd0zosmwsfTGjQ==", + "version": "0.3.205", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.205.tgz", + "integrity": "sha512-ft6iBw9kXudsusiXNpeybIPBJ07Z3tqp1ROSg5cEJqgA+9i+JJj2sRfQth+QD+lyenbbAU8yPieLxIimvfBhtw==", "license": "SEE LICENSE IN README.md", "engines": { "node": ">=18.0.0" }, "optionalDependencies": { - "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.202", - "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.202", - "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.202", - "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.202", - "@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.202", - "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.202", - "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.202", - "@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.202" + "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.205", + "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.205", + "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.205", + "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.205", + "@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.205", + "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.205", + "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.205", + "@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.205" }, "peerDependencies": { "@anthropic-ai/sdk": ">=0.93.0", @@ -69,9 +69,9 @@ } }, "node_modules/@anthropic-ai/claude-agent-sdk-darwin-arm64": { - "version": "0.3.202", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.202.tgz", - "integrity": "sha512-ujR3zDthDPkZs+AxW95iHpqLT5cuwGImsS3mVxLt1DlDij4qeTnihLX8+EpQTK+oNW9jjvFA86yKwa84fa1KYA==", + "version": "0.3.205", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.205.tgz", + "integrity": "sha512-lrfJ4eVtzfPkCpbSkBOGSMQCBbvmW6nbPzgHE4IwMN3scZlpuFMUFqh2aaJa/X2SAcWD9H2S0t2WWvSRgM7BjA==", "cpu": [ "arm64" ], @@ -82,9 +82,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-darwin-x64": { - "version": "0.3.202", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.202.tgz", - "integrity": "sha512-s/RVSGgkVmIMfyt1ndR8braLLu82bARoijmt1kk8d4IptUZ0Sc+zNUWKoFXwR9XqDBu6rBbBF9RIzD02raT57w==", + "version": "0.3.205", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.205.tgz", + "integrity": "sha512-G6ETPmL5mNzJ2DFsWxG3jmsmrXgZX1N2ZCJvxaGUUpjTsKZJ4Tup1cWYvcd/m7o5fYZmx9REmgzTwsAIc1fdPQ==", "cpu": [ "x64" ], @@ -95,9 +95,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64": { - "version": "0.3.202", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.202.tgz", - "integrity": "sha512-a4YtRkgGYt3ogePJDW8Ts6bNW690jb9LHyZaiWXsi+zT53xCNqJB2zKPyRc7hXWOqzIk4nCfwJpjmhLzMu3WIg==", + "version": "0.3.205", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.205.tgz", + "integrity": "sha512-CXzySK3PV3EizCRPXnxPqeaAtgrBFDnMFOVpMe36oC3U16yDb1b1tAJGqZi/7uFrVvAiaXvnSFxhUWnDDSaO+A==", "cpu": [ "arm64" ], @@ -111,9 +111,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64-musl": { - "version": "0.3.202", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.202.tgz", - "integrity": "sha512-abSb3Gah45kUNyOeKjmQ/dd1KZ4CaQz5JAr9YQxRDXoOwx8wJVx6huBIpDxjms9wyS9X5Rqxn0Lx7zFP+wV2zQ==", + "version": "0.3.205", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.205.tgz", + "integrity": "sha512-91fgdG4aTnQ29sKOcUqgH4+tKCW2ut6PWGRSYmXNDbROasJm1rAlPdzC5brdu/e4c0CDSNV6TWyE5JCjaS/jlQ==", "cpu": [ "arm64" ], @@ -127,9 +127,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-linux-x64": { - "version": "0.3.202", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.202.tgz", - "integrity": "sha512-XIvhdCWAAT4OdOA82fOJII+WH0Tf8pFckckEbJMMmOgQBKOnHT+609Pd3Ehw6zGcA9iFrhG5mY8Ncuckeo1aMw==", + "version": "0.3.205", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.205.tgz", + "integrity": "sha512-siS+1iNqBSlGFZZvJY6+mhzZ/6/ec/TbX9GMuwmTF0E6fxGhIIp797jJxR1q8r6FAq7d39mEoRNhC0Ffo60uNQ==", "cpu": [ "x64" ], @@ -143,9 +143,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-linux-x64-musl": { - "version": "0.3.202", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.202.tgz", - "integrity": "sha512-fze5nAQL1ErcMCQNB10ILaWdM0QbJSaTQzBz8NVAy0FGW8ZL0t4Wf/VgFkfzXbfkaxmPuM1C27Dn5HiU7UDEHQ==", + "version": "0.3.205", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.205.tgz", + "integrity": "sha512-vvsb7GlnA8CTSVvvTkrXjcSeRKqxSM7p/tU3Od9ICAZeWHglptekEyzLEApzLuLbI5ewfFF/F0q3NwOBbo18dg==", "cpu": [ "x64" ], @@ -159,9 +159,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-win32-arm64": { - "version": "0.3.202", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.202.tgz", - "integrity": "sha512-N1J0HRvC+8a69bqNY7+ENIYQzR0i7s+rOIGH5XtuLxvLqOnZO8LHxWEZOe8ezabGq5eZqphSCgL6vQnQQpNh+A==", + "version": "0.3.205", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.205.tgz", + "integrity": "sha512-SpP5zF68weFez/6pKrGzq/UVAJDMDNphWqmkLfOpWTDBL5xy6XlIZw5Bl4EXoVnfi2VLFkwuffNeFe+9SdX7kw==", "cpu": [ "arm64" ], @@ -172,9 +172,9 @@ ] }, "node_modules/@anthropic-ai/claude-agent-sdk-win32-x64": { - "version": "0.3.202", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.202.tgz", - "integrity": "sha512-ytLGEC1fjTSiVSoXukS+j9G+06Mi20NSzxxzlG6uE75SEB0+17tHdWUaHqd8PhH/6GPzcYx81czxWQl1MVbq4Q==", + "version": "0.3.205", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.205.tgz", + "integrity": "sha512-kg2kkXyeSoFLruO3Ic2IruLxzBR0xCUtmlJHdWi3SYW7JhAKNJg4fcrdJsWcardmEw23Y2UDGDJbRyxqSVx6wg==", "cpu": [ "x64" ], @@ -544,9 +544,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.138.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.138.0.tgz", - "integrity": "sha512-1a7ZKmrRTCoN1XMZ4L0PyyqrMnrNlLyPuOkdSX2MZg7IiIGRUyurNhAm73ptDOraoBcIordsIGKNPKUzy3ZmfA==", + "version": "0.139.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz", + "integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==", "dev": true, "license": "MIT", "funding": { @@ -554,9 +554,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.4.tgz", - "integrity": "sha512-EZLpf/8y7GXkkra90ML47kzik/GMP3EMcE9bPyHmRfxLC6z9+aW5A8poCsoxjrT5GfEcNAAvWwUHjvP1pUQkfw==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz", + "integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==", "cpu": [ "arm64" ], @@ -571,9 +571,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.4.tgz", - "integrity": "sha512-aUi+HBvmYb7j8krl1+qJgkG8C17fO79gk3c+jPw4S8glRFc1DTija9S3EyaTSQUm5GJXYKDAsugBEhFHH2vYiQ==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz", + "integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==", "cpu": [ "arm64" ], @@ -588,9 +588,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.4.tgz", - "integrity": "sha512-F7hHC3gwY11+vByKPRWqwGbeXWVgKmL+pTGCinaEhdihzBV2aQ0fvZOch9cXYUOKuKKq429HeYXOqQLc7wFCEg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz", + "integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==", "cpu": [ "x64" ], @@ -605,9 +605,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.4.tgz", - "integrity": "sha512-sI5yw+7s92SK6odiEhD5lKCBlWcpjHS5qyqpVQbZAJ0fIzEUXrmbl3DH2ybR3PZogulNJF+COLtmA8hUfvkCCQ==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz", + "integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==", "cpu": [ "x64" ], @@ -622,9 +622,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.4.tgz", - "integrity": "sha512-mCi0OKgEieFircrtVYmQAFGszRtMnZ6fpZAXrxanXAu7lqZcsK1E1RAaZNG0uKAnxox3B1f4EyQNnoyMfN1vAA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz", + "integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==", "cpu": [ "arm" ], @@ -639,9 +639,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.4.tgz", - "integrity": "sha512-B9Ial3Kv5sh0SHnB1g/QWcUQCEvCF6QKGAl4zXypYj65mVI+B4AhFBwPtSN7pDrJeIx8Z7zdy4ntx+wQABom7w==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz", + "integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==", "cpu": [ "arm64" ], @@ -659,9 +659,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.4.tgz", - "integrity": "sha512-lZVym0PuHE1KZ22gmFTC15lAkrg9iTszR617oYRB/iPY1A56ywoJzVKOJBKaot5RiikCObmur6pogpse3gRcng==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz", + "integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==", "cpu": [ "arm64" ], @@ -679,9 +679,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.4.tgz", - "integrity": "sha512-t2DNiLJWNTbnEHyUzTumldML6ET4/g16467LZoDDJ3tSxGvguL5/NyC2lCsNKuyRycg9XeDQF5SSv+TNOhQEXg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz", + "integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==", "cpu": [ "ppc64" ], @@ -699,9 +699,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.4.tgz", - "integrity": "sha512-0WIRnL1Uw4BvTZRLQt+PVgo6ZKTJadlC2btP+/EOXv2f/DWbY0rEgl+y834mIVwP1FkTlWVTrGGJXf12lru7EQ==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz", + "integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==", "cpu": [ "s390x" ], @@ -719,9 +719,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.4.tgz", - "integrity": "sha512-JWtGshGfX+oENAKonoNkqEJX+7hC8yfhi9GUyPX1VX4mdh1y5r+ZiJLR5XzAB0aoP6s/PcILsGjKq8O0mm24bw==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz", + "integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==", "cpu": [ "x64" ], @@ -739,9 +739,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.4.tgz", - "integrity": "sha512-rT6yQcxUuXs4CnbofqwHRRV0iem349rLMYpTjkgQGLjrY4ado/eDzwPZPTCgTOlF6Nkp8NEv70yLMTn6qkWxsQ==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz", + "integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==", "cpu": [ "x64" ], @@ -759,9 +759,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.4.tgz", - "integrity": "sha512-KXMGoboq5cyaCQjDA4GLuRiOwBQ0EyFnJoVViLeZ45/3rFItRODEr+NdsBcVpll40hhNArlm/speWGRvj08LzA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz", + "integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==", "cpu": [ "arm64" ], @@ -776,9 +776,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.4.tgz", - "integrity": "sha512-5K83rb36oJiY7BCyE9zLZtGcPV4g5wvq+xwdO0XPIwDVZI8cyB/AUjkNXGb92/rnmezEkjMOpgY61rtwjQtFwg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz", + "integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==", "cpu": [ "wasm32" ], @@ -795,9 +795,9 @@ } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.4.tgz", - "integrity": "sha512-PnWBtw3TV5KOg69HQQDR0mnQuyCmSGR2pAB4DC1rPF808fgKeTUMj2EOEyKATpgiuxuR5APQmiDO7PDgEjTFSA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz", + "integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==", "cpu": [ "arm64" ], @@ -812,9 +812,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.4.tgz", - "integrity": "sha512-M1lpniBePobTfsa7Ks9a199e1akxsXn+GYBUKsEzv3YFzOm1HJAMNwKI3qr0Zq+mxwx9gOZoTdP1yXRYsZUocQ==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz", + "integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==", "cpu": [ "x64" ], @@ -934,9 +934,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "26.1.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.0.tgz", - "integrity": "sha512-O0A1G3xPGy4w7AgQdAQYUlQ+BKk2Oovw8eRpofyp5KdBZULnbe+WqaOVNrm705SHphCiG4XHsACrSmPu1f+Kgw==", + "version": "26.1.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.1.tgz", + "integrity": "sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==", "dev": true, "license": "MIT", "dependencies": { @@ -3324,13 +3324,13 @@ } }, "node_modules/rolldown": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.4.tgz", - "integrity": "sha512-IjZYiLxZwpnhwhdBH2ugdTGVSdhCQUmLxLoqyjiL0JxYjyRst+5a0P3xfrTxJ5F638j4Mvvw5FAX5XE6eHpXbA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz", + "integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.138.0", + "@oxc-project/types": "=0.139.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -3340,21 +3340,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.1.4", - "@rolldown/binding-darwin-arm64": "1.1.4", - "@rolldown/binding-darwin-x64": "1.1.4", - "@rolldown/binding-freebsd-x64": "1.1.4", - "@rolldown/binding-linux-arm-gnueabihf": "1.1.4", - "@rolldown/binding-linux-arm64-gnu": "1.1.4", - "@rolldown/binding-linux-arm64-musl": "1.1.4", - "@rolldown/binding-linux-ppc64-gnu": "1.1.4", - "@rolldown/binding-linux-s390x-gnu": "1.1.4", - "@rolldown/binding-linux-x64-gnu": "1.1.4", - "@rolldown/binding-linux-x64-musl": "1.1.4", - "@rolldown/binding-openharmony-arm64": "1.1.4", - "@rolldown/binding-wasm32-wasi": "1.1.4", - "@rolldown/binding-win32-arm64-msvc": "1.1.4", - "@rolldown/binding-win32-x64-msvc": "1.1.4" + "@rolldown/binding-android-arm64": "1.1.5", + "@rolldown/binding-darwin-arm64": "1.1.5", + "@rolldown/binding-darwin-x64": "1.1.5", + "@rolldown/binding-freebsd-x64": "1.1.5", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", + "@rolldown/binding-linux-arm64-gnu": "1.1.5", + "@rolldown/binding-linux-arm64-musl": "1.1.5", + "@rolldown/binding-linux-ppc64-gnu": "1.1.5", + "@rolldown/binding-linux-s390x-gnu": "1.1.5", + "@rolldown/binding-linux-x64-gnu": "1.1.5", + "@rolldown/binding-linux-x64-musl": "1.1.5", + "@rolldown/binding-openharmony-arm64": "1.1.5", + "@rolldown/binding-wasm32-wasi": "1.1.5", + "@rolldown/binding-win32-arm64-msvc": "1.1.5", + "@rolldown/binding-win32-x64-msvc": "1.1.5" } }, "node_modules/router": { @@ -3590,9 +3590,9 @@ } }, "node_modules/std-env": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", - "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", "dev": true, "license": "MIT" }, @@ -3826,16 +3826,16 @@ } }, "node_modules/vite": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.3.tgz", - "integrity": "sha512-Ds+gBRbj0lwRO2Y5hwnUBdxSwlAve9LeRyU4sNnAr0ewW0gWF0n5bgXgUzbgZ49MV9BVUAQUFYVcDUcilUExMA==", + "version": "8.1.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.4.tgz", + "integrity": "sha512-bTT9PsdWO+MQMNG9ZXIP/qM9wGh37DFxTV/sPq9cFpHr3w4jkgef032PkAL9jAqhk3Nz8NQw3O8n6/xFkqO4QQ==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", + "picomatch": "^4.0.5", "postcss": "^8.5.16", - "rolldown": "~1.1.3", + "rolldown": "~1.1.4", "tinyglobby": "^0.2.17" }, "bin": { diff --git a/package.json b/package.json index 0e817d1c..3cf69ba9 100644 --- a/package.json +++ b/package.json @@ -64,14 +64,14 @@ }, "dependencies": { "@agentclientprotocol/sdk": "1.2.1", - "@anthropic-ai/claude-agent-sdk": "0.3.202", + "@anthropic-ai/claude-agent-sdk": "0.3.205", "zod": "^3.25.0 || ^4.0.0" }, "devDependencies": { "@anthropic-ai/sdk": "0.110.0", "@eslint/js": "10.0.1", "@tsconfig/node22": "22.0.5", - "@types/node": "26.1.0", + "@types/node": "26.1.1", "@typescript-eslint/eslint-plugin": "8.63.0", "@typescript-eslint/parser": "8.63.0", "eslint": "10.6.0", diff --git a/src/acp-agent.ts b/src/acp-agent.ts index d9522e3f..3f2a7d41 100644 --- a/src/acp-agent.ts +++ b/src/acp-agent.ts @@ -229,8 +229,11 @@ type Session = { * the SDK still runs it and emits a result with no uuid we can match. Because * the SDK processes input FIFO, those orphan results arrive (in submission * order) before the next live turn's, so skipping exactly this many leaves - * the genuine head untouched. Reset to 0 on every activation as a backstop - * against an SDK that drops queued input on interrupt (no orphan emitted). */ + * the genuine head untouched. On CLIs with the interrupt receipt, orphans + * the interrupt dropped (absent from `still_queued`) are uncounted as soon + * as the receipt arrives (see cancel()). Reset to 0 on every activation as + * a backstop against a dropped queued input this can't see (older CLIs, a + * receipt lost to a failed control round-trip). */ pendingOrphanResults?: number; /** The long-lived consumer task. Lazily started on the first `prompt()` and * kept alive for the session so between-turn/background messages are still @@ -1777,6 +1780,15 @@ export class ClaudeAcpAgent { lastRefusalExplanation = message.api_refusal_explanation ?? message.content; } break; + // `control_request_progress` only reports on side_question + // control requests, which this adapter never issues. + // `background_tasks_changed` is a level signal (the full live + // background-task set on every membership change) for surfaces + // that render a background-activity indicator; turn lifecycle + // here is driven by results/idle, so there is nothing to track. + case "control_request_progress": + case "background_tasks_changed": + break; default: unreachable(message, this.logger); break; @@ -2388,13 +2400,10 @@ export class ClaudeAcpAgent { // `conversation_reset` (from `/clear`, plan-mode exit, fresh-session // flows) is safe to drop: turn lifecycle here is driven by // results/idle, and the client owns its own transcript view. - // `control_request_progress` only reports on side_question control - // requests, which this adapter never issues. case "tool_use_summary": case "auth_status": case "prompt_suggestion": case "conversation_reset": - case "control_request_progress": break; default: unreachable(message); @@ -2456,22 +2465,22 @@ export class ClaudeAcpAgent { // they have no in-flight SDK work to interrupt. The active turn is settled // by the consumer when it observes the interrupt's trailing idle (or via the // backstop below). Mirrors the old pendingMessages cancellation. + const orphanedUuids: string[] = []; if (session.turnQueue) { - let orphaned = 0; for (const turn of session.turnQueue) { if (turn !== session.activeTurn && !turn.settled) { turn.settled = true; // Deliberately no `usage`: a queued turn never ran, so the session // accumulator (the active turn's tally) is not its spend. turn.resolve({ stopReason: "cancelled" }); - orphaned++; + orphanedUuids.push(turn.promptUuid); } } // Each removed queued turn's user message was already pushed to the SDK, // which processes input FIFO and will still emit a result for it with no // uuid to match. Count those so the consumer skips them (see // ensureActiveTurn) rather than misattributing them to the head. - session.pendingOrphanResults = (session.pendingOrphanResults ?? 0) + orphaned; + session.pendingOrphanResults = (session.pendingOrphanResults ?? 0) + orphanedUuids.length; session.turnQueue = session.turnQueue.filter( (turn) => turn === session.activeTurn && !turn.settled, ); @@ -2503,7 +2512,29 @@ export class ClaudeAcpAgent { }, this.forceCancelGraceMs); } - await session.query.interrupt(); + const receipt = await session.query.interrupt(); + // On CLIs advertising `interrupt_receipt_v1`, the receipt's `still_queued` + // lists exactly which queued messages survive the interrupt and will still + // run. An orphaned turn whose uuid is absent was dropped by the interrupt + // and will never emit a result — uncount it now instead of leaving a stale + // skip that activateTurn's reset only clears once a later live ECHO + // arrives: an echo-less result in between (a local-only command like + // `/context`) would be wrongly swallowed by the leftover count. Subtracting + // a count (rather than tracking uuids) stays race-safe against the + // consumer draining concurrently: dropped uuids produce no results, so the + // consumer's decrements only ever consume the still-queued share. Unknown + // uuids in the receipt (internally-enqueued messages) are ignored, per its + // contract. Older CLIs resolve `undefined` (guard the FIELD, not just the + // receipt, so a bare `{}` success from a gateway can't read as "everything + // was dropped") — keep the count-everything behavior and its + // activation-time self-heal. + if (Array.isArray(receipt?.still_queued) && orphanedUuids.length > 0) { + const stillQueued = new Set(receipt.still_queued); + const dropped = orphanedUuids.filter((uuid) => !stillQueued.has(uuid)).length; + if (dropped > 0) { + session.pendingOrphanResults = Math.max(0, (session.pendingOrphanResults ?? 0) - dropped); + } + } } /** Mark a session's SDK query stream as permanently ended and release the diff --git a/src/sdk-shims.d.ts b/src/sdk-shims.d.ts deleted file mode 100644 index 695542af..00000000 --- a/src/sdk-shims.d.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Shims for types the `@anthropic-ai/claude-agent-sdk` 0.3.198 `sdk.d.ts` - * references in its `SDKMessage` union but never declares - * (`SDKControlRequestProgressMessage`, `SDKConversationResetMessage`). With - * `skipLibCheck` the dangling references silently become error-`any`, which - * poisons narrowing on the whole union — every exhaustive switch over - * `SDKMessage` stops compiling. - * - * These must stay GLOBAL declarations (no top-level import/export in this - * file): unqualified names inside the SDK's module declaration fall back to - * the global scope, which is the only way a consumer can supply them. Shapes - * are transcribed from the zod schemas embedded in the bundled CLI binary. - * Delete this file once the SDK ships the declarations itself — a proper - * module-scoped declaration will shadow these. - */ - -/** Progress for a long-running client-originated control request (currently - * only `side_question`), correlated by `request_id`. */ -declare type SDKControlRequestProgressMessage = { - type: "control_request_progress"; - request_id: string; - status: "started" | "api_retry"; - attempt?: number; - max_retries?: number; - retry_delay_ms?: number; - error_status?: number | null; - uuid: import("crypto").UUID; - session_id: string; -}; - -/** Emitted by `/clear`, plan-mode exit, and fresh-session flows; the surface - * should mount a fresh transcript under `new_conversation_id`. */ -declare type SDKConversationResetMessage = { - type: "conversation_reset"; - new_conversation_id: import("crypto").UUID; - uuid: import("crypto").UUID; - session_id: string; -}; diff --git a/src/tests/acp-agent.test.ts b/src/tests/acp-agent.test.ts index 4b162580..c25befc7 100644 --- a/src/tests/acp-agent.test.ts +++ b/src/tests/acp-agent.test.ts @@ -6160,6 +6160,145 @@ describe("post-error recovery", () => { expect(compactResult.usage?.inputTokens).toBe(10); await agent.sessions["test-session"]?.consumer; }); + + it("uncounts an orphan the interrupt receipt reports dropped, so a no-echo result isn't swallowed", async () => { + // interrupt_receipt_v1 CLIs: interrupt() resolves with `still_queued` — + // the queued messages that will still run. Here the interrupt DROPS turn + // 2's queued message (still_queued: []), so its orphan result never + // arrives. Without the receipt reconciliation the stale count (1) would + // swallow the next echo-less result — the /compact below — and hang its + // prompt; activation's reset can't help because /compact never echoes. + const agent = createMockAgent(); + let release!: () => void; + const gate = new Promise((resolve) => (release = resolve)); + + injectGeneratorSession(agent, (input) => { + async function* messageGenerator() { + const iter = input[Symbol.asyncIterator](); + const u1 = await iter.next(); + yield userEcho(u1.value); // turn 1 active + await iter.next(); // turn 2's pushed message (cancelled; interrupt drops it) + await gate; // wait until the test cancels and sends /compact + yield { type: "system", subtype: "session_state_changed", state: "idle" }; // turn 1 settles cancelled + // NO orphan result for turn 2 — the interrupt dropped its queued message. + await iter.next(); // /compact's pushed message — never echoes its uuid + yield { + type: "system", + subtype: "status", + status: "compacting", + session_id: "test-session", + }; + yield createResultMessage({ subtype: "success", stop_reason: "end_turn", is_error: false }); + yield { type: "system", subtype: "session_state_changed", state: "idle" }; + } + return messageGenerator(); + }); + // New-CLI interrupt: nothing queued survives the interrupt. + agent.sessions["test-session"]!.query.interrupt = vi.fn(async () => ({ still_queued: [] })); + + const first = agent.prompt({ + sessionId: "test-session", + prompt: [{ type: "text", text: "first" }], + }); + await waitFor(() => !!agent.sessions["test-session"]?.activeTurn); + const second = agent.prompt({ + sessionId: "test-session", + prompt: [{ type: "text", text: "second" }], + }); + await waitFor(() => (agent.sessions["test-session"]?.turnQueue?.length ?? 0) >= 2); + + await agent.cancel({ sessionId: "test-session" }); // counts turn 2, then the receipt uncounts it + expect(agent.sessions["test-session"]?.pendingOrphanResults).toBe(0); + const compact = agent.prompt({ + sessionId: "test-session", + prompt: [{ type: "text", text: "/compact" }], + }); + release(); + + await expect(first).resolves.toEqual({ + stopReason: "cancelled", + usage: cancelledTurnUsage, + }); + await expect(second).resolves.toEqual({ stopReason: "cancelled" }); + const compactResult = await compact; + expect(compactResult.stopReason).toBe("end_turn"); + expect(compactResult.usage?.inputTokens).toBe(10); + await agent.sessions["test-session"]?.consumer; + }); + + it("keeps counting an orphan the interrupt receipt reports still queued", async () => { + // The receipt lists turn 2's uuid in still_queued — its message survives + // the interrupt, runs, and emits an orphan result that must still be + // skipped (not promoted onto the later /compact). + const agent = createMockAgent(); + let release!: () => void; + const gate = new Promise((resolve) => (release = resolve)); + + const orphanResult = createResultMessage({ + subtype: "success", + stop_reason: "end_turn", + is_error: false, + }); + orphanResult.usage.input_tokens = 999; + + injectGeneratorSession(agent, (input) => { + async function* messageGenerator() { + const iter = input[Symbol.asyncIterator](); + const u1 = await iter.next(); + yield userEcho(u1.value); // turn 1 active + await iter.next(); // turn 2's pushed message (cancelled, but survives the interrupt) + await gate; + yield { type: "system", subtype: "session_state_changed", state: "idle" }; // turn 1 settles cancelled + yield orphanResult; // turn 2's orphan — the kept count skips it + await iter.next(); // /compact's pushed message — never echoes its uuid + yield { + type: "system", + subtype: "status", + status: "compacting", + session_id: "test-session", + }; + yield createResultMessage({ subtype: "success", stop_reason: "end_turn", is_error: false }); + yield { type: "system", subtype: "session_state_changed", state: "idle" }; + } + return messageGenerator(); + }); + const first = agent.prompt({ + sessionId: "test-session", + prompt: [{ type: "text", text: "first" }], + }); + await waitFor(() => !!agent.sessions["test-session"]?.activeTurn); + const second = agent.prompt({ + sessionId: "test-session", + prompt: [{ type: "text", text: "second" }], + }); + await waitFor(() => (agent.sessions["test-session"]?.turnQueue?.length ?? 0) >= 2); + + // Report turn 2's queued message as surviving the interrupt. Captured + // before cancel() filters the queue (interrupt runs after the filter). + const session = agent.sessions["test-session"]!; + const survivingUuid = session.turnQueue![1].promptUuid; + session.query.interrupt = vi.fn(async () => ({ still_queued: [survivingUuid] })); + + await agent.cancel({ sessionId: "test-session" }); + expect(agent.sessions["test-session"]?.pendingOrphanResults).toBe(1); + const compact = agent.prompt({ + sessionId: "test-session", + prompt: [{ type: "text", text: "/compact" }], + }); + release(); + + await expect(first).resolves.toEqual({ + stopReason: "cancelled", + usage: cancelledTurnUsage, + }); + await expect(second).resolves.toEqual({ stopReason: "cancelled" }); + const compactResult = await compact; + expect(compactResult.stopReason).toBe("end_turn"); + // /compact settled with its OWN result, proving the surviving orphan was + // skipped rather than promoted onto it (which would leak the 999). + expect(compactResult.usage?.inputTokens).toBe(10); + await agent.sessions["test-session"]?.consumer; + }); }); describe("session/cancel wedge recovery (issue #680)", () => {