mirror of
https://github.com/agentclientprotocol/claude-agent-acp.git
synced 2026-10-27 12:40:32 +00:00
feat: update to Claude Code 2.1.309 (#1413)
Updates `@anthropic-ai/claude-agent-sdk` from 0.3.303 to **0.3.309** (Claude Code **2.1.309**), `@agentclientprotocol/sdk` 2.0.0 → 2.0.1, `@anthropic-ai/sdk` 0.135.0 → 1.1.0 and typescript-eslint 8.71.2 → 8.72.0. TypeScript stays on 6.0.3. typescript-eslint requires `typescript <6.1.0` and has no TS 7 support yet (typescript-eslint/typescript-eslint#10940), so we wait for that rather than running two TypeScript versions side by side. ## Uses new SDK features - **`/usage` recommendations (0.3.305):** our structured `/usage` Markdown now lists Claude Code's per-window suggestions (`- **41%** Disable my_server · /mcp`) above the MCP server table, in the order Claude Code sends them. `fix` is not rendered: its messages must be queued at priority `later` with `client_composed` set, which an ACP prompt cannot express. A malformed recommendation makes the response fail validation, so we fall back to Claude Code's own `/usage` text. - **`wakeup_source` on `command_lifecycle` "started" (0.3.307):** when a ScheduleWakeup or another scheduled task starts a turn with no prompt open, the async-task (AIR) "Claude is working" task now says `A scheduled wakeup fired (set for <UTC time>).` or `A scheduled task fired.` instead of `Claude Code started a turn without a prompt.` It works whether the frame arrives before or after `session_state_changed: running`; older CLIs and unknown values behave as before. ## Fixes for changes in the new CLI - **Fork transcripts (2.1.307):** the placeholder `tool_result` text in a fork's copied prefix changed (`Fork started — that fork is you.` / `This is the parent's call, not yours. …`). `stripForkPrefix` matches only the directive text, so behaviour is unchanged; its doc comment and the replay fixture now describe both formats. ## Behaviour changes ACP users may notice - **2.1.304:** tool search is on by default behind a custom `ANTHROPIC_BASE_URL` (our `providers/set` gateway sessions), and a gateway that rejects it is retried without it (2.1.305). - **2.1.304:** `rm -r` of a mounted drive or a top-level folder on it (`/Volumes/…`, `/media/…`, `/mnt/c/…`) now asks for permission, even in bypass mode, without an always-allow option. `CLAUDE_CODE_DISABLE_MOUNTED_DRIVE_RM_PROMPT` turns it off. - **2.1.309:** with `sandbox.enabled` but no runnable sandbox, commands in `sandbox.excludedCommands` now ask before writing to the sandbox's write-list paths, and the unsandboxed fallback is refused. - **2.1.304:** `allowedMcpServers` no longer filters `managed-mcp.json` stdio servers that use `${VAR}` only in `env`. ## Upstream fixes this picks up - Model switches, prompts and interrupts no longer wait behind our background `getContextUsage` call (2.1.304); `setModel()` waits up to 15 s for the API to confirm a model (2.1.307). - A PreToolUse hook's deny reason is no longer replaced by a timeout of our `Edit|Write` preview callback hook (2.1.304). - Host-supplied `managedSettings` no longer drop `disableAllHooks`, `disableSkillShellExecution` and empty HTTP hook lists (0.3.309). - Gateway fixes: `context_management` rejections are retried without the field (2.1.304); no `prompt-caching-scope` header behind custom gateways (2.1.307). - A disabled MCP server is no longer reconnected by a later permission prompt in SDK sessions; HTTP MCP tool calls are no longer sent twice when the connection drops (2.1.307/2.1.309). - Bash permission and sandbox hardening (2.1.309), fewer "prompt is too long" and autocompact-thrashing errors, and faster resume of large compacted sessions. ## Checked, no change needed - **ACP SDK 2.0.1** only changes the client-side `createHttpStream` transport, which we don't use. - **`@anthropic-ai/sdk` 1.x** (API version 2026-10-21, Bearer auth, sampling params removed): we only use its types, none of which changed in a way that affects us. Neither our `dist/` nor the Agent SDK runtime imports it, and the bundled CLI carries its own client. It is Apache-2.0 from 1.0.0 (previously MIT). - **`sed -i` (2.1.309)** runs as plain Bash; SDK sessions already did, since `_simulatedSedEdit` only exists in the TUI preview. - The remaining type changes (`liveReply`, context-usage row `id`s, `pluginId`/`path` on agents and skills, new settings keys) are additive and unused. Verified: `npm run build`, `npm run check`, `npm run test:run` (3277 passed, 30 skipped), and a typecheck of `examples/`.
This commit is contained in:
214
package-lock.json
generated
214
package-lock.json
generated
@@ -9,8 +9,8 @@
|
||||
"version": "0.94.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "2.0.0",
|
||||
"@anthropic-ai/claude-agent-sdk": "0.3.303",
|
||||
"@agentclientprotocol/sdk": "2.0.1",
|
||||
"@anthropic-ai/claude-agent-sdk": "0.3.309",
|
||||
"diff": "9.0.0",
|
||||
"zod": "4.6.7"
|
||||
},
|
||||
@@ -18,12 +18,12 @@
|
||||
"claude-agent-acp": "dist/index.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anthropic-ai/sdk": "0.135.0",
|
||||
"@anthropic-ai/sdk": "1.1.0",
|
||||
"@eslint/js": "10.0.1",
|
||||
"@tsconfig/node22": "22.0.6",
|
||||
"@types/node": "26.6.5",
|
||||
"@typescript-eslint/eslint-plugin": "8.71.2",
|
||||
"@typescript-eslint/parser": "8.71.2",
|
||||
"@typescript-eslint/eslint-plugin": "8.72.0",
|
||||
"@typescript-eslint/parser": "8.72.0",
|
||||
"ajv": "8.20.0",
|
||||
"ajv-formats": "3.0.1",
|
||||
"eslint": "10.13.0",
|
||||
@@ -39,31 +39,31 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@agentclientprotocol/sdk": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@agentclientprotocol/sdk/-/sdk-2.0.0.tgz",
|
||||
"integrity": "sha512-Za5gWOPQ44aBOdegDJZ+OjCAPn7POc0cw8BlFJQ1mmUY1OoUWxRi3+QZT4gdZnyEWgKXyfeGRb0Oo1ag9YkQ1w==",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@agentclientprotocol/sdk/-/sdk-2.0.1.tgz",
|
||||
"integrity": "sha512-v4E/NcBTGFEKmorGso8ysAjJTkNh5UMJPP4aDznyMNmnP9t10g/aCCo/Djy7RYaYZy5ZROFK3G5cEj9tDgGOTQ==",
|
||||
"license": "Apache-2.0",
|
||||
"peerDependencies": {
|
||||
"zod": "^3.25.76 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk": {
|
||||
"version": "0.3.303",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.303.tgz",
|
||||
"integrity": "sha512-XBql04V40xr+fmGJh8AmIpqRZKFNrs99BzXzzSVlx+uE5NjFCzKmkNKTdMD0DQtDvFJlijOLoh1siaH+au9Psw==",
|
||||
"version": "0.3.309",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.309.tgz",
|
||||
"integrity": "sha512-Tou0fXBkxKJDcgT09v3Iv/Jd7bNgapgfAiHxGPctAADPFQhidfhZbcv7at1cVL0Lon5sbs+OzP/pEOigw2TI0A==",
|
||||
"license": "SEE LICENSE IN README.md",
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.303",
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.303",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.303",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.303",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.303",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.303",
|
||||
"@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.303",
|
||||
"@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.303"
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.309",
|
||||
"@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.309",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.309",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.309",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.309",
|
||||
"@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.309",
|
||||
"@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.309",
|
||||
"@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.309"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@anthropic-ai/sdk": ">=0.93.0",
|
||||
@@ -72,9 +72,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-darwin-arm64": {
|
||||
"version": "0.3.303",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.303.tgz",
|
||||
"integrity": "sha512-QAWV9Zd7i7FQDaUSwZNVXJirqCS9CSF4aPwbkGwLvLH83qcfFOmnyxWUg1H7dU6tJFLOCyDLQzc3xzUA4dakoA==",
|
||||
"version": "0.3.309",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.309.tgz",
|
||||
"integrity": "sha512-qC93iDGEioaXqWsYazcZzNv3hd8d4VHkCFgb+AzvbkCWOzZHnRu1EqF4Iz5kCTLDs4VdMvCa1NkzlUwtTedBVw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -85,9 +85,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-darwin-x64": {
|
||||
"version": "0.3.303",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.303.tgz",
|
||||
"integrity": "sha512-o931musaryuq04fMaEjceCEmgsBrLh39WvwsQWmv070h9jXS8EmWRFpdSILv1xrUFXiukRSq8yUDRVzg+FvTug==",
|
||||
"version": "0.3.309",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.309.tgz",
|
||||
"integrity": "sha512-RvP0UOcYxn42c1lgByoMufHaMOH86U2ThGGFupOxBKATY4Q6WJd6M1CukQ/qygfgJ8lcxvK8GOOd3SIny4kQ8Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -98,12 +98,15 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64": {
|
||||
"version": "0.3.303",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.303.tgz",
|
||||
"integrity": "sha512-c04i9i1BU3j9G3D7XdVzXTdM3/sDpQH+1o8jNcMPnztOTYbNFmH/cacgeX9x3N112r+5VhCEHXGILoe5GxVxeg==",
|
||||
"version": "0.3.309",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.309.tgz",
|
||||
"integrity": "sha512-+8rQwzycuXTW2e/C+yotvP/SDiEDHxItdPvIriGAf2ixh7nPRjVuzHOj1tamgVO0J8nmpPvIAO4edzJ7VZCcAg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -111,12 +114,15 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64-musl": {
|
||||
"version": "0.3.303",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.303.tgz",
|
||||
"integrity": "sha512-a/OsLTEx9ly9hC26cXidg9mtTMA00SQnEiTN9P4IPNj64uSp/7uBnlWl0VeUoVFbgKYYq6XrGRcD6F1ilQsYxg==",
|
||||
"version": "0.3.309",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.309.tgz",
|
||||
"integrity": "sha512-14ZD9hdUeobw+jr166YvCZyZwSYG3hDWi5S4yQxpSCrRuY/1z4Kl1AfymUlugiN3UT4Q1rif32K6GnjRJBvQdA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -124,12 +130,15 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-linux-x64": {
|
||||
"version": "0.3.303",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.303.tgz",
|
||||
"integrity": "sha512-lsm1sX2+9Wos8glBbn86M8leFuK60LKB1cJdS0b2HiTSwiDCqpUKYRGJNUlW1JdUTwJB5nDv6S8/FOJExm1/tw==",
|
||||
"version": "0.3.309",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.309.tgz",
|
||||
"integrity": "sha512-96G8/uTArlvTTg0qGtiP36W76Ug/YT0gMBh8KX7J2RigdOTrk6QkHtUeCzrw6yff5hEIzecN7EWNw6uh9USo4A==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -137,12 +146,15 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-linux-x64-musl": {
|
||||
"version": "0.3.303",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.303.tgz",
|
||||
"integrity": "sha512-yixAa3ZOZd1hiI4mpoukzT9TnuDCzSEHyh7pHcbYTRmdRE2y8bAOmI0eiMFJPivnG4iyGhS2C4XNAe6hAjl30w==",
|
||||
"version": "0.3.309",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.309.tgz",
|
||||
"integrity": "sha512-9ViOt4MGMiE2h8V+JUPR5l9F0ZTFuhKC3jawIL/MrMfYY1m2Gt3gcMuP7h7YK914a4tmpXrmITWrpTOu1WOZQQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -150,9 +162,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-win32-arm64": {
|
||||
"version": "0.3.303",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.303.tgz",
|
||||
"integrity": "sha512-AnEseiw5E1JF3XRaGIZN880V/i2OxCaTOQJF9F1+oELleSdvLnCUxCE6/baGvaZjrgvffO9YbYChBpgUKj9j7A==",
|
||||
"version": "0.3.309",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.309.tgz",
|
||||
"integrity": "sha512-lDsmw5hguDF7KNMG2OvvKlG0Zgs9bKlrX9Gs2Rv/NjLVl0PIy1TUsh7byOsUR4pXwoMU1toyU1CKqBc/Au8hHw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -163,9 +175,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk-win32-x64": {
|
||||
"version": "0.3.303",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.303.tgz",
|
||||
"integrity": "sha512-j5kABFEQ6x6T5AB/glmn0r9VwZRbzcH+nSEOWglB69ODNfkZA2btjcz9r794+2BLqJ9QAyswiYWcOX4sDz/RmQ==",
|
||||
"version": "0.3.309",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.309.tgz",
|
||||
"integrity": "sha512-otGHQimz6F++6SajGqSAKBclRPGTn60cx+8u3dnhMa5yovKZ5SgChFgstFsiDIWr/ZBqpJysvFemSgHX61mlmQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -176,10 +188,10 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@anthropic-ai/sdk": {
|
||||
"version": "0.135.0",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.135.0.tgz",
|
||||
"integrity": "sha512-WWrdL57JhU5KoynpTjt9GXK6k7ud9nPw2uw/Trn+oBXh6s0KpjhAccWZm2Qxw08ARYG1UDQj5iC+JO42j1rt/g==",
|
||||
"license": "MIT",
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-1.1.0.tgz",
|
||||
"integrity": "sha512-Op/fKMsfVwFCfObc87leLOFlORE0RjOvLzRzPCWWLI7Ww6bobpKR6YmYC8ZYgulRh+m2a/3/61VUXoWHFtCXIw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@opentelemetry/api": "^1.4.1",
|
||||
"json-schema-to-ts": "^3.1.1",
|
||||
@@ -919,17 +931,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.71.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.71.2.tgz",
|
||||
"integrity": "sha512-K72/pvT7qpm8q3k8G5u93NCSN1RcLxX5h7Gw0bQpbMXviBHeY6Nx1WEwmk9wP+eKTSQ8ecxvcgt/ZX/rB938xQ==",
|
||||
"version": "8.72.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.72.0.tgz",
|
||||
"integrity": "sha512-aysKgysPX9XOv/dk95UkvUDe++Q3n3dJPG9CEWT3RUccxfkPJ+Qn6nhKPDhZXU6Ofo9kgwGWwKyQCnZIXTgngg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.12.2",
|
||||
"@typescript-eslint/scope-manager": "8.71.2",
|
||||
"@typescript-eslint/type-utils": "8.71.2",
|
||||
"@typescript-eslint/utils": "8.71.2",
|
||||
"@typescript-eslint/visitor-keys": "8.71.2",
|
||||
"@typescript-eslint/scope-manager": "8.72.0",
|
||||
"@typescript-eslint/type-utils": "8.72.0",
|
||||
"@typescript-eslint/utils": "8.72.0",
|
||||
"@typescript-eslint/visitor-keys": "8.72.0",
|
||||
"ignore": "^7.0.5",
|
||||
"natural-compare": "^1.4.0",
|
||||
"ts-api-utils": "^2.5.0"
|
||||
@@ -942,22 +954,22 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/parser": "^8.71.2",
|
||||
"@typescript-eslint/parser": "^8.72.0",
|
||||
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
|
||||
"typescript": ">=4.8.4 <6.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.71.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.71.2.tgz",
|
||||
"integrity": "sha512-aTtmZjp/x3gsGHkDs04/1I8kNogBJkpQaeGaN0PfiG43NzDEmW2n+OP5im9wfsvyX0yP1UZWBrAGtDBfH9jEVw==",
|
||||
"version": "8.72.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.72.0.tgz",
|
||||
"integrity": "sha512-0O+x3ynciD6zuXb1CKSu165haQV6ey53DKhHHUnujP7U+XlltJG+/keT0Xhd1HXCze+IHf7Tvt39ovyepX/LNw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.71.2",
|
||||
"@typescript-eslint/types": "8.71.2",
|
||||
"@typescript-eslint/typescript-estree": "8.71.2",
|
||||
"@typescript-eslint/visitor-keys": "8.71.2",
|
||||
"@typescript-eslint/scope-manager": "8.72.0",
|
||||
"@typescript-eslint/types": "8.72.0",
|
||||
"@typescript-eslint/typescript-estree": "8.72.0",
|
||||
"@typescript-eslint/visitor-keys": "8.72.0",
|
||||
"debug": "^4.4.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -973,14 +985,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.71.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.71.2.tgz",
|
||||
"integrity": "sha512-Pd68GsLVh5ZXlcEccFPdhSOvtHZ/2WU0XsA8zmXOS3F158lVlcTLhfQ6qvtL1RzbMie0Kp8xtbVwAg4nejKPHw==",
|
||||
"version": "8.72.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.72.0.tgz",
|
||||
"integrity": "sha512-bzHngBbmIgzj6TsTjVUCntGx6FFLgE7mPjpO0PzHycOaZHXqS43XnrkFIHRS16LLr/xO6FHSXG3GJFo/F4bSBQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.71.2",
|
||||
"@typescript-eslint/types": "^8.71.2",
|
||||
"@typescript-eslint/tsconfig-utils": "^8.72.0",
|
||||
"@typescript-eslint/types": "^8.72.0",
|
||||
"debug": "^4.4.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -995,14 +1007,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.71.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.71.2.tgz",
|
||||
"integrity": "sha512-YF4wgg5iUX5Obywoac27B56FhozMNFhdJSbh11H5GBAwmBtfyRhmgTQqW9tRNNmHDvsZ2CX6UGzoz+YfYn2Leg==",
|
||||
"version": "8.72.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.72.0.tgz",
|
||||
"integrity": "sha512-JewPyJOYcXBSAS7PIgufscHOwZqhTGyBRiQJhhuDln66tMchxvvt7nJ90JKKIoOelmdHA3kAQbm9cEUHc4JSEA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.71.2",
|
||||
"@typescript-eslint/visitor-keys": "8.71.2"
|
||||
"@typescript-eslint/types": "8.72.0",
|
||||
"@typescript-eslint/visitor-keys": "8.72.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -1013,9 +1025,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.71.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.71.2.tgz",
|
||||
"integrity": "sha512-jz49TAWokm/7yWD9/o837nYax9DyALxIPOImdImvVzo6canBCZYzin8zJyqNR5k1j9K1DE0bVvr0XYB+99uAdg==",
|
||||
"version": "8.72.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.72.0.tgz",
|
||||
"integrity": "sha512-0vbxCf7xLVPSjcOdm6eQbgViH8EDJ8xnl1laLT9yoqSz2YmcSfSg5Vdl0JUvZf6rcuJ0ZivJIuMD4edTg8obaA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -1030,15 +1042,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.71.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.71.2.tgz",
|
||||
"integrity": "sha512-cC8UaalCViJkb3rH/xLIvidEwoFRtMZMUqDjjJMGEKaTFwYRHYBcoczlEGHuvkHEO8svRDrgZ0yQgOArFmwang==",
|
||||
"version": "8.72.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.72.0.tgz",
|
||||
"integrity": "sha512-gEXxmmbSTSOVTC37IYYvremOM31v+UN2W772bJxmVYPhAOPSUxtacyb7wkm1EhkYKU6KzFJsdU44DCV4to0LWw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.71.2",
|
||||
"@typescript-eslint/typescript-estree": "8.71.2",
|
||||
"@typescript-eslint/utils": "8.71.2",
|
||||
"@typescript-eslint/types": "8.72.0",
|
||||
"@typescript-eslint/typescript-estree": "8.72.0",
|
||||
"@typescript-eslint/utils": "8.72.0",
|
||||
"debug": "^4.4.3",
|
||||
"ts-api-utils": "^2.5.0"
|
||||
},
|
||||
@@ -1055,9 +1067,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.71.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.71.2.tgz",
|
||||
"integrity": "sha512-y5Ukl3oVp7e/VGxqIoEXoeb/Roj71PAJ4WG/N/RGj+eeDvKRLM15kGr9MijLbN0i03S52zxu31Z98E5gWChJKw==",
|
||||
"version": "8.72.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.72.0.tgz",
|
||||
"integrity": "sha512-Adfoy59kxNEjT5E5fnyrp7GpHYuWs8ONbdohu4ez6IU46qDq8LltG90tLmkucZB2uhYL8mqBjtXqGd8yhfjXqQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -1069,16 +1081,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.71.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.71.2.tgz",
|
||||
"integrity": "sha512-DgNOEsl4pdYzwSL+N/Cl33tX1Hlw3FvId73LnKXfq7jnrF2viOEn7X+P7RBkkSWCVm92gnWd28GIC5vJuLREeA==",
|
||||
"version": "8.72.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.72.0.tgz",
|
||||
"integrity": "sha512-gwLfFgWcw15tN2KXDCJvxEOQjPn+P3j/n3eSqkZ5sX4VpW9Cosodr+tWwBZRvJT3TcKx6HnPRGZMCNAiP1Xhug==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.71.2",
|
||||
"@typescript-eslint/tsconfig-utils": "8.71.2",
|
||||
"@typescript-eslint/types": "8.71.2",
|
||||
"@typescript-eslint/visitor-keys": "8.71.2",
|
||||
"@typescript-eslint/project-service": "8.72.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.72.0",
|
||||
"@typescript-eslint/types": "8.72.0",
|
||||
"@typescript-eslint/visitor-keys": "8.72.0",
|
||||
"debug": "^4.4.3",
|
||||
"minimatch": "^10.2.2",
|
||||
"semver": "^7.7.3",
|
||||
@@ -1097,16 +1109,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.71.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.71.2.tgz",
|
||||
"integrity": "sha512-RyKa1G1M9rKHDhrk+HJtiF1MvLD5Ux2JW3TSkzw+02pRALdpJb+QzG1Y5em+LvCgzGwgg8H7mBRFKljNz7ImPw==",
|
||||
"version": "8.72.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.72.0.tgz",
|
||||
"integrity": "sha512-0up3oArJZBYnzOaEXKo8DmI43QvMZyPoFEt9LWzRJcyl/RcJjfKKPebknN1VbTgXBf3M/KggUh5uUMdURLnseg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.9.1",
|
||||
"@typescript-eslint/scope-manager": "8.71.2",
|
||||
"@typescript-eslint/types": "8.71.2",
|
||||
"@typescript-eslint/typescript-estree": "8.71.2"
|
||||
"@typescript-eslint/scope-manager": "8.72.0",
|
||||
"@typescript-eslint/types": "8.72.0",
|
||||
"@typescript-eslint/typescript-estree": "8.72.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -1121,13 +1133,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.71.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.71.2.tgz",
|
||||
"integrity": "sha512-zozlN0UA32bSG2zkKAgQyRYSbEISVoyU6vvkKYo2aSny74D8Jh4e+POkJ6Lk0dlezZi5x7TtD+88Xb06IB8nhg==",
|
||||
"version": "8.72.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.72.0.tgz",
|
||||
"integrity": "sha512-E2mroviasaWecDzTGiGMtuEguuLVOeRggnrIn93CJuxYJzQBHbyymlT08aVQydbBal5538o+JkBKXh2sRuCyIA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.71.2",
|
||||
"@typescript-eslint/types": "8.72.0",
|
||||
"eslint-visitor-keys": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
10
package.json
10
package.json
@@ -65,18 +65,18 @@
|
||||
"node": ">=22"
|
||||
},
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "2.0.0",
|
||||
"@anthropic-ai/claude-agent-sdk": "0.3.303",
|
||||
"@agentclientprotocol/sdk": "2.0.1",
|
||||
"@anthropic-ai/claude-agent-sdk": "0.3.309",
|
||||
"diff": "9.0.0",
|
||||
"zod": "4.6.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anthropic-ai/sdk": "0.135.0",
|
||||
"@anthropic-ai/sdk": "1.1.0",
|
||||
"@eslint/js": "10.0.1",
|
||||
"@tsconfig/node22": "22.0.6",
|
||||
"@types/node": "26.6.5",
|
||||
"@typescript-eslint/eslint-plugin": "8.71.2",
|
||||
"@typescript-eslint/parser": "8.71.2",
|
||||
"@typescript-eslint/eslint-plugin": "8.72.0",
|
||||
"@typescript-eslint/parser": "8.72.0",
|
||||
"ajv": "8.20.0",
|
||||
"ajv-formats": "3.0.1",
|
||||
"eslint": "10.13.0",
|
||||
|
||||
@@ -5876,9 +5876,28 @@ export class ClaudeAcpAgent {
|
||||
// turn activation and settlement stay driven by echoes/results/idle.
|
||||
// (Raw-mode emission above still forwards these frames.)
|
||||
if ((message as { type: string }).type === "command_lifecycle") {
|
||||
const frame = message as unknown as { command_uuid: string; state: string };
|
||||
const frame = message as unknown as {
|
||||
command_uuid: string;
|
||||
state: string;
|
||||
wakeup_source?: string;
|
||||
scheduled_for?: number;
|
||||
};
|
||||
switch (frame.state) {
|
||||
case "started": {
|
||||
// CLIs 2.1.307+ (capability lifecycle_wakeup_source_v1) mark a
|
||||
// command that a scheduler fire started. With no prompt of ours
|
||||
// open, it starts an autonomous turn: name the fire as its cause,
|
||||
// whether `session_state_changed` already opened the task or not.
|
||||
const cause = scheduledFireCause(frame);
|
||||
if (
|
||||
cause &&
|
||||
asyncTasks.enabled &&
|
||||
!session.cancelled &&
|
||||
!session.activeTurn &&
|
||||
!firstUnsettledQueuedTurn()
|
||||
) {
|
||||
await asyncTasks.autonomousTurnStarted(cause);
|
||||
}
|
||||
// Remember dispatch on the live turn so a cancel() that orphans
|
||||
// it seeds the right state (see Turn.commandStarted)...
|
||||
const queued = findUnsettledTurn(frame.command_uuid);
|
||||
@@ -13973,3 +13992,29 @@ function getMatchingModelUsage(modelUsage: Record<string, ModelUsage>, currentMo
|
||||
return { key: bestKey, usage: modelUsage[bestKey] };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* What a `command_lifecycle` "started" frame says started the command: a
|
||||
* ScheduleWakeup (dynamic `/loop`) wakeup, with the time it was set for in
|
||||
* epoch milliseconds, or another scheduled task such as a cron job. Undefined
|
||||
* for any other command, and for a value this adapter does not know. The time
|
||||
* is given in UTC because the agent does not know the client's time zone.
|
||||
*/
|
||||
function scheduledFireCause(frame: {
|
||||
wakeup_source?: string;
|
||||
scheduled_for?: number;
|
||||
}): string | undefined {
|
||||
switch (frame.wakeup_source) {
|
||||
case "loop_wakeup": {
|
||||
const scheduledFor =
|
||||
typeof frame.scheduled_for === "number" ? new Date(frame.scheduled_for) : undefined;
|
||||
return scheduledFor && !Number.isNaN(scheduledFor.getTime())
|
||||
? `A scheduled wakeup fired (set for ${scheduledFor.toISOString()}).`
|
||||
: "A scheduled wakeup fired.";
|
||||
}
|
||||
case "schedule_wakeup":
|
||||
return "A scheduled task fired.";
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -458,18 +458,25 @@ export class AsyncTaskRuntime {
|
||||
* example to answer a task notification. ACP v1 has no session state, and
|
||||
* the client already got the stop reason of its last prompt, so this task is
|
||||
* the only sign that the agent works. The tool calls of the turn are in the
|
||||
* transcript already, so the task is not shown there. Does nothing while
|
||||
* such a task is open.
|
||||
* transcript already, so the task is not shown there.
|
||||
*
|
||||
* [cause] names what started the turn when Claude Code says so, for example
|
||||
* "A scheduled wakeup fired." It replaces the description of a task that is
|
||||
* already open; without it, an open task is left as it is.
|
||||
*/
|
||||
async autonomousTurnStarted(): Promise<void> {
|
||||
if (!this.enabled || this.autonomousTurn) return;
|
||||
async autonomousTurnStarted(cause?: string): Promise<void> {
|
||||
if (!this.enabled) return;
|
||||
if (this.autonomousTurn) {
|
||||
if (cause) await this.publishProgress(this.autonomousTurn, { description: cause });
|
||||
return;
|
||||
}
|
||||
const task = this.task(`autonomous-turn-${randomUUID()}`);
|
||||
task.autonomousTurn = true;
|
||||
task.startedObserved = true;
|
||||
task.name = "Claude is working";
|
||||
task.taskType = "turn";
|
||||
task.description =
|
||||
this.lastNotificationSummary ?? "Claude Code started a turn without a prompt.";
|
||||
cause ?? this.lastNotificationSummary ?? "Claude Code started a turn without a prompt.";
|
||||
task.showInTranscript = false;
|
||||
this.lastNotificationSummary = undefined;
|
||||
this.autonomousTurn = task;
|
||||
|
||||
@@ -292,9 +292,13 @@ function isForkDirectiveMessage(message: { type?: unknown; message?: unknown }):
|
||||
* The transcript of a `subagent_type: "fork"` child without the context it
|
||||
* inherited. A fork transcript starts with a copy of the parent's launching
|
||||
* assistant message (its text, thinking and every tool_use, including the
|
||||
* Agent call itself), then a user message with a "Fork started — processing
|
||||
* in background" tool_result for each of those tool_uses and the text
|
||||
* `<fork-boilerplate>…</fork-boilerplate>\nYour directive: <prompt>`. A fork
|
||||
* Agent call itself), then a user message with a placeholder tool_result for
|
||||
* each of those tool_uses and the text
|
||||
* `<fork-boilerplate>…</fork-boilerplate>\nYour directive: <prompt>`. Since
|
||||
* Claude Code 2.1.307 the fork's own Agent call gets "Fork started — that fork
|
||||
* is you." and the other tool_uses get "This is the parent's call, not
|
||||
* yours. …"; before that, every one got "Fork started — processing in
|
||||
* background". Only the directive text is matched here. A fork
|
||||
* launched from inside a subagent also gets a copy of that subagent's history
|
||||
* before them. None of it is the child's own work: replayed as is, the
|
||||
* parent's text and tool calls would show again in the child.
|
||||
|
||||
@@ -2269,6 +2269,57 @@ describe("usage Markdown", () => {
|
||||
expect(formatted).not.toContain("lowest");
|
||||
});
|
||||
|
||||
it("renders each window's recommendations in order, escaped, before its MCP table", () => {
|
||||
const formatted = formatUsageResponse({
|
||||
...usageResponse,
|
||||
behaviors: {
|
||||
...usageResponse.behaviors,
|
||||
day: {
|
||||
...usageResponse.behaviors.day,
|
||||
recommendations: [
|
||||
{ key: "mcp_server", pct: 41, title: "Disable my_server", detail: "/mcp" },
|
||||
{ key: "effort", pct: 12, title: "Lower effort | max" },
|
||||
],
|
||||
fix: { keys: ["mcp_server"], messages: ["/mcp disable my_server"] },
|
||||
},
|
||||
week: {
|
||||
...usageResponse.behaviors.week,
|
||||
mcp_servers: [],
|
||||
recommendations: [{ key: "agent", pct: 5, title: "Trim the reviewer agent" }],
|
||||
},
|
||||
},
|
||||
});
|
||||
const day = formatted.slice(
|
||||
formatted.indexOf("**Last 24h**"),
|
||||
formatted.indexOf("**Last 7d**"),
|
||||
);
|
||||
expect(day).toContain(
|
||||
"- **41%** Disable my\\_server · /mcp\n- **12%** Lower effort \\| max\n\n| MCP server |",
|
||||
);
|
||||
expect(formatted).not.toContain("undefined");
|
||||
expect(formatted).not.toContain("/mcp disable");
|
||||
expect(
|
||||
formatted.endsWith(
|
||||
"**Last 7d** · 43 requests · 5 sessions\n\n- **5%** Trim the reviewer agent",
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("falls back to the CLI text for a malformed recommendation", () => {
|
||||
expect(
|
||||
parseUsageResponse({
|
||||
...usageResponse,
|
||||
behaviors: {
|
||||
...usageResponse.behaviors,
|
||||
day: {
|
||||
...usageResponse.behaviors.day,
|
||||
recommendations: [{ key: "effort", pct: "high", title: "Lower effort" }],
|
||||
},
|
||||
},
|
||||
}),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it("recognizes only exact local usage commands", () => {
|
||||
expect(isUsageCommandText(" /usage ")).toBe(true);
|
||||
expect(isUsageCommandText("/cost")).toBe(false);
|
||||
@@ -3837,7 +3888,8 @@ describe("subagent transcript replay", () => {
|
||||
it("replays a fork child without the launching message and boilerplate it inherited", async () => {
|
||||
// A `subagent_type: "fork"` transcript starts with a copy of the
|
||||
// launching message (same API message id) and a directive message with
|
||||
// a "Fork started" result for each of its tool_uses.
|
||||
// a placeholder result for each of its tool_uses, worded as Claude Code
|
||||
// 2.1.307+ writes them.
|
||||
const launching = [
|
||||
{ type: "text", text: "I'll fork a worker." },
|
||||
toolUse("toolu_fork", "Agent", {
|
||||
@@ -3850,7 +3902,15 @@ describe("subagent transcript replay", () => {
|
||||
const forkStarted = (id: string) => ({
|
||||
type: "tool_result",
|
||||
tool_use_id: id,
|
||||
content: [{ type: "text", text: "Fork started — processing in background" }],
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text:
|
||||
id === "toolu_fork"
|
||||
? "Fork started — that fork is you."
|
||||
: "This is the parent's call, not yours. It may not have run yet, and its result goes to the parent.",
|
||||
},
|
||||
],
|
||||
});
|
||||
const trace = await replayRfd(
|
||||
[
|
||||
@@ -24163,6 +24223,93 @@ describe("deferred settlement for live background subagents (issues #864/#866)",
|
||||
]);
|
||||
});
|
||||
|
||||
describe("started by a scheduler fire", () => {
|
||||
const lifecycleStarted = (fire: { wakeup_source?: string; scheduled_for?: number }) =>
|
||||
({
|
||||
type: "command_lifecycle",
|
||||
command_uuid: "scheduler-fire",
|
||||
state: "started",
|
||||
...fire,
|
||||
uuid: randomUUID(),
|
||||
session_id: "test-session",
|
||||
}) as any;
|
||||
|
||||
const autonomousTurn = async (fireFrames: (afterRunning: boolean) => any[]) => {
|
||||
const { agent, asyncTaskUpdates } = await airAgent();
|
||||
let wake = () => {};
|
||||
const woken = new Promise<void>((resolve) => (wake = resolve));
|
||||
injectGeneratorSession(agent, (input) => {
|
||||
async function* messageGenerator() {
|
||||
const iter = input[Symbol.asyncIterator]();
|
||||
yield userEcho((await iter.next()).value);
|
||||
yield running();
|
||||
yield resultMessage();
|
||||
yield idle();
|
||||
await woken;
|
||||
yield* fireFrames(false);
|
||||
yield running();
|
||||
yield* fireFrames(true);
|
||||
yield assistantText("woke up");
|
||||
yield resultMessage();
|
||||
yield idle();
|
||||
}
|
||||
return messageGenerator();
|
||||
});
|
||||
await agent.prompt({ sessionId: "test-session", prompt: [{ type: "text", text: "go" }] });
|
||||
wake();
|
||||
await agent.sessions["test-session"]?.consumer;
|
||||
return asyncTaskUpdates()
|
||||
.filter((update) => update.sessionUpdate !== "async_task_state_update")
|
||||
.map(({ sessionUpdate, description }) => ({ sessionUpdate, description }));
|
||||
};
|
||||
|
||||
it("names a ScheduleWakeup fire that arrives before the running state", async () => {
|
||||
const updates = await autonomousTurn((afterRunning) =>
|
||||
afterRunning
|
||||
? []
|
||||
: [
|
||||
lifecycleStarted({
|
||||
wakeup_source: "loop_wakeup",
|
||||
scheduled_for: 1_790_000_000_000,
|
||||
}),
|
||||
],
|
||||
);
|
||||
expect(updates).toEqual([
|
||||
{
|
||||
sessionUpdate: "async_task_spawned",
|
||||
description: "A scheduled wakeup fired (set for 2026-09-21T14:13:20.000Z).",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("renames the open task for a scheduled task fire after the running state", async () => {
|
||||
const updates = await autonomousTurn((afterRunning) =>
|
||||
afterRunning ? [lifecycleStarted({ wakeup_source: "schedule_wakeup" })] : [],
|
||||
);
|
||||
expect(updates).toEqual([
|
||||
{
|
||||
sessionUpdate: "async_task_spawned",
|
||||
description: "Claude Code started a turn without a prompt.",
|
||||
},
|
||||
{ sessionUpdate: "async_task_progress", description: "A scheduled task fired." },
|
||||
]);
|
||||
});
|
||||
|
||||
it("leaves the task as before for a started frame without a known wakeup source", async () => {
|
||||
const updates = await autonomousTurn((afterRunning) =>
|
||||
afterRunning
|
||||
? [lifecycleStarted({}), lifecycleStarted({ wakeup_source: "future_source" })]
|
||||
: [],
|
||||
);
|
||||
expect(updates).toEqual([
|
||||
{
|
||||
sessionUpdate: "async_task_spawned",
|
||||
description: "Claude Code started a turn without a prompt.",
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it("ends the turn task when a prompt is folded into the turn", async () => {
|
||||
const { agent, updates, asyncTaskUpdates } = await airAgent();
|
||||
let wake = () => {};
|
||||
|
||||
@@ -41,7 +41,8 @@ const launch = (uuid: string, launchId: string) => ({
|
||||
});
|
||||
/**
|
||||
* The directive message that Claude Code writes after the copy of the launching
|
||||
* message in a `subagent_type: "fork"` child's transcript.
|
||||
* message in a `subagent_type: "fork"` child's transcript, with the tool_result
|
||||
* text of Claude Code before 2.1.307.
|
||||
*/
|
||||
const forkDirective = (uuid: string, launchIds: string[], prompt: string) => ({
|
||||
...base,
|
||||
|
||||
@@ -8,10 +8,18 @@ const usageWindowSchema = z
|
||||
.nullable()
|
||||
.optional();
|
||||
const contributionSchema = z.object({ name: z.string(), pct: percentSchema });
|
||||
// Claude Code 2.1.305+ suggests one change per notable source, largest share first.
|
||||
const recommendationSchema = z.object({
|
||||
key: z.string(),
|
||||
pct: percentSchema,
|
||||
title: z.string(),
|
||||
detail: z.string().optional(),
|
||||
});
|
||||
const behaviorPeriodSchema = z.object({
|
||||
request_count: countSchema,
|
||||
session_count: countSchema,
|
||||
mcp_servers: z.array(contributionSchema),
|
||||
recommendations: z.array(recommendationSchema).optional(),
|
||||
});
|
||||
const modelUsageSchema = z.object({
|
||||
inputTokens: countSchema,
|
||||
@@ -133,12 +141,23 @@ function appendContributions(
|
||||
request_count: number;
|
||||
session_count: number;
|
||||
mcp_servers: { name: string; pct: number }[];
|
||||
recommendations?: { pct: number; title: string; detail?: string }[];
|
||||
},
|
||||
): void {
|
||||
lines.push(
|
||||
"",
|
||||
`**${label}** · ${period.request_count} requests · ${period.session_count} sessions`,
|
||||
);
|
||||
// Rendered in the order Claude Code sends them, as its own `/usage` does. The
|
||||
// window's `fix` is left out: its messages must be queued at priority "later"
|
||||
// with `client_composed` set, which an ACP prompt cannot express.
|
||||
if (period.recommendations?.length) {
|
||||
lines.push("");
|
||||
for (const recommendation of period.recommendations) {
|
||||
const detail = recommendation.detail ? ` · ${escapeMarkdown(recommendation.detail)}` : "";
|
||||
lines.push(`- **${recommendation.pct}%** ${escapeMarkdown(recommendation.title)}${detail}`);
|
||||
}
|
||||
}
|
||||
if (period.mcp_servers.length === 0) return;
|
||||
lines.push("", "| MCP server | Usage |", "|:--|--:|");
|
||||
for (const server of [...period.mcp_servers].sort((a, b) => b.pct - a.pct).slice(0, 3)) {
|
||||
|
||||
Reference in New Issue
Block a user