Compare commits

..

5 Commits

Author SHA1 Message Date
acp-release-bot[bot]
ea7076c0bc chore(main): release 0.73.0 (#1067)
🤖 I have created a release *beep* *boop*
---


##
[0.73.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.72.0...v0.73.0)
(2026-09-01)


### Features

* **deps:** update Claude SDK
([#1066](https://github.com/agentclientprotocol/claude-agent-acp/issues/1066))
([b4e3aaa](b4e3aaa727))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: acp-release-bot[bot] <246668977+acp-release-bot[bot]@users.noreply.github.com>
2026-09-01 22:25:37 +02:00
Andrey Bragin
b4e3aaa727 feat(deps): update Claude SDK (#1066) 2026-09-01 22:22:35 +02:00
acp-release-bot[bot]
d3eff19157 chore(main): release 0.72.0 (#1064)
🤖 I have created a release *beep* *boop*
---


##
[0.72.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.71.0...v0.72.0)
(2026-09-01)


### Features

* **deps:** Update to @anthropic-ai/claude-agent-sdk 0.3.252
([#1062](https://github.com/agentclientprotocol/claude-agent-acp/issues/1062))
([9aaf066](9aaf0663bf))


### Bug Fixes

* Adopt per-model effort settings and user_message_uuid result
attribution
([#1065](https://github.com/agentclientprotocol/claude-agent-acp/issues/1065))
([a04d354](a04d35496c))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: acp-release-bot[bot] <246668977+acp-release-bot[bot]@users.noreply.github.com>
2026-09-01 20:50:15 +02:00
Ben Brandt
a04d35496c fix: Adopt per-model effort settings and user_message_uuid result attribution (#1065)
- Seed the Effort picker from the CLI's persisted per-model effort
  (settings.modelSettings) with the legacy top-level effortLevel as
  fallback, and stop pinning settings-derived seeds at the flag layer —
  only explicit ACP picker choices pin effort now, so the CLI's own
  per-model restore survives model switches.
- Re-seed effort from the new model's persisted entry on unpinned model
  switches instead of dragging the old model's value along.
- Use the result's user_message_uuid (SDK 0.3.246+) as an exact join
key:
the empty-interruption diagnostic handler consumes exactly the token it
  names (and no longer swallows a live turn's stamped failure), and
  ensureActiveTurn consumes the named orphan / skips the dup-over-loss
one-skip when the stamp refutes it. Older producers keep the heuristics.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 18:46:59 +00:00
dependabot[bot]
9aaf0663bf feat(deps): Update to @anthropic-ai/claude-agent-sdk 0.3.252 (#1062)
Bumps the minor group with 17 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
|
[@agentclientprotocol/sdk](https://github.com/agentclientprotocol/typescript-sdk)
| `1.3.0` | `1.4.0` |
|
[@anthropic-ai/claude-agent-sdk](https://github.com/anthropics/claude-agent-sdk-typescript)
| `0.3.238` | `0.3.251` |
| [zod](https://github.com/colinhacks/zod) | `4.4.3` | `4.5.2` |
|
[@anthropic-ai/sdk](https://github.com/anthropics/anthropic-sdk-typescript)
| `0.120.0` | `0.122.0` |
|
[@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node)
| `26.2.0` | `26.4.0` |
|
[@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin)
| `8.67.0` | `8.68.0` |
|
[@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser)
| `8.67.0` | `8.68.0` |
| [eslint](https://github.com/eslint/eslint) | `10.8.1` | `10.9.1` |
|
[@jridgewell/sourcemap-codec](https://github.com/jridgewell/sourcemaps/tree/HEAD/packages/sourcemap-codec)
| `1.5.5` | `1.6.0` |
|
[express-rate-limit](https://github.com/express-rate-limit/express-rate-limit)
| `8.6.2` | `8.7.0` |
| [hono](https://github.com/honojs/hono) | `4.13.2` | `4.13.5` |
| [ip-address](https://github.com/beaugunderson/ip-address) | `10.5.0` |
`10.7.0` |
| [jose](https://github.com/panva/jose) | `6.2.9` | `6.2.10` |
| [negotiator](https://github.com/jshttp/negotiator) | `1.0.0` | `1.1.0`
|
| [picomatch](https://github.com/micromatch/picomatch) | `4.0.5` |
`4.0.7` |
|
[rolldown](https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown)
| `1.2.5` | `1.2.6` |
|
[standardwebhooks](https://github.com/standard-webhooks/standard-webhooks)
| `1.0.0` | `1.1.1` |


Updates `@agentclientprotocol/sdk` from 1.3.0 to 1.4.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/agentclientprotocol/typescript-sdk/releases">@​agentclientprotocol/sdk's
releases</a>.</em></p>
<blockquote>
<h2>v1.4.0</h2>
<h2><a
href="https://github.com/agentclientprotocol/typescript-sdk/compare/v1.3.0...v1.4.0">1.4.0</a>
(2026-08-20)</h2>
<h3>Features</h3>
<ul>
<li>Stabilize elicitation APIs (<a
href="https://redirect.github.com/agentclientprotocol/typescript-sdk/issues/242">#242</a>)
(<a
href="dc3b0e420f">dc3b0e4</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>jsonrpc:</strong> report malformed input (<a
href="https://redirect.github.com/agentclientprotocol/typescript-sdk/issues/230">#230</a>)
(<a
href="0819dbebfb">0819dbe</a>)</li>
<li><strong>server:</strong> preserve routed outbound messages (<a
href="https://redirect.github.com/agentclientprotocol/typescript-sdk/issues/231">#231</a>)
(<a
href="5f58a95a10">5f58a95</a>)</li>
<li><strong>v2:</strong> enforce initialization lifecycle (<a
href="https://redirect.github.com/agentclientprotocol/typescript-sdk/issues/235">#235</a>)
(<a
href="b7d6e81fea">b7d6e81</a>)</li>
<li><strong>v2:</strong> enforce method contracts (<a
href="https://redirect.github.com/agentclientprotocol/typescript-sdk/issues/229">#229</a>)
(<a
href="01010146a7">0101014</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/agentclientprotocol/typescript-sdk/blob/main/CHANGELOG.md">@​agentclientprotocol/sdk's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/agentclientprotocol/typescript-sdk/compare/v1.3.0...v1.4.0">1.4.0</a>
(2026-08-20)</h2>
<h3>Features</h3>
<ul>
<li>Stabilize elicitation APIs (<a
href="https://redirect.github.com/agentclientprotocol/typescript-sdk/issues/242">#242</a>)
(<a
href="dc3b0e420f">dc3b0e4</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>jsonrpc:</strong> report malformed input (<a
href="https://redirect.github.com/agentclientprotocol/typescript-sdk/issues/230">#230</a>)
(<a
href="0819dbebfb">0819dbe</a>)</li>
<li><strong>server:</strong> preserve routed outbound messages (<a
href="https://redirect.github.com/agentclientprotocol/typescript-sdk/issues/231">#231</a>)
(<a
href="5f58a95a10">5f58a95</a>)</li>
<li><strong>v2:</strong> enforce initialization lifecycle (<a
href="https://redirect.github.com/agentclientprotocol/typescript-sdk/issues/235">#235</a>)
(<a
href="b7d6e81fea">b7d6e81</a>)</li>
<li><strong>v2:</strong> enforce method contracts (<a
href="https://redirect.github.com/agentclientprotocol/typescript-sdk/issues/229">#229</a>)
(<a
href="01010146a7">0101014</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="e6463f4440"><code>e6463f4</code></a>
chore(main): release 1.4.0 (<a
href="https://redirect.github.com/agentclientprotocol/typescript-sdk/issues/237">#237</a>)</li>
<li><a
href="dc3b0e420f"><code>dc3b0e4</code></a>
feat: Stabilize elicitation APIs (<a
href="https://redirect.github.com/agentclientprotocol/typescript-sdk/issues/242">#242</a>)</li>
<li><a
href="b7d6e81fea"><code>b7d6e81</code></a>
fix(v2): enforce initialization lifecycle (<a
href="https://redirect.github.com/agentclientprotocol/typescript-sdk/issues/235">#235</a>)</li>
<li><a
href="7585334c5b"><code>7585334</code></a>
chore(deps): bump the minor group with 19 updates (<a
href="https://redirect.github.com/agentclientprotocol/typescript-sdk/issues/241">#241</a>)</li>
<li><a
href="01010146a7"><code>0101014</code></a>
fix(v2): enforce method contracts (<a
href="https://redirect.github.com/agentclientprotocol/typescript-sdk/issues/229">#229</a>)</li>
<li><a
href="e1054d0122"><code>e1054d0</code></a>
chore(deps): bump crate-ci/typos from 1.48.0 to 1.49.0 (<a
href="https://redirect.github.com/agentclientprotocol/typescript-sdk/issues/238">#238</a>)</li>
<li><a
href="611e1fea6a"><code>611e1fe</code></a>
chore(deps): bump the minor group with 17 updates (<a
href="https://redirect.github.com/agentclientprotocol/typescript-sdk/issues/239">#239</a>)</li>
<li><a
href="5ae784b010"><code>5ae784b</code></a>
chore(deps-dev): bump nanoid from 3.3.16 to 3.3.18 (<a
href="https://redirect.github.com/agentclientprotocol/typescript-sdk/issues/240">#240</a>)</li>
<li><a
href="f838f942e0"><code>f838f94</code></a>
chore: verify generated protocol sources (<a
href="https://redirect.github.com/agentclientprotocol/typescript-sdk/issues/232">#232</a>)</li>
<li><a
href="5f58a95a10"><code>5f58a95</code></a>
fix(server): preserve routed outbound messages (<a
href="https://redirect.github.com/agentclientprotocol/typescript-sdk/issues/231">#231</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/agentclientprotocol/typescript-sdk/compare/v1.3.0...v1.4.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `@anthropic-ai/claude-agent-sdk` from 0.3.238 to 0.3.251
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/anthropics/claude-agent-sdk-typescript/releases">@​anthropic-ai/claude-agent-sdk's
releases</a>.</em></p>
<blockquote>
<h2>v0.3.251</h2>
<h2>What's changed</h2>
<ul>
<li>Updated to parity with Claude Code v2.1.251</li>
</ul>
<h2>Update</h2>
<pre lang="sh"><code>npm install @anthropic-ai/claude-agent-sdk@0.3.251
# or
yarn add @anthropic-ai/claude-agent-sdk@0.3.251
# or
pnpm add @anthropic-ai/claude-agent-sdk@0.3.251
# or
bun add @anthropic-ai/claude-agent-sdk@0.3.251
</code></pre>
<h2>v0.3.250</h2>
<h2>What's changed</h2>
<ul>
<li>Updated to parity with Claude Code v2.1.250</li>
</ul>
<h2>Update</h2>
<pre lang="sh"><code>npm install @anthropic-ai/claude-agent-sdk@0.3.250
# or
yarn add @anthropic-ai/claude-agent-sdk@0.3.250
# or
pnpm add @anthropic-ai/claude-agent-sdk@0.3.250
# or
bun add @anthropic-ai/claude-agent-sdk@0.3.250
</code></pre>
<h2>v0.3.248</h2>
<h2>What's changed</h2>
<ul>
<li>Added a per-server <code>timeout</code> for SDK-hosted MCP servers
(<code>createSdkMcpServer({ timeout })</code>), overriding
<code>MCP_TOOL_TIMEOUT</code> for that server's tool calls</li>
</ul>
<h2>Update</h2>
<pre lang="sh"><code>npm install @anthropic-ai/claude-agent-sdk@0.3.248
# or
yarn add @anthropic-ai/claude-agent-sdk@0.3.248
# or
pnpm add @anthropic-ai/claude-agent-sdk@0.3.248
# or
bun add @anthropic-ai/claude-agent-sdk@0.3.248
</code></pre>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/anthropics/claude-agent-sdk-typescript/blob/main/CHANGELOG.md">@​anthropic-ai/claude-agent-sdk's
changelog</a>.</em></p>
<blockquote>
<h2>0.3.251</h2>
<ul>
<li>Updated to parity with Claude Code v2.1.251</li>
</ul>
<h2>0.3.250</h2>
<ul>
<li>Updated to parity with Claude Code v2.1.250</li>
</ul>
<h2>0.3.249</h2>
<ul>
<li>Updated to parity with Claude Code v2.1.249</li>
</ul>
<h2>0.3.248</h2>
<ul>
<li>Added a per-server <code>timeout</code> for SDK-hosted MCP servers
(<code>createSdkMcpServer({ timeout })</code>), overriding
<code>MCP_TOOL_TIMEOUT</code> for that server's tool calls</li>
</ul>
<h2>0.3.247</h2>
<ul>
<li>Added an optional <code>ambient</code> flag to
<code>task_started</code>, <code>task_notification</code> and
<code>background_tasks_changed</code> task entries so hosts can exclude
housekeeping tasks from activity indicators</li>
<li>Fixed the <code>permissionMode</code> on per-turn
<code>system/init</code> frames reporting the mode at turn start instead
of the live mode, so a mode switch right after submitting no longer
sends a stale value</li>
</ul>
<h2>0.3.246</h2>
<ul>
<li>Added optional <code>user_message_uuid</code> to error result
messages and to the first assistant message or <code>stream_event</code>
of each turn, linking a reply or failure to the user message that
triggered it</li>
<li>Added <code>modelUsage[*].costBasis</code> (<code>'list' | 'managed'
| 'unknown'</code>) reporting which price table each model's
<code>costUSD</code> was computed from</li>
<li>Added <code>modelPricing</code> support in the
<code>managedSettings</code> option for hosts that set
<code>CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST</code>; an admin-managed
settings source that sets <code>modelPricing</code> still wins</li>
<li>Added <code>perTaskStopAffordance</code> option: when set,
<code>interrupt()</code> aborts only the current turn and keeps
background agents and workflows running; otherwise (and for one-shot
string prompts) they stop</li>
</ul>
<h2>0.3.245</h2>
<ul>
<li>Updated to parity with Claude Code v2.1.245</li>
</ul>
<h2>0.3.244</h2>
<ul>
<li>Updated to parity with Claude Code v2.1.244</li>
</ul>
<h2>0.3.243</h2>
<ul>
<li>Added optional <code>queued_turn_count</code> to result messages:
the number of queued user sends still pending when the result was
produced, so hosts know whether another turn and result will follow</li>
<li>Fixed <code>mcp_status</code> reporting a remote MCP server as
connected after its connection dropped; it now reports pending while
reconnecting, then connected or failed</li>
<li>Fixed managed <code>disableAllHooks</code> also disabling hook
callbacks registered through the <code>hooks</code> option; they now
keep running, matching <code>allowManagedHooksOnly</code></li>
<li>Changed Read tool PDF results: the <code>document</code> block (or
page <code>image</code> blocks for <code>pages</code> reads) now arrives
inside the <code>tool_result</code> content instead of as a separate
<code>user</code> message after it</li>
<li>Updated to parity with Claude Code v2.1.243</li>
</ul>
<h2>0.3.242</h2>
<ul>
<li>Updated to parity with Claude Code v2.1.242</li>
</ul>
<h2>0.3.241</h2>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="75667f1f76"><code>75667f1</code></a>
chore: Update CHANGELOG.md</li>
<li><a
href="0ec415863a"><code>0ec4158</code></a>
chore: Update CHANGELOG.md</li>
<li><a
href="0518a194af"><code>0518a19</code></a>
chore: Update CHANGELOG.md</li>
<li><a
href="40b4c54986"><code>40b4c54</code></a>
chore: Update CHANGELOG.md</li>
<li><a
href="86e1856fc3"><code>86e1856</code></a>
chore: Update CHANGELOG.md</li>
<li><a
href="9d56dae014"><code>9d56dae</code></a>
chore: Update CHANGELOG.md</li>
<li><a
href="dca08feed8"><code>dca08fe</code></a>
chore: Update CHANGELOG.md</li>
<li><a
href="0ebd333363"><code>0ebd333</code></a>
chore: Update CHANGELOG.md</li>
<li><a
href="48275071e8"><code>4827507</code></a>
chore: Update CHANGELOG.md</li>
<li><a
href="bfa6b84596"><code>bfa6b84</code></a>
chore: Update CHANGELOG.md</li>
<li>Additional commits viewable in <a
href="https://github.com/anthropics/claude-agent-sdk-typescript/compare/v0.3.238...v0.3.251">compare
view</a></li>
</ul>
</details>
<br />

Updates `zod` from 4.4.3 to 4.5.2
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/colinhacks/zod/releases">zod's
releases</a>.</em></p>
<blockquote>
<h2>v4.5.2</h2>
<h2>Commits:</h2>
<ul>
<li>a354314ac04fdd5484aa62dd5c3a4b553211a0e4 fix(docs): keep blog posts
out of the docs collection (<a
href="https://redirect.github.com/colinhacks/zod/issues/6484">#6484</a>)</li>
<li>d378c42aff6869f0929058a7923cd775880f5c4c ci: drop canary publishing
from the release workflow (<a
href="https://redirect.github.com/colinhacks/zod/issues/6487">#6487</a>)</li>
<li>212b941791e7faae078e17645eb612824fd8f79a fix(v4): let a prototype
method getter answer a bare call so vi.spyOn works (<a
href="https://redirect.github.com/colinhacks/zod/issues/6488">#6488</a>)</li>
<li>e7576f542a7bc7ef3cc5eeec237714fd0e6b6e98 docs(blog): let the page
show through the navbar in dark mode (<a
href="https://redirect.github.com/colinhacks/zod/issues/6489">#6489</a>)</li>
<li>fedb06fafe33a66ce0b5c236ad2557e0a5a170fe fix(docs): match the blog
TOC hover bar to the 2px active indicator</li>
<li>6c932fcb2eea6eb671710ea058ca9fdc382ada89 chore: bump devcontainer
image to Node 24 (<a
href="https://redirect.github.com/colinhacks/zod/issues/6470">#6470</a>)</li>
<li>6635d9dd367a664109de83c021995821f48efa29 docs(blog): soften the
&quot;method memoization&quot; attribution</li>
<li>019ae299cc75daa132bf1acf59086a520abf6b85 fix(docs): drop ISR on the
docs route so the home page hydrates</li>
<li>652bb438aa4c626c1cd7948c6849c4691239fca7 chore(docs): drop the
scroll log from the route-change scroller</li>
<li>571c8e8a3d73b4305f4abfdd6977773cc12f2bf5 fix(docs): render blog tabs
with the stock fumadocs tab card</li>
<li>9a193aa24b4efa3b315b91d4c56c8bc385b8513f 4.5.2</li>
</ul>
<h2>v4.5.1</h2>
<h2>Commits:</h2>
<ul>
<li>2e862dbf89da2835e5206a8fd3d3be61afe3cf7f ci: gate the GitHub release
and JSR publish on the version being live on npm</li>
<li>8e03380510db36fa6fda979fc78a375fdea8021c 4.5.1</li>
</ul>
<h2>v4.5.0</h2>
<p>Zod 4.5 is now available.</p>
<pre lang="sh"><code>npm install zod@latest
</code></pre>
<p>At a glance:</p>
<ul>
<li><a
href="https://github.com/colinhacks/zod/blob/HEAD%5Bhttps://github.com/colinhacks/zod/blob/HEAD%60https://github.com/colinhacks/zod/blob/HEADzhttps://github.com/colinhacks/zod/blob/HEAD.https://github.com/colinhacks/zod/blob/HEADchttps://github.com/colinhacks/zod/blob/HEADohttps://github.com/colinhacks/zod/blob/HEADmhttps://github.com/colinhacks/zod/blob/HEADphttps://github.com/colinhacks/zod/blob/HEADihttps://github.com/colinhacks/zod/blob/HEADlhttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD)https://github.com/colinhacks/zod/blob/HEAD%60https://github.com/colinhacks/zod/blob/HEAD%5Dhttps://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD#https://github.com/colinhacks/zod/blob/HEADzhttps://github.com/colinhacks/zod/blob/HEADchttps://github.com/colinhacks/zod/blob/HEADohttps://github.com/colinhacks/zod/blob/HEADmhttps://github.com/colinhacks/zod/blob/HEADphttps://github.com/colinhacks/zod/blob/HEADihttps://github.com/colinhacks/zod/blob/HEADlhttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEAD)https://github.com/colinhacks/zod/blob/HEAD">https://github.com/colinhacks/zod/blob/HEAD[https://github.com/colinhacks/zod/blob/HEAD`https://github.com/colinhacks/zod/blob/HEADzhttps://github.com/colinhacks/zod/blob/HEAD.https://github.com/colinhacks/zod/blob/HEADchttps://github.com/colinhacks/zod/blob/HEADohttps://github.com/colinhacks/zod/blob/HEADmhttps://github.com/colinhacks/zod/blob/HEADphttps://github.com/colinhacks/zod/blob/HEADihttps://github.com/colinhacks/zod/blob/HEADlhttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD)https://github.com/colinhacks/zod/blob/HEAD`https://github.com/colinhacks/zod/blob/HEAD]https://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD#https://github.com/colinhacks/zod/blob/HEADzhttps://github.com/colinhacks/zod/blob/HEADchttps://github.com/colinhacks/zod/blob/HEADohttps://github.com/colinhacks/zod/blob/HEADmhttps://github.com/colinhacks/zod/blob/HEADphttps://github.com/colinhacks/zod/blob/HEADihttps://github.com/colinhacks/zod/blob/HEADlhttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEAD)https://github.com/colinhacks/zod/blob/HEAD</a>
— the flagship feature of Zod 4.5</li>
<li><a
href="https://github.com/colinhacks/zod/blob/HEAD%5Bhttps://github.com/colinhacks/zod/blob/HEAD%60https://github.com/colinhacks/zod/blob/HEADzhttps://github.com/colinhacks/zod/blob/HEAD.https://github.com/colinhacks/zod/blob/HEADchttps://github.com/colinhacks/zod/blob/HEADrhttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEADdhttps://github.com/colinhacks/zod/blob/HEADihttps://github.com/colinhacks/zod/blob/HEADthttps://github.com/colinhacks/zod/blob/HEADChttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADrhttps://github.com/colinhacks/zod/blob/HEADdhttps://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD)https://github.com/colinhacks/zod/blob/HEAD%60https://github.com/colinhacks/zod/blob/HEAD%5Dhttps://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD#https://github.com/colinhacks/zod/blob/HEADzhttps://github.com/colinhacks/zod/blob/HEADchttps://github.com/colinhacks/zod/blob/HEADrhttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEADdhttps://github.com/colinhacks/zod/blob/HEADihttps://github.com/colinhacks/zod/blob/HEADthttps://github.com/colinhacks/zod/blob/HEADchttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADrhttps://github.com/colinhacks/zod/blob/HEADdhttps://github.com/colinhacks/zod/blob/HEAD)https://github.com/colinhacks/zod/blob/HEAD">https://github.com/colinhacks/zod/blob/HEAD[https://github.com/colinhacks/zod/blob/HEAD`https://github.com/colinhacks/zod/blob/HEADzhttps://github.com/colinhacks/zod/blob/HEAD.https://github.com/colinhacks/zod/blob/HEADchttps://github.com/colinhacks/zod/blob/HEADrhttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEADdhttps://github.com/colinhacks/zod/blob/HEADihttps://github.com/colinhacks/zod/blob/HEADthttps://github.com/colinhacks/zod/blob/HEADChttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADrhttps://github.com/colinhacks/zod/blob/HEADdhttps://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD)https://github.com/colinhacks/zod/blob/HEAD`https://github.com/colinhacks/zod/blob/HEAD]https://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD#https://github.com/colinhacks/zod/blob/HEADzhttps://github.com/colinhacks/zod/blob/HEADchttps://github.com/colinhacks/zod/blob/HEADrhttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEADdhttps://github.com/colinhacks/zod/blob/HEADihttps://github.com/colinhacks/zod/blob/HEADthttps://github.com/colinhacks/zod/blob/HEADchttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADrhttps://github.com/colinhacks/zod/blob/HEADdhttps://github.com/colinhacks/zod/blob/HEAD)https://github.com/colinhacks/zod/blob/HEAD</a>
— 12–19 digits plus Luhn checksum</li>
<li><a
href="https://github.com/colinhacks/zod/blob/HEAD%5Bhttps://github.com/colinhacks/zod/blob/HEAD%60https://github.com/colinhacks/zod/blob/HEADzhttps://github.com/colinhacks/zod/blob/HEAD.https://github.com/colinhacks/zod/blob/HEADphttps://github.com/colinhacks/zod/blob/HEADrhttps://github.com/colinhacks/zod/blob/HEADohttps://github.com/colinhacks/zod/blob/HEADphttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEADrhttps://github.com/colinhacks/zod/blob/HEADthttps://github.com/colinhacks/zod/blob/HEADihttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEADshttps://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD)https://github.com/colinhacks/zod/blob/HEAD%60https://github.com/colinhacks/zod/blob/HEAD%5Dhttps://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD#https://github.com/colinhacks/zod/blob/HEADzhttps://github.com/colinhacks/zod/blob/HEADphttps://github.com/colinhacks/zod/blob/HEADrhttps://github.com/colinhacks/zod/blob/HEADohttps://github.com/colinhacks/zod/blob/HEADphttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEADrhttps://github.com/colinhacks/zod/blob/HEADthttps://github.com/colinhacks/zod/blob/HEADihttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEADshttps://github.com/colinhacks/zod/blob/HEAD)https://github.com/colinhacks/zod/blob/HEAD">https://github.com/colinhacks/zod/blob/HEAD[https://github.com/colinhacks/zod/blob/HEAD`https://github.com/colinhacks/zod/blob/HEADzhttps://github.com/colinhacks/zod/blob/HEAD.https://github.com/colinhacks/zod/blob/HEADphttps://github.com/colinhacks/zod/blob/HEADrhttps://github.com/colinhacks/zod/blob/HEADohttps://github.com/colinhacks/zod/blob/HEADphttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEADrhttps://github.com/colinhacks/zod/blob/HEADthttps://github.com/colinhacks/zod/blob/HEADihttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEADshttps://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD)https://github.com/colinhacks/zod/blob/HEAD`https://github.com/colinhacks/zod/blob/HEAD]https://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD#https://github.com/colinhacks/zod/blob/HEADzhttps://github.com/colinhacks/zod/blob/HEADphttps://github.com/colinhacks/zod/blob/HEADrhttps://github.com/colinhacks/zod/blob/HEADohttps://github.com/colinhacks/zod/blob/HEADphttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEADrhttps://github.com/colinhacks/zod/blob/HEADthttps://github.com/colinhacks/zod/blob/HEADihttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEADshttps://github.com/colinhacks/zod/blob/HEAD)https://github.com/colinhacks/zod/blob/HEAD</a>
— the multi-property counterpart to <code>z.property()</code></li>
<li><a
href="https://github.com/colinhacks/zod/blob/HEAD%5Bhttps://github.com/colinhacks/zod/blob/HEAD%60https://github.com/colinhacks/zod/blob/HEADzhttps://github.com/colinhacks/zod/blob/HEAD.https://github.com/colinhacks/zod/blob/HEADdhttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEADphttps://github.com/colinhacks/zod/blob/HEADPhttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADrhttps://github.com/colinhacks/zod/blob/HEADthttps://github.com/colinhacks/zod/blob/HEADihttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADlhttps://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD)https://github.com/colinhacks/zod/blob/HEAD%60https://github.com/colinhacks/zod/blob/HEAD%5Dhttps://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD#https://github.com/colinhacks/zod/blob/HEADzhttps://github.com/colinhacks/zod/blob/HEADdhttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEADphttps://github.com/colinhacks/zod/blob/HEADphttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADrhttps://github.com/colinhacks/zod/blob/HEADthttps://github.com/colinhacks/zod/blob/HEADihttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADlhttps://github.com/colinhacks/zod/blob/HEAD)https://github.com/colinhacks/zod/blob/HEAD/https://github.com/colinhacks/zod/blob/HEAD%5Bhttps://github.com/colinhacks/zod/blob/HEAD%60https://github.com/colinhacks/zod/blob/HEAD.https://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEADxhttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADchttps://github.com/colinhacks/zod/blob/HEADthttps://github.com/colinhacks/zod/blob/HEADPhttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADrhttps://github.com/colinhacks/zod/blob/HEADthttps://github.com/colinhacks/zod/blob/HEADihttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADlhttps://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD)https://github.com/colinhacks/zod/blob/HEAD%60https://github.com/colinhacks/zod/blob/HEAD%5Dhttps://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD#https://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEADxhttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADchttps://github.com/colinhacks/zod/blob/HEADthttps://github.com/colinhacks/zod/blob/HEADphttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADrhttps://github.com/colinhacks/zod/blob/HEADthttps://github.com/colinhacks/zod/blob/HEADihttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADlhttps://github.com/colinhacks/zod/blob/HEAD)https://github.com/colinhacks/zod/blob/HEAD">https://github.com/colinhacks/zod/blob/HEAD[https://github.com/colinhacks/zod/blob/HEAD`https://github.com/colinhacks/zod/blob/HEADzhttps://github.com/colinhacks/zod/blob/HEAD.https://github.com/colinhacks/zod/blob/HEADdhttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEADphttps://github.com/colinhacks/zod/blob/HEADPhttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADrhttps://github.com/colinhacks/zod/blob/HEADthttps://github.com/colinhacks/zod/blob/HEADihttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADlhttps://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD)https://github.com/colinhacks/zod/blob/HEAD`https://github.com/colinhacks/zod/blob/HEAD]https://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD#https://github.com/colinhacks/zod/blob/HEADzhttps://github.com/colinhacks/zod/blob/HEADdhttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEADphttps://github.com/colinhacks/zod/blob/HEADphttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADrhttps://github.com/colinhacks/zod/blob/HEADthttps://github.com/colinhacks/zod/blob/HEADihttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADlhttps://github.com/colinhacks/zod/blob/HEAD)https://github.com/colinhacks/zod/blob/HEAD/https://github.com/colinhacks/zod/blob/HEAD[https://github.com/colinhacks/zod/blob/HEAD`https://github.com/colinhacks/zod/blob/HEAD.https://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEADxhttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADchttps://github.com/colinhacks/zod/blob/HEADthttps://github.com/colinhacks/zod/blob/HEADPhttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADrhttps://github.com/colinhacks/zod/blob/HEADthttps://github.com/colinhacks/zod/blob/HEADihttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADlhttps://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD)https://github.com/colinhacks/zod/blob/HEAD`https://github.com/colinhacks/zod/blob/HEAD]https://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD#https://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEADxhttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADchttps://github.com/colinhacks/zod/blob/HEADthttps://github.com/colinhacks/zod/blob/HEADphttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADrhttps://github.com/colinhacks/zod/blob/HEADthttps://github.com/colinhacks/zod/blob/HEADihttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADlhttps://github.com/colinhacks/zod/blob/HEAD)https://github.com/colinhacks/zod/blob/HEAD</a></li>
<li><a
href="https://github.com/colinhacks/zod/blob/HEAD%5Bhttps://github.com/colinhacks/zod/blob/HEAD%60https://github.com/colinhacks/zod/blob/HEADzhttps://github.com/colinhacks/zod/blob/HEAD.https://github.com/colinhacks/zod/blob/HEADvhttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADlhttps://github.com/colinhacks/zod/blob/HEADihttps://github.com/colinhacks/zod/blob/HEADdhttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADthttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD)https://github.com/colinhacks/zod/blob/HEAD:https://github.com/colinhacks/zod/blob/HEAD">https://github.com/colinhacks/zod/blob/HEAD[https://github.com/colinhacks/zod/blob/HEAD`https://github.com/colinhacks/zod/blob/HEADzhttps://github.com/colinhacks/zod/blob/HEAD.https://github.com/colinhacks/zod/blob/HEADvhttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADlhttps://github.com/colinhacks/zod/blob/HEADihttps://github.com/colinhacks/zod/blob/HEADdhttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADthttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD)https://github.com/colinhacks/zod/blob/HEAD:https://github.com/colinhacks/zod/blob/HEAD</a>
<a
href="https://github.com/colinhacks/zod/blob/HEADbhttps://github.com/colinhacks/zod/blob/HEADohttps://github.com/colinhacks/zod/blob/HEADohttps://github.com/colinhacks/zod/blob/HEADlhttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADnhttps://github.com/colinhacks/zod/blob/HEAD%60https://github.com/colinhacks/zod/blob/HEAD%5Dhttps://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD#https://github.com/colinhacks/zod/blob/HEADzhttps://github.com/colinhacks/zod/blob/HEADvhttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADlhttps://github.com/colinhacks/zod/blob/HEADihttps://github.com/colinhacks/zod/blob/HEADdhttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADthttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEAD)https://github.com/colinhacks/zod/blob/HEAD">https://github.com/colinhacks/zod/blob/HEADbhttps://github.com/colinhacks/zod/blob/HEADohttps://github.com/colinhacks/zod/blob/HEADohttps://github.com/colinhacks/zod/blob/HEADlhttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADnhttps://github.com/colinhacks/zod/blob/HEAD`https://github.com/colinhacks/zod/blob/HEAD]https://github.com/colinhacks/zod/blob/HEAD(https://github.com/colinhacks/zod/blob/HEAD#https://github.com/colinhacks/zod/blob/HEADzhttps://github.com/colinhacks/zod/blob/HEADvhttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADlhttps://github.com/colinhacks/zod/blob/HEADihttps://github.com/colinhacks/zod/blob/HEADdhttps://github.com/colinhacks/zod/blob/HEADahttps://github.com/colinhacks/zod/blob/HEADthttps://github.com/colinhacks/zod/blob/HEADehttps://github.com/colinhacks/zod/blob/HEAD)https://github.com/colinhacks/zod/blob/HEAD</a>
— a fast-path to verify input validity without a full parse (up to 16x
faster on invalid data)</li>
<li><a
href="https://github.com/colinhacks/zod/blob/HEAD/#9x-reduction-in-schema-memory-footprint">9x
reduction in memory footprint</a></li>
<li><a href="https://zod.dev/error-customization#locales">New
locales</a>: Bengali (<code>bn</code>), Central Kurdish
(<code>ckb</code>), Hindi (<code>hi</code>), Kannada (<code>kn</code>),
Norwegian Nynorsk (<code>nn</code>), Brazilian Portuguese
(<code>pt-BR</code>), Slovak (<code>sk</code>), Turkmen
(<code>tk</code>)</li>
</ul>
<h2><code>z.compile()</code></h2>
<p>You can now pre-compile any Zod schema using
<code>z.compile(schema)</code>. This dramatically speeds up parsing
performance.</p>
<pre lang="ts"><code>import * as z from &quot;zod&quot;;
<p>const Player = z.object({
username: z.string(),
bio: z.string(),
xp: z.number(),
// ...20 more properties...
&lt;/tr&gt;&lt;/table&gt;
</code></pre></p>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="9a193aa24b"><code>9a193aa</code></a>
4.5.2</li>
<li><a
href="571c8e8a3d"><code>571c8e8</code></a>
fix(docs): render blog tabs with the stock fumadocs tab card</li>
<li><a
href="652bb438aa"><code>652bb43</code></a>
chore(docs): drop the scroll log from the route-change scroller</li>
<li><a
href="019ae299cc"><code>019ae29</code></a>
fix(docs): drop ISR on the docs route so the home page hydrates</li>
<li><a
href="6635d9dd36"><code>6635d9d</code></a>
docs(blog): soften the &quot;method memoization&quot; attribution</li>
<li><a
href="6c932fcb2e"><code>6c932fc</code></a>
chore: bump devcontainer image to Node 24 (<a
href="https://redirect.github.com/colinhacks/zod/issues/6470">#6470</a>)</li>
<li><a
href="fedb06fafe"><code>fedb06f</code></a>
fix(docs): match the blog TOC hover bar to the 2px active indicator</li>
<li><a
href="e7576f542a"><code>e7576f5</code></a>
docs(blog): let the page show through the navbar in dark mode (<a
href="https://redirect.github.com/colinhacks/zod/issues/6489">#6489</a>)</li>
<li><a
href="212b941791"><code>212b941</code></a>
fix(v4): let a prototype method getter answer a bare call so vi.spyOn
works (...</li>
<li><a
href="d378c42aff"><code>d378c42</code></a>
ci: drop canary publishing from the release workflow (<a
href="https://redirect.github.com/colinhacks/zod/issues/6487">#6487</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/colinhacks/zod/compare/v4.4.3...v4.5.2">compare
view</a></li>
</ul>
</details>
<br />

Updates `@anthropic-ai/sdk` from 0.120.0 to 0.122.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/anthropics/anthropic-sdk-typescript/releases">@​anthropic-ai/sdk's
releases</a>.</em></p>
<blockquote>
<h2>sdk: v0.122.0</h2>
<h2>0.122.0 (2026-08-27)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.121.0...sdk-v0.122.0">sdk-v0.121.0...sdk-v0.122.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> beta files/skills namespaces use GA shapes;
drop dated beta header pins (<a
href="45d693a66b">45d693a</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>client:</strong> classify cross-realm DOMException
abort/timeout errors correctly (<a
href="1bd639515e">1bd6395</a>)</li>
<li><strong>client:</strong> use configured logger for SSE parse errors
(<a
href="80eeaf7b01">80eeaf7</a>)</li>
<li><strong>sessions:</strong> make event accumulator forward-compatible
with new event types (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/410">#410</a>)
(<a
href="2d084fdaeb">2d084fd</a>)</li>
<li><strong>tools:</strong> let read return a view_range of a file over
the size cap (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/427">#427</a>)
(<a
href="56b32601f6">56b3260</a>)</li>
<li><strong>uploads:</strong> default bare Blob filename on skills
endpoints (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/422">#422</a>)
(<a
href="e1116491dd">e111649</a>)</li>
<li><strong>webhooks:</strong> require headers to be passed to
<code>unwrap()</code> (<a
href="3230804e63">3230804</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><strong>internal:</strong> codegen related update (<a
href="c2f327a30d">c2f327a</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li><strong>api:</strong> clarify pagination on the organization
rate-limit list endpoints (<a
href="d6015b90b3">d6015b9</a>)</li>
<li>document TypeScript 5.0 as the minimum supported version (<a
href="788e721793">788e721</a>)</li>
</ul>
<h2>sdk: v0.121.0</h2>
<h2>0.121.0 (2026-08-26)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.120.0...sdk-v0.121.0">sdk-v0.120.0...sdk-v0.121.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> add <code>updates</code> thinking display mode
(beta) (<a
href="22274eaf96">22274ea</a>)</li>
<li><strong>api:</strong> add missing anthropic-beta values (<a
href="0779f02666">0779f02</a>)</li>
<li><strong>api:</strong> add support for Organization API endpoints (<a
href="8b61e7a4d4">8b61e7a</a>)</li>
<li><strong>helpers:</strong> support Standard Schema for structured
outputs and tools (<a
href="788007889c">7880078</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>tools:</strong> keep the tool runner going on pause_turn (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/288">#288</a>)
(<a
href="f040dc9d39">f040dc9</a>)</li>
<li><strong>types:</strong> don't reference NodeJS.ProcessEnv from
published types (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/367">#367</a>)
(<a
href="fd38dfdc48">fd38dfd</a>)</li>
</ul>
<h3>Chores</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/anthropics/anthropic-sdk-typescript/blob/main/CHANGELOG.md">@​anthropic-ai/sdk's
changelog</a>.</em></p>
<blockquote>
<h2>0.122.0 (2026-08-27)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.121.0...sdk-v0.122.0">sdk-v0.121.0...sdk-v0.122.0</a></p>
<h3>Features</h3>
<ul>
<li>
<p><strong>api:</strong> beta files/skills namespaces use GA shapes;
drop dated beta header pins (<a
href="45d693a66b">45d693a</a>)</p>
<p>The beta Files and Skills namespaces (<code>client.beta.files</code>,
<code>client.beta.skills</code>) no longer send the
<code>files-api-2025-04-14</code> / <code>skills-2025-10-02</code>
headers and return the same shapes as <code>client.files</code> /
<code>client.skills</code> (with <code>Beta</code>-prefixed type names).
Requests that still send those headers on raw HTTP keep receiving the
beta shapes.</p>
<p>Changes in the beta namespaces:</p>
<ul>
<li><code>client.beta.skills.delete()</code> now deletes a Skill
together with all of its versions (previously refused while any version
existed). It returns <code>BetaDeletedSkill</code> (was
<code>SkillDeleteResponse</code>).</li>
<li>Beta Messages type <code>BetaSkill</code> (the <code>{type,
skill_id, version}</code> entry in <code>BetaContainer.skills</code>) is
renamed <code>BetaContainerSkill</code>; the request-side
<code>BetaSkillParams</code> keeps its name. <code>BetaSkill</code> now
names the Skill object returned by
<code>client.beta.skills.create()</code> / <code>retrieve()</code> /
<code>list()</code> (replacing <code>SkillCreateResponse</code> /
<code>SkillRetrieveResponse</code> / <code>SkillListResponse</code>),
and skill versions are <code>BetaSkillVersion</code> /
<code>BetaDeletedSkillVersion</code> (replacing
<code>Version*Response</code>).</li>
<li><code>client.beta.files.list()</code> returns a
<code>BetaFileMetadataPageCursor</code>
(<code>PageCursor&lt;BetaFileMetadata&gt;</code> with <code>data</code>
/ <code>next_page</code>) and <code>FileListParams</code> paginates with
<code>page</code> / <code>ids</code> (was
<code>BetaFileMetadataPage</code>, a
<code>Page&lt;BetaFileMetadata&gt;</code> with <code>data</code>,
<code>has_more</code>, <code>first_id</code>, <code>last_id</code> and
<code>before_id</code> / <code>after_id</code>); <code>for await</code>
auto-pagination is unchanged. <code>BetaSkill</code> uses
<code>display_name</code> (was <code>display_title</code>, also in
<code>SkillCreateParams</code>) and <code>latest_version_id</code> (was
<code>latest_version</code>), and <code>BetaSkillVersion</code> is
addressed by its <code>skver_…</code> <code>id</code> (the
Unix-timestamp <code>version</code> field is gone).</li>
</ul>
<p>Migration guides: <a
href="https://platform.claude.com/docs/en/build-with-claude/files#migrate-from-files-api-2025-04-14">Migrate
from <code>files-api-2025-04-14</code></a> · <a
href="https://platform.claude.com/docs/en/build-with-claude/skills-guide#migrate-from-skills-2025-10-02">Migrate
from <code>skills-2025-10-02</code></a></p>
</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>client:</strong> classify cross-realm DOMException
abort/timeout errors correctly (<a
href="1bd639515e">1bd6395</a>)</li>
<li><strong>client:</strong> use configured logger for SSE parse errors
(<a
href="80eeaf7b01">80eeaf7</a>)</li>
<li><strong>sessions:</strong> make event accumulator forward-compatible
with new event types (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/410">#410</a>)
(<a
href="2d084fdaeb">2d084fd</a>)</li>
<li><strong>tools:</strong> let read return a view_range of a file over
the size cap (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/427">#427</a>)
(<a
href="56b32601f6">56b3260</a>)</li>
<li><strong>uploads:</strong> default bare Blob filename on skills
endpoints (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/422">#422</a>)
(<a
href="e1116491dd">e111649</a>)</li>
<li><strong>webhooks:</strong> require headers to be passed to
<code>unwrap()</code> (<a
href="3230804e63">3230804</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><strong>internal:</strong> codegen related update (<a
href="c2f327a30d">c2f327a</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li><strong>api:</strong> clarify pagination on the organization
rate-limit list endpoints (<a
href="d6015b90b3">d6015b9</a>)</li>
<li>document TypeScript 5.0 as the minimum supported version (<a
href="788e721793">788e721</a>)</li>
</ul>
<h2>0.121.0 (2026-08-26)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.120.0...sdk-v0.121.0">sdk-v0.120.0...sdk-v0.121.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> add <code>updates</code> thinking display mode
(beta) (<a
href="22274eaf96">22274ea</a>)</li>
<li><strong>api:</strong> add missing anthropic-beta values (<a
href="0779f02666">0779f02</a>)</li>
<li><strong>api:</strong> add support for Organization API endpoints (<a
href="8b61e7a4d4">8b61e7a</a>)</li>
<li><strong>helpers:</strong> support Standard Schema for structured
outputs and tools (<a
href="788007889c">7880078</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="7f3898c525"><code>7f3898c</code></a>
chore: release main</li>
<li><a
href="956ccb55b6"><code>956ccb5</code></a>
docs(api): clarify pagination on the organization rate-limit list
endpoints</li>
<li><a
href="2131d80a5e"><code>2131d80</code></a>
fix(tools): let read return a view_range of a file over the size cap (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/427">#427</a>)</li>
<li><a
href="f830c8f561"><code>f830c8f</code></a>
fix(aws,bedrock): prefer env credentials over AWS_PROFILE (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/436">#436</a>)</li>
<li><a
href="4d50ddfb4f"><code>4d50ddf</code></a>
test(ecosystem): expect cross-realm timeout errors to classify
correctly</li>
<li><a
href="acdd669d75"><code>acdd669</code></a>
fix(client): classify cross-realm DOMException abort/timeout errors
correctly</li>
<li><a
href="0a14622ea9"><code>0a14622</code></a>
test(tools): pin that read, edit and the memory tool preserve CRLF and
CR lin...</li>
<li><a
href="17035dea97"><code>17035de</code></a>
docs: document TypeScript 5.0 as the minimum supported version</li>
<li><a
href="2f95d8a6f7"><code>2f95d8a</code></a>
fix(client): use configured logger for SSE parse errors</li>
<li><a
href="33f5103009"><code>33f5103</code></a>
feat(api): beta files/skills namespaces use GA shapes; drop dated beta
header...</li>
<li>Additional commits viewable in <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.120.0...sdk-v0.122.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `@types/node` from 26.2.0 to 26.4.0
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/eslint-plugin` from 8.67.0 to 8.68.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/eslint-plugin's
releases</a>.</em></p>
<blockquote>
<h2>v8.68.0</h2>
<h2>8.68.0 (2026-08-24)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>eslint-plugin:</strong> [strict-void-return] add fix
suggestions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12086">#12086</a>)</li>
<li><strong>utils:</strong> support ESLint rule meta.languages (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12663">#12663</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [unified-signatures] deduplicate
types in report (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12656">#12656</a>)</li>
<li><strong>eslint-plugin:</strong> [return-await] prevent autofix from
breaking code in arrow-functions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12707">#12707</a>)</li>
<li><strong>eslint-plugin:</strong> [unified-signatures] report
identical signatures (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12678">#12678</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
prevent stack overflow in recursive types (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12711">#12711</a>)</li>
<li><strong>eslint-plugin:</strong> [no-floating-promises] setting
<code>ignoreVoid: false</code> results in false negative in
ArrowFunctionExpression (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12646">#12646</a>)</li>
<li><strong>eslint-plugin:</strong> [no-empty-object-type] ignore
suggestions that result in invalid interfaces and export defaults (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12739">#12739</a>)</li>
<li><strong>website:</strong> playground crashes on <code>extends</code>
configs (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12608">#12608</a>)</li>
<li><strong>website:</strong> account for thanks.dev and out-of-band
donors in sponsors list (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12735">#12735</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Hugo <a
href="https://github.com/hugop95"><code>@​hugop95</code></a></li>
<li>Josh Goldberg ✨</li>
<li>Niki <a
href="https://github.com/phaux"><code>@​phaux</code></a></li>
<li>Thiago Barbosa</li>
<li>Younsang Na <a
href="https://github.com/nayounsang"><code>@​nayounsang</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.68.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md">@​typescript-eslint/eslint-plugin's
changelog</a>.</em></p>
<blockquote>
<h2>8.68.0 (2026-08-24)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>eslint-plugin:</strong> [strict-void-return] add fix
suggestions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12086">#12086</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-empty-object-type] ignore
suggestions that result in invalid interfaces and export defaults (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12739">#12739</a>)</li>
<li><strong>eslint-plugin:</strong> [no-floating-promises] setting
<code>ignoreVoid: false</code> results in false negative in
ArrowFunctionExpression (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12646">#12646</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
prevent stack overflow in recursive types (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12711">#12711</a>)</li>
<li><strong>eslint-plugin:</strong> [unified-signatures] report
identical signatures (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12678">#12678</a>)</li>
<li><strong>eslint-plugin:</strong> [return-await] prevent autofix from
breaking code in arrow-functions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12707">#12707</a>)</li>
<li><strong>eslint-plugin:</strong> [unified-signatures] deduplicate
types in report (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12656">#12656</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Hugo <a
href="https://github.com/hugop95"><code>@​hugop95</code></a></li>
<li>Niki <a
href="https://github.com/phaux"><code>@​phaux</code></a></li>
<li>Younsang Na <a
href="https://github.com/nayounsang"><code>@​nayounsang</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.68.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="8f4e00a4e8"><code>8f4e00a</code></a>
chore(release): publish 8.68.0</li>
<li><a
href="db30514e0f"><code>db30514</code></a>
feat(eslint-plugin): [strict-void-return] add fix suggestions (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin/issues/12086">#12086</a>)</li>
<li><a
href="9690339c4e"><code>9690339</code></a>
test(eslint-plugin): [prefer-optional-chain] use
<code>createRuleTesterWithTypes</code> ...</li>
<li><a
href="abe9011a43"><code>abe9011</code></a>
fix(eslint-plugin): [no-empty-object-type] ignore suggestions that
result in ...</li>
<li><a
href="79c16e7840"><code>79c16e7</code></a>
chore(eslint-plugin): extract FunctionSignature and enum comparison
utils (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin/issues/1">#1</a>...</li>
<li><a
href="2442e3048b"><code>2442e30</code></a>
test(eslint-plugin): [no-floating-promises] format test snippets (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin/issues/12723">#12723</a>)</li>
<li><a
href="3596323c63"><code>3596323</code></a>
fix(eslint-plugin): [no-floating-promises] setting <code>ignoreVoid:
false</code> result...</li>
<li><a
href="956a3397d9"><code>956a339</code></a>
fix(eslint-plugin): [no-unnecessary-type-assertion] prevent stack
overflow in...</li>
<li><a
href="55f6d5d4ca"><code>55f6d5d</code></a>
chore: enable source maps (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin/issues/12677">#12677</a>)</li>
<li><a
href="3aefb6a27d"><code>3aefb6a</code></a>
test: improve vitest performance with <code>isolate: false</code> (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin/issues/12703">#12703</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.68.0/packages/eslint-plugin">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/parser` from 8.67.0 to 8.68.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/parser's
releases</a>.</em></p>
<blockquote>
<h2>v8.68.0</h2>
<h2>8.68.0 (2026-08-24)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>eslint-plugin:</strong> [strict-void-return] add fix
suggestions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12086">#12086</a>)</li>
<li><strong>utils:</strong> support ESLint rule meta.languages (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12663">#12663</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [unified-signatures] deduplicate
types in report (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12656">#12656</a>)</li>
<li><strong>eslint-plugin:</strong> [return-await] prevent autofix from
breaking code in arrow-functions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12707">#12707</a>)</li>
<li><strong>eslint-plugin:</strong> [unified-signatures] report
identical signatures (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12678">#12678</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
prevent stack overflow in recursive types (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12711">#12711</a>)</li>
<li><strong>eslint-plugin:</strong> [no-floating-promises] setting
<code>ignoreVoid: false</code> results in false negative in
ArrowFunctionExpression (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12646">#12646</a>)</li>
<li><strong>eslint-plugin:</strong> [no-empty-object-type] ignore
suggestions that result in invalid interfaces and export defaults (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12739">#12739</a>)</li>
<li><strong>website:</strong> playground crashes on <code>extends</code>
configs (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12608">#12608</a>)</li>
<li><strong>website:</strong> account for thanks.dev and out-of-band
donors in sponsors list (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12735">#12735</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Hugo <a
href="https://github.com/hugop95"><code>@​hugop95</code></a></li>
<li>Josh Goldberg ✨</li>
<li>Niki <a
href="https://github.com/phaux"><code>@​phaux</code></a></li>
<li>Thiago Barbosa</li>
<li>Younsang Na <a
href="https://github.com/nayounsang"><code>@​nayounsang</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.68.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md">@​typescript-eslint/parser's
changelog</a>.</em></p>
<blockquote>
<h2>8.68.0 (2026-08-24)</h2>
<p>This was a version bump only for parser to align it with other
projects, there were no code changes.</p>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.68.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website...

_Description has been truncated_

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
2026-09-01 20:21:24 +02:00
14 changed files with 1314 additions and 340 deletions

View File

@@ -1,3 +1,3 @@
{
".": "0.71.0"
".": "0.73.0"
}

View File

@@ -1,5 +1,24 @@
# Changelog
## [0.73.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.72.0...v0.73.0) (2026-09-01)
### Features
* **deps:** update Claude SDK ([#1066](https://github.com/agentclientprotocol/claude-agent-acp/issues/1066)) ([b4e3aaa](https://github.com/agentclientprotocol/claude-agent-acp/commit/b4e3aaa727821a85cef6dcf51452381d35fd68a5))
## [0.72.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.71.0...v0.72.0) (2026-09-01)
### Features
* **deps:** Update to @anthropic-ai/claude-agent-sdk 0.3.252 ([#1062](https://github.com/agentclientprotocol/claude-agent-acp/issues/1062)) ([9aaf066](https://github.com/agentclientprotocol/claude-agent-acp/commit/9aaf0663bf48a4209e8e6511af395102c05e501e))
### Bug Fixes
* Adopt per-model effort settings and user_message_uuid result attribution ([#1065](https://github.com/agentclientprotocol/claude-agent-acp/issues/1065)) ([a04d354](https://github.com/agentclientprotocol/claude-agent-acp/commit/a04d35496c1f1b4c19d8f4204ee6377de45624ab))
## [0.71.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.70.0...v0.71.0) (2026-08-31)

459
package-lock.json generated
View File

@@ -1,31 +1,31 @@
{
"name": "@agentclientprotocol/claude-agent-acp",
"version": "0.71.0",
"version": "0.73.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@agentclientprotocol/claude-agent-acp",
"version": "0.71.0",
"version": "0.73.0",
"license": "Apache-2.0",
"dependencies": {
"@agentclientprotocol/sdk": "1.3.0",
"@anthropic-ai/claude-agent-sdk": "0.3.238",
"zod": "^3.25.0 || ^4.0.0"
"@agentclientprotocol/sdk": "1.4.0",
"@anthropic-ai/claude-agent-sdk": "0.3.257",
"zod": "^4.0.0"
},
"bin": {
"claude-agent-acp": "dist/index.js"
},
"devDependencies": {
"@anthropic-ai/sdk": "0.120.0",
"@anthropic-ai/sdk": "0.122.0",
"@eslint/js": "10.0.1",
"@tsconfig/node22": "22.0.6",
"@types/node": "26.2.0",
"@typescript-eslint/eslint-plugin": "8.67.0",
"@typescript-eslint/parser": "8.67.0",
"eslint": "10.8.1",
"@types/node": "26.4.0",
"@typescript-eslint/eslint-plugin": "8.69.0",
"@typescript-eslint/parser": "8.69.0",
"eslint": "10.9.1",
"eslint-config-prettier": "10.1.8",
"globals": "17.11.0",
"globals": "17.12.0",
"prettier": "3.9.6",
"ts-node": "10.9.2",
"typescript": "6.0.3",
@@ -36,31 +36,31 @@
}
},
"node_modules/@agentclientprotocol/sdk": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@agentclientprotocol/sdk/-/sdk-1.3.0.tgz",
"integrity": "sha512-i3h/efaeuMUFAO1HSfo97QZQnnvMd7wWBYtBsdL6UMZg3a78sk3Ffya5Xu7C7tYsXomXoDXJBAzQF2PcFKAhIQ==",
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@agentclientprotocol/sdk/-/sdk-1.4.0.tgz",
"integrity": "sha512-/eufudw+aFY1LKLolT6yFE6UMmYRl7fMJ/DEONSIyR6wI3slHWITBsANRGqXEY8FRzqUxwh7QEaGiZHcJPVThg==",
"license": "Apache-2.0",
"peerDependencies": {
"zod": "^3.25.0 || ^4.0.0"
}
},
"node_modules/@anthropic-ai/claude-agent-sdk": {
"version": "0.3.238",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.238.tgz",
"integrity": "sha512-ppRfbAflZuV7HPqr8BkHPEk8c7gih3PK+GHnZq6zP0Uo7bJHXAJwc4Za8LJRm4hXMixBNMjK1cU/VWfQ9fE2sg==",
"version": "0.3.257",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.257.tgz",
"integrity": "sha512-Se55zXv48IYLg/WzoXzpbPLcq86suwDSbRUoNb69l4dkovorqS/47Xuy7MUo/gPNwwcPB4a+aqbXbshU33dcdQ==",
"license": "SEE LICENSE IN README.md",
"engines": {
"node": ">=18.0.0"
},
"optionalDependencies": {
"@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.238",
"@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.238",
"@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.238",
"@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.238",
"@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.238",
"@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.238",
"@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.238",
"@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.238"
"@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.257",
"@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.257",
"@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.257",
"@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.257",
"@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.257",
"@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.257",
"@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.257",
"@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.257"
},
"peerDependencies": {
"@anthropic-ai/sdk": ">=0.93.0",
@@ -69,9 +69,9 @@
}
},
"node_modules/@anthropic-ai/claude-agent-sdk-darwin-arm64": {
"version": "0.3.238",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.238.tgz",
"integrity": "sha512-7KctNItTzHRiDg+jFxTM46o+Y/YS52V6HSopaWPggO6BbR+3MV/rKMmq+zP93If7eVwadW9Yg9vHLjXDKIw9OQ==",
"version": "0.3.257",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.257.tgz",
"integrity": "sha512-ITjFPYB8riu9tbxbrWArokiZ/90w/NDrYbtEvyr3ScilVtu1iupkComxkhvbUxoyT4JRDpKsdw/ZOfwSl/pkpA==",
"cpu": [
"arm64"
],
@@ -82,9 +82,9 @@
]
},
"node_modules/@anthropic-ai/claude-agent-sdk-darwin-x64": {
"version": "0.3.238",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.238.tgz",
"integrity": "sha512-aDt8LXjWISwLzqeCBHxJC5AR1iVYlY5UYIm2VLztjI1U0PCb1BiH/IfyyjjizqRhhEwyQp9YAJeZZq8n/2vQEA==",
"version": "0.3.257",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.257.tgz",
"integrity": "sha512-0s7QoLRnopbMvCqVpgCnvBWg4UNxPUybMZTknkn3HLBMvUjUdHs1QXb77c/yrT1WAPqDTw1Ju+H0esXwWa8/Kg==",
"cpu": [
"x64"
],
@@ -95,9 +95,9 @@
]
},
"node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64": {
"version": "0.3.238",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.238.tgz",
"integrity": "sha512-P7V9TFokcNRdIJPUzDQ0GLBfMuoewWEH4rh6U3e7RaEAxzdeUcdS9P0N4arXqK2YAOtK1o93QSEtiGUF1fTfWw==",
"version": "0.3.257",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.257.tgz",
"integrity": "sha512-38tv1s1CaIE6CyEBmJpfKtvwPzrasxPiRT079BEs5aaiLPQ+pmSGF0+Lwy7ot9i6xqr7v5/91TVo5JXHI8Pkpg==",
"cpu": [
"arm64"
],
@@ -108,9 +108,9 @@
]
},
"node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64-musl": {
"version": "0.3.238",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.238.tgz",
"integrity": "sha512-ankSEMAMTVulKYg0NT8fZ7a5+q+aIzfiqhcrLe9zYPeuhE+lpNeq0JPMcHyOtUAENxVI0/2J2q5OXGl9O0qWcg==",
"version": "0.3.257",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.257.tgz",
"integrity": "sha512-t2WHfKjY4Jjgzfk0lbBUt6EVPQe+wXvXvRmYnK7ICfzZ1jS8CrpDajwBQErKFSMiUqGAutZy3vwDwGziW32cpw==",
"cpu": [
"arm64"
],
@@ -121,9 +121,9 @@
]
},
"node_modules/@anthropic-ai/claude-agent-sdk-linux-x64": {
"version": "0.3.238",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.238.tgz",
"integrity": "sha512-/arAOSqtIAWDu7Z4Uf2Un3n+/5Zg3NpxZKzMQEDnYNkbJRhr44sM59TFaEOiWq1FqlUJKi6wHjgm+TW5B8DWWg==",
"version": "0.3.257",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.257.tgz",
"integrity": "sha512-0FRyIwV4jEJErdDDoYMC0v9lzuFNz5y0lK2340H5fP02eNXsP3U0htKW/bfRP8Ppei+xc4QUZgdCI6rVzkhXGg==",
"cpu": [
"x64"
],
@@ -134,9 +134,9 @@
]
},
"node_modules/@anthropic-ai/claude-agent-sdk-linux-x64-musl": {
"version": "0.3.238",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.238.tgz",
"integrity": "sha512-buo3IBSd7EmYcQsH+OKARgNNbSF1+l/+z1hDXSXGKzwD1LFioJZ/k9FWWWC166SoQ93jPmN+G/ksTzEUB3zRGQ==",
"version": "0.3.257",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.257.tgz",
"integrity": "sha512-qSlgAUEpj2JAA+YqMD222iv2W3x8xBDSYwdoaOx20VbaJRjFq8feViI7kcr1C6wVN+ApX4Rxl7YX6gekiBkA2g==",
"cpu": [
"x64"
],
@@ -147,9 +147,9 @@
]
},
"node_modules/@anthropic-ai/claude-agent-sdk-win32-arm64": {
"version": "0.3.238",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.238.tgz",
"integrity": "sha512-yW8lhV7QgYiNu3NatjgmkhUspgJsG2N2N6lmm/7B99sWFobKTbVLMLsXxj6A46R6qE2C4Zzm6PjxgGaMYOMn0g==",
"version": "0.3.257",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.257.tgz",
"integrity": "sha512-7iEwy14lSqaAWvNR3KucBusQs5+hG6uoliYWZ2M2FyaCk5PHYymVf9mrRPtWhaYfNwum/YY13acYIIvnAbz0kA==",
"cpu": [
"arm64"
],
@@ -160,9 +160,9 @@
]
},
"node_modules/@anthropic-ai/claude-agent-sdk-win32-x64": {
"version": "0.3.238",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.238.tgz",
"integrity": "sha512-6Fb2JRrBci282fBfhCGB0Tpq7N8V5HMMqsO3YGFkc9hhbn/y9G7glNKSNgRtxD5Ujjxj8rhiaYstXW2DJzanzw==",
"version": "0.3.257",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.257.tgz",
"integrity": "sha512-NW0zMjHXFBdu2TcjT9Zo5o/1tJaDy6A7v4Gt/vLVJgaipV/RzAbyRGXZzH37hswyiKrSLGaSoYYP1vUfncbkUQ==",
"cpu": [
"x64"
],
@@ -173,9 +173,9 @@
]
},
"node_modules/@anthropic-ai/sdk": {
"version": "0.120.0",
"resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.120.0.tgz",
"integrity": "sha512-ZlvmNFT/iIF6JD13rxbbMWD8nvGR0RaUp6yMQnoc+4Af0YjVVe/bIdW1XSQQsoxXAtg1NaT6Vak0LKFlJ4d37Q==",
"version": "0.122.0",
"resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.122.0.tgz",
"integrity": "sha512-GGPNftt0caaz9MDlmNQGHX8855Ojaduyy5pm9Sm1h7HalCn0cWNb5/bweadJF+4yzbal+QL6ztBa09WAAOzLmQ==",
"license": "MIT",
"dependencies": {
"json-schema-to-ts": "^3.1.1",
@@ -420,9 +420,9 @@
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz",
"integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==",
"dev": true,
"license": "MIT"
},
@@ -479,9 +479,9 @@
}
},
"node_modules/@oxc-project/types": {
"version": "0.146.0",
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.146.0.tgz",
"integrity": "sha512-XC0QsnnhVe7sLIWmYmdPw7x5P0h4W8vUU3Nv1ySgWXtvCz8NizoAEpGXA0sOYoJQV2Rl13LgURAHQ5cI5ILCSA==",
"version": "0.147.0",
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.147.0.tgz",
"integrity": "sha512-IJ3s6ltHLp45S0bh7phkX+gJO7A1Wuz2EaqpAhb8WjqDwbzMiWKHhyyT42tskaWjEYXtHtVCPpnBJVT9+dcRLg==",
"dev": true,
"license": "MIT",
"funding": {
@@ -489,9 +489,9 @@
}
},
"node_modules/@rolldown/binding-android-arm-eabi": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.5.tgz",
"integrity": "sha512-DLe/i+l8ynIBY7XEQ191TeZvCoowIGa18R+dIV30GW7DiOtp74i/xX8hs8GUjW5ARV7VZuie3d6AumSmCwbeRA==",
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.6.tgz",
"integrity": "sha512-b+jTcARdTiFLI6jB4a5XjTm0RWd6KcRfQj/I2356fxUZemiho9zQLxo0RtCuMDAyKcLo6cEltkgbQp6d1+sjjQ==",
"cpu": [
"arm"
],
@@ -506,9 +506,9 @@
}
},
"node_modules/@rolldown/binding-android-arm64": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.5.tgz",
"integrity": "sha512-zXcwKlQApYAOELHd8PwKDFkagYF9Wy4e0RJ+0qnzl9Pjnpj75TEG8ufv40p2J7kCEfwZAsNiuzRIyNNMWT38ig==",
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.6.tgz",
"integrity": "sha512-lkWU8ZJaRk9q3CIEY1Tc7vIFALp3Xw5NfGJo2hQg5oIqNgxWi1zI+IiDEK3r70BF5Dzol1tcXsnzsRc8NLhG+Q==",
"cpu": [
"arm64"
],
@@ -523,9 +523,9 @@
}
},
"node_modules/@rolldown/binding-darwin-arm64": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.5.tgz",
"integrity": "sha512-dK4QakI42nzWgJT5sm4y4y/O//D4OxM75/cH28RLV+nzIN9AY+YsbuUVrUTjlLjXR6vpyxFbSsbmNuJ6BP9sww==",
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.6.tgz",
"integrity": "sha512-dgR56NYnvAszm7Ob1B2/Vn0e8bUQYZH2UjVaMMtMVOCKFSfjhfLmuA/9+O+F+ajUdG6B/bSssrKW6JJYASa8jA==",
"cpu": [
"arm64"
],
@@ -540,9 +540,9 @@
}
},
"node_modules/@rolldown/binding-darwin-x64": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.5.tgz",
"integrity": "sha512-fqSALaUu1Wjd1nK2uW2kJDWdLCc8lx1IcY+MTY26Aurfdx19anlzhqXOgCFbBFQnlFDTn4TC1/7Nz4Bl2mLP3A==",
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.6.tgz",
"integrity": "sha512-vpVxFvUCFioJqug7OTvqptkc4yb8UX0AwfDmJpaR/0sWz+BUmqSVAf7c8JkUgnN8YLspb4a/N6NhTyMAmdyQ7Q==",
"cpu": [
"x64"
],
@@ -557,9 +557,9 @@
}
},
"node_modules/@rolldown/binding-freebsd-x64": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.5.tgz",
"integrity": "sha512-/vCnNxlkxs9tKxNDcyWUePpJ/PgTzxIaVhoM5SmG8UV+GR/IcPam4VYxi7GIMo7PSDuNqlJqvprqii9NqqVCMw==",
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.6.tgz",
"integrity": "sha512-h1wG6Y6K3JlRswxsI64qQJqBAy4vrLuHgRbc8CZMGSWTOFRY6ghMApM1NKzB2I0n5xV1fjkE18SuVl2QpLeNpA==",
"cpu": [
"x64"
],
@@ -574,9 +574,9 @@
}
},
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.5.tgz",
"integrity": "sha512-abk0NLA519LxRCszmbE0jYKuQ9YPocOXTiOXOo6Yr+YAT95VH+PtqYAjOJvGKt3viEd/x4qzabAlwd5bHOOARg==",
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.6.tgz",
"integrity": "sha512-tbCiqub0q2MVWJKgF5PoAlNWCtQydiOYSLIkd8sByqK/6MMYLJRcSXSYodqYtd0O+Fw7QaVmKKlS4oL94YRZ0w==",
"cpu": [
"arm"
],
@@ -591,9 +591,9 @@
}
},
"node_modules/@rolldown/binding-linux-arm64-gnu": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.5.tgz",
"integrity": "sha512-Y7eALiJ8lr0M2HH103Js+g7V34wf6snlpZLAsHI90uLhr3PVlNsbFVAXJC9d/V6BnPyKtpSwI+NcB/RLxsQxuA==",
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.6.tgz",
"integrity": "sha512-oxK9+baEBPhZG5HB4URY+uU04zJWeZlH6Tb9rB5DK4DF9XR1uXNLXt5Q5ZsugTKayNCNLhkcwz/ye74hRI98dg==",
"cpu": [
"arm64"
],
@@ -608,9 +608,9 @@
}
},
"node_modules/@rolldown/binding-linux-arm64-musl": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.5.tgz",
"integrity": "sha512-xMvZgnbZg4YVnR/AX2b3oOPDTFYJvUVaJg5FedA/LuvexAtXibZQej4cnTkw3rjsJ/ggUROB64TdtETiim+FYA==",
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.6.tgz",
"integrity": "sha512-muWCk27FVBEZtv0MsK8gnfSmgczA8KQ0uRVJbTABKhkRfQc38aUrcb7fhi3BNiyseFmgcRsoMfQsSNJ+DbZdSw==",
"cpu": [
"arm64"
],
@@ -625,9 +625,9 @@
}
},
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.5.tgz",
"integrity": "sha512-GRjeqTUDHTo5GwntsLaAMcBahG3nlpjftXWZLN73HiYQlhwEowvarFgQnRnQZtIp4keXX7quXFbG38uPZBa2EA==",
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.6.tgz",
"integrity": "sha512-eWDoSfU7Co2qj3vgB3Dt4lj1mG6CoWbcJQkRMP3XJplyCMtuaq3LHvPFjS9QIPvMGWVadJC04Xiy0IdcVPtnwQ==",
"cpu": [
"ppc64"
],
@@ -642,9 +642,9 @@
}
},
"node_modules/@rolldown/binding-linux-s390x-gnu": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.5.tgz",
"integrity": "sha512-vLNTR45F2Uwc8AufkNXPmB4VliaXs+FvcheEogIzOXzO4l+LzieXF5A/TWxLy5HtqpsRCHUfd0lPVrrdgXdLHQ==",
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.6.tgz",
"integrity": "sha512-2bWNjRSIayvupRKxXUY2tWG9fYdoUlTqWywHRvE8Eq3GvuQ+f2HeIkve697fIt+IQs/PV8yFsdWuhp1aJ1PdnA==",
"cpu": [
"s390x"
],
@@ -659,9 +659,9 @@
}
},
"node_modules/@rolldown/binding-linux-x64-gnu": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.5.tgz",
"integrity": "sha512-Mgj59/HTuYeK9Gz2MA+mBWKnHsAgkBSec15ZMb1st3oIfFbX7gCjOae7GydHhzcyQi9Z/7M1QuN9bR3oFqF0jQ==",
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.6.tgz",
"integrity": "sha512-KekI0gS0wLxe1UBSQSjenBVwou/JkcQPDzBPICGZjxUv9k3RteHDPBQaiOicZUFKRIH2wKEimGwVpnJsbPzu7w==",
"cpu": [
"x64"
],
@@ -676,9 +676,9 @@
}
},
"node_modules/@rolldown/binding-linux-x64-musl": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.5.tgz",
"integrity": "sha512-mY8AP0/ichsbhAxGnLa3d3+MwV0EfgrPND2bplI3Ym8T6R2pJ0N87bvrKVwNXmdy3jnr6eQBecdqx/HMknBmpA==",
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.6.tgz",
"integrity": "sha512-TvtPnfVr+HtyGiDmPK4VWmlNm7QhNNAcK5Q9A7aOXsI8545yCyaoMaicXrFZ72JzeYjaUVk7yT243zT0jzjFKQ==",
"cpu": [
"x64"
],
@@ -693,9 +693,9 @@
}
},
"node_modules/@rolldown/binding-openharmony-arm64": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.5.tgz",
"integrity": "sha512-8SLssA2oweAxyRgDp789ACfRb/3P+zNRJpzZxSizxF9m8NUDQ4+3xjo8ttjhVGGw6Qxb70oZiEtIjaKikCO7Yw==",
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.6.tgz",
"integrity": "sha512-iOo0VEay2XFhaCcH0sps5XIimkSuOnNaZrf6+ZkoSOQBJPKNU48RkmJv0/lSpipexu5P+ouFgafe5IGr/DiQfg==",
"cpu": [
"arm64"
],
@@ -710,9 +710,9 @@
}
},
"node_modules/@rolldown/binding-win32-arm64-msvc": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.5.tgz",
"integrity": "sha512-vGbruD5zquhoc8D9SViXgN2FBJtNdTyQ4DtG+SWiEGlJiAzoKcZ2xp+xuXCffhubVdt0NJlTZqkeRuERy7g8Cw==",
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.6.tgz",
"integrity": "sha512-y5NTmmasMS455JlOCO4ZM9krIchv3Mvm1crL1iUPGOPgEzSkves9n0SdC5Sjz6+qWDFhd8/JpfWMH8NSWNHe+A==",
"cpu": [
"arm64"
],
@@ -727,9 +727,9 @@
}
},
"node_modules/@rolldown/binding-win32-x64-msvc": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.5.tgz",
"integrity": "sha512-e/SXpgISz+IoqVcSSI0rx/d/he8zqLex+/rCWpnHpmVfmPIUjag9H6P7zotf0gJHwPUhQxZ/mF8tr6acebT9yw==",
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.6.tgz",
"integrity": "sha512-np8iZSLfXlAD4kWhiyq/u0Yt8oZDtRQ8lGhQaCXo2rl37KNjeU0GjJuwr4P3oeZ++ROfofsKNBqR5LTO8aXyWQ==",
"cpu": [
"x64"
],
@@ -838,9 +838,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "26.2.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.2.0.tgz",
"integrity": "sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==",
"version": "26.4.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.4.0.tgz",
"integrity": "sha512-faiGnoIrLH/V8cibOMEAZ8pMw6oXqSukl29ra4mN8GdaB2ZewzeaLj+INpV5N+Z1eKWzY+IzaIZH2EIR6YZRNQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -848,17 +848,17 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.67.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.67.0.tgz",
"integrity": "sha512-Un7Heoyj65NREbKAyIrFxeM143NZpExWmy1Nep4DLeQOeLlTeumPjoNKnBrU5D5moWXbPJgRa5Uwcdu0faVNGQ==",
"version": "8.69.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.69.0.tgz",
"integrity": "sha512-t5jQTKPIgVW1PE6dR6H6Qz5gm8zjMlX5/2gRaOGd9eO6V7J+tQc6iWKukEe7dY8u9HyYasQ0yfF0/FSSTEO2gA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.12.2",
"@typescript-eslint/scope-manager": "8.67.0",
"@typescript-eslint/type-utils": "8.67.0",
"@typescript-eslint/utils": "8.67.0",
"@typescript-eslint/visitor-keys": "8.67.0",
"@typescript-eslint/scope-manager": "8.69.0",
"@typescript-eslint/type-utils": "8.69.0",
"@typescript-eslint/utils": "8.69.0",
"@typescript-eslint/visitor-keys": "8.69.0",
"ignore": "^7.0.5",
"natural-compare": "^1.4.0",
"ts-api-utils": "^2.5.0"
@@ -871,22 +871,22 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^8.67.0",
"@typescript-eslint/parser": "^8.69.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.67.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.67.0.tgz",
"integrity": "sha512-fUBfTuuEulWqX6V8+O3PtScV01tzYYRUDTAirHFKoRAt7nOzoGiPt0M/bB47wWNy0coOOcgEwAMUtBpykMxl6w==",
"version": "8.69.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.69.0.tgz",
"integrity": "sha512-l4b0DhWioGg6Gt2ebGlvfkFMOjRsauxtsnDRwUSRX1qHq3HdTfQHV8wW9zEXeciai6HfeaKOedQn2Zoofx3WBw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.67.0",
"@typescript-eslint/types": "8.67.0",
"@typescript-eslint/typescript-estree": "8.67.0",
"@typescript-eslint/visitor-keys": "8.67.0",
"@typescript-eslint/scope-manager": "8.69.0",
"@typescript-eslint/types": "8.69.0",
"@typescript-eslint/typescript-estree": "8.69.0",
"@typescript-eslint/visitor-keys": "8.69.0",
"debug": "^4.4.3"
},
"engines": {
@@ -902,14 +902,14 @@
}
},
"node_modules/@typescript-eslint/project-service": {
"version": "8.67.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.67.0.tgz",
"integrity": "sha512-cvE8c7ulYeXN9fYuszhCeCsbzyVEXuhrRCybnBre7TUmqb5nRmBfQAwCj0O3WJFDeyAZt4VYv51vMCC9LHSdYw==",
"version": "8.69.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.69.0.tgz",
"integrity": "sha512-yi4obFrHMmnsesWehHbkg9zMA7Jt8cXT+mKM08G999pH1yT6nqgsHx7MYm0uY1wAj8CqiBXYRJ7WAT0QdQHQXg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.67.0",
"@typescript-eslint/types": "^8.67.0",
"@typescript-eslint/tsconfig-utils": "^8.69.0",
"@typescript-eslint/types": "^8.69.0",
"debug": "^4.4.3"
},
"engines": {
@@ -924,14 +924,14 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "8.67.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.67.0.tgz",
"integrity": "sha512-EgvsleTwS4E+WzzSvem8fAUubLwatMNF1B5hHSLQxcvs7q2dtRhGyujHwLJSYlG41niJ7GP24Aha2+0mb1b2kg==",
"version": "8.69.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.69.0.tgz",
"integrity": "sha512-ewfspqWvSxKSOaplqAUNbaSFO0eB6w1EtQ+esfYFRm3614Ty4uNtExkcbgd6nWsXphbqKyf9ZYdbZdv2xEoWEQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.67.0",
"@typescript-eslint/visitor-keys": "8.67.0"
"@typescript-eslint/types": "8.69.0",
"@typescript-eslint/visitor-keys": "8.69.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -942,9 +942,9 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.67.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.67.0.tgz",
"integrity": "sha512-vV+LUSv5njUWsknE71fqKTlXUva+R76SaeORd6Zojcunk/6DvKFXONU3BrAs2H49mbygUXt6gbYunzwqNwlhdg==",
"version": "8.69.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.69.0.tgz",
"integrity": "sha512-xNqK7YTDZsLniQMV/4rpFR8Z5JlqeRvVjuG1YgF/mdPVH84HSD19L8CczMA0qg2RfwEV231GHH3VnToJDo4MfQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -959,15 +959,15 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.67.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.67.0.tgz",
"integrity": "sha512-aVWDXbRmdXO9siTfX4ditQI1T9+zVcNazT48EJCD0v40/9RIFoUgZ05CmGEq9H2gixRpjUn/iplwvlcvutJW/Q==",
"version": "8.69.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.69.0.tgz",
"integrity": "sha512-ZfoJAVg3JZndQEpEl9petVlxau3lRuElc4HRMuAlLCf8to04/iHz692RUSNmXKDjEuJmIL+KZ2/BsOcBc16dsA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.67.0",
"@typescript-eslint/typescript-estree": "8.67.0",
"@typescript-eslint/utils": "8.67.0",
"@typescript-eslint/types": "8.69.0",
"@typescript-eslint/typescript-estree": "8.69.0",
"@typescript-eslint/utils": "8.69.0",
"debug": "^4.4.3",
"ts-api-utils": "^2.5.0"
},
@@ -984,9 +984,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "8.67.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.67.0.tgz",
"integrity": "sha512-sBtgslww8nsMYUjhdPBiSyUqSzT8uR6g93A2QXnQC8+cGdjz0CyaOdqHDRJb1AtORbZCNUJBBeFA/tNR2uQmww==",
"version": "8.69.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.69.0.tgz",
"integrity": "sha512-K3VrubUPhlo9VDBS6QdI8YB5j7ClpqLRdefcz6PFrhnwicehBweqQ9Evhl4l+FYz0HdDmMqIiSX0aldGRYtDCA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -998,16 +998,16 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "8.67.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.67.0.tgz",
"integrity": "sha512-EKQBCE9yNlRJYm7jdTW5AhDacDUmSwQb0FAJAmK2EKYrNXIsa2vxcSZx6PvJ/dEdI6lS+Y9W+EXckLj0iPFGcw==",
"version": "8.69.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.69.0.tgz",
"integrity": "sha512-AdFkgqck3Vudb/kWnxlyafU/4aBhHrbQ9locP2N4psXTy5mOBg0SHJumnLvx7r6g1gV4DKvUFwV2nJZBoqOD8w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.67.0",
"@typescript-eslint/tsconfig-utils": "8.67.0",
"@typescript-eslint/types": "8.67.0",
"@typescript-eslint/visitor-keys": "8.67.0",
"@typescript-eslint/project-service": "8.69.0",
"@typescript-eslint/tsconfig-utils": "8.69.0",
"@typescript-eslint/types": "8.69.0",
"@typescript-eslint/visitor-keys": "8.69.0",
"debug": "^4.4.3",
"minimatch": "^10.2.2",
"semver": "^7.7.3",
@@ -1026,16 +1026,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "8.67.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.67.0.tgz",
"integrity": "sha512-U9D1FdwEWBwok3hxxSdhclMb0twvt9QnjIQ0VfQ1AiX2epnpSgv2ubVDsayOFyY8K6FX+AQ7E0FKWVG3iKsj1A==",
"version": "8.69.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.69.0.tgz",
"integrity": "sha512-tUbx60BBqQa31kXF5MCsOOLL5E/WzUuxIn7YpAvq+eaUlqvk8/NXnXMBNAdLCr0icjkzem7iUA5QqWHe/hJ1aw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.9.1",
"@typescript-eslint/scope-manager": "8.67.0",
"@typescript-eslint/types": "8.67.0",
"@typescript-eslint/typescript-estree": "8.67.0"
"@typescript-eslint/scope-manager": "8.69.0",
"@typescript-eslint/types": "8.69.0",
"@typescript-eslint/typescript-estree": "8.69.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1050,13 +1050,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.67.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.67.0.tgz",
"integrity": "sha512-fkv8dHRDqfGtTHuJeebdrQ7cX6Ad4WAS00rgHh9UGvMycF1mjBfsxry1XsLIFhWZ6Judlh6UdzK+TYlbpCXgnA==",
"version": "8.69.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.69.0.tgz",
"integrity": "sha512-+rmdgPA+EXkNgKYvHvFfhrs35utXbwaC5PGpDquSXcoXQDKUA5UjV0LmTucG/4JXkM31BTu4TilHtrN8IVBe8w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.67.0",
"@typescript-eslint/types": "8.69.0",
"eslint-visitor-keys": "^5.0.0"
},
"engines": {
@@ -1645,9 +1645,9 @@
}
},
"node_modules/eslint": {
"version": "10.8.1",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-10.8.1.tgz",
"integrity": "sha512-wqA7W2jbsC/BnV9Iv1UZpKVFkO1AdNoSmYW8NWG4HNOBbkAMvIqDZ27pI2f07dqn583NcIC44ckjAcOXDL1QbQ==",
"version": "10.9.1",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-10.9.1.tgz",
"integrity": "sha512-9VaAkDURekixUQJy0oJYl2DcN6oKMfxay7XzaGYAWQwsb6qfKf+x76R2k1L8kb1boc+FyCAaTA9GmiKaaiaF+A==",
"dev": true,
"license": "MIT",
"workspaces": [
@@ -1973,9 +1973,9 @@
}
},
"node_modules/express-rate-limit": {
"version": "8.6.2",
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.6.2.tgz",
"integrity": "sha512-YH4ru+eOJxQABscKFfRCy9R7x9QFGdezclVMwwgFFndzS2Xnm0uo6B0ABZsLhcpeptGv2qvuJVWlQr9gQZoC3A==",
"version": "8.7.0",
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.7.0.tgz",
"integrity": "sha512-hOwV7WOxXfjRpAM1DSJWZDXx3GhplwD8IfwuwvogD8i1Qnkgosw/H45s4ZnFAUHDAhPjlY9hLBvJhKmGMyY26g==",
"license": "MIT",
"peer": true,
"dependencies": {
@@ -2019,9 +2019,9 @@
"license": "Unlicense"
},
"node_modules/fast-uri": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz",
"integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==",
"version": "3.1.6",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.6.tgz",
"integrity": "sha512-7Ical1vFEMr0onbVzEDIreM22I4khW+fzyQPwvAFWBp1iwdshSZRsL4jjRvPG9JP1uiqMHRto+YU6R2/CzDz5Q==",
"funding": [
{
"type": "github",
@@ -2224,9 +2224,9 @@
}
},
"node_modules/globals": {
"version": "17.11.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-17.11.0.tgz",
"integrity": "sha512-Z2I8hM+PbJDXQDq3Icgpzv+mPdwr68iZUU9d5WW4FuXfDUQfkZaZuvjMv42/5crNyw154+9+VWXbYrUgDXbxNw==",
"version": "17.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-17.12.0.tgz",
"integrity": "sha512-cezEd/DTyyht9cvSSURyygXPfy04GtWO/5e6ZPvH7fCtjKz9PYOmuawphw1Ctd1f6C+5JypXfGD7ahNMXvevBA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2276,9 +2276,9 @@
}
},
"node_modules/hono": {
"version": "4.13.2",
"resolved": "https://registry.npmjs.org/hono/-/hono-4.13.2.tgz",
"integrity": "sha512-JydRilDRkYBQMt9qR9U92mXxmbGqsqSn/IKOrh4e7/gEbn+0zSr8igTu0obwJoNGN4sez28DIql7FBHWydoJpA==",
"version": "4.13.5",
"resolved": "https://registry.npmjs.org/hono/-/hono-4.13.5.tgz",
"integrity": "sha512-O6+/eCYRkzzzy0rPWwKLiGBR1nFuUPZynnwjxN1MBA62NNqbT0wQEzQyK2gSO5yDIDB336sXQleAhOHrzlYyKw==",
"license": "MIT",
"peer": true,
"engines": {
@@ -2324,9 +2324,9 @@
}
},
"node_modules/ignore": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz",
"integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==",
"version": "7.0.8",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.8.tgz",
"integrity": "sha512-YYNsSlXBjMk92SKnkwvB5LOVSa6OznlFUGcsvrFgNJbJCd0M1XKeFVRc8ZByeCqz32FivYNHJVooLmdqrmvp/Q==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2351,9 +2351,9 @@
"peer": true
},
"node_modules/ip-address": {
"version": "10.5.0",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.5.0.tgz",
"integrity": "sha512-R5SnVLJmgYYvf2F2ZgwSBnelz5G4q5AxIC277GDfUaNbrZKNANcBC7RHqYYePlszf4kBolVkJauG0ZjHHFh55g==",
"version": "10.7.0",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.0.tgz",
"integrity": "sha512-BGFsyJd5mpXp3rK6jIdADLNgpJUK1jnjzvYF8lK+VyDab9JAmqN0YOKDdP17HlgKb2+ehPgDc8EtnRLbGCAMhA==",
"license": "MIT",
"peer": true,
"engines": {
@@ -2407,9 +2407,9 @@
"license": "ISC"
},
"node_modules/jose": {
"version": "6.2.9",
"resolved": "https://registry.npmjs.org/jose/-/jose-6.2.9.tgz",
"integrity": "sha512-XrchZOFZUl/T3vTwRe8XK+cJrGtMF4th1ARnDfwbBXFKThGhlsxEE4Zu03AD/bjJSt/9jT/mxrOCkJWOg77aPA==",
"version": "6.2.10",
"resolved": "https://registry.npmjs.org/jose/-/jose-6.2.10.tgz",
"integrity": "sha512-iiW7J9qRFlGxvCOIBDBDxFePQSn7ZMAnrYGhrrOo6siO/MIqwfyilLR27pkfDgUk+raLuzADS8A3S/KLBisc0g==",
"license": "MIT",
"peer": true,
"funding": {
@@ -2888,13 +2888,34 @@
"license": "MIT"
},
"node_modules/negotiator": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
"integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.1.0.tgz",
"integrity": "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==",
"license": "MIT",
"peer": true,
"dependencies": {
"content-type": "^2.1.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/negotiator/node_modules/content-type": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz",
"integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">= 0.6"
"node": ">=18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/object-assign": {
@@ -3062,9 +3083,9 @@
"license": "ISC"
},
"node_modules/picomatch": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
"integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz",
"integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3164,9 +3185,9 @@
}
},
"node_modules/qs": {
"version": "6.15.3",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz",
"integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==",
"version": "6.16.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz",
"integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==",
"license": "BSD-3-Clause",
"peer": true,
"dependencies": {
@@ -3221,13 +3242,13 @@
}
},
"node_modules/rolldown": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.5.tgz",
"integrity": "sha512-VD2IE5PUG4Oj8zz2VGykiYd5wbnjdIiSsNQb8Qu5B+noEp+A78mu2iVvpp27g8es14Tk9rofNs5Tku9iQCS4fA==",
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.6.tgz",
"integrity": "sha512-vMM4q3aixf46GiF1Kok8jDPFsEpXgFWGjUHXNkNHNm+Y2adXAG2dbX91jkti3i0ZRsOlcmbuzAz1poObSHCmUA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@oxc-project/types": "=0.146.0",
"@oxc-project/types": "=0.147.0",
"@rolldown/pluginutils": "^1.0.0"
},
"bin": {
@@ -3237,21 +3258,21 @@
"node": "^20.19.0 || >=22.12.0"
},
"optionalDependencies": {
"@rolldown/binding-android-arm-eabi": "1.2.5",
"@rolldown/binding-android-arm64": "1.2.5",
"@rolldown/binding-darwin-arm64": "1.2.5",
"@rolldown/binding-darwin-x64": "1.2.5",
"@rolldown/binding-freebsd-x64": "1.2.5",
"@rolldown/binding-linux-arm-gnueabihf": "1.2.5",
"@rolldown/binding-linux-arm64-gnu": "1.2.5",
"@rolldown/binding-linux-arm64-musl": "1.2.5",
"@rolldown/binding-linux-ppc64-gnu": "1.2.5",
"@rolldown/binding-linux-s390x-gnu": "1.2.5",
"@rolldown/binding-linux-x64-gnu": "1.2.5",
"@rolldown/binding-linux-x64-musl": "1.2.5",
"@rolldown/binding-openharmony-arm64": "1.2.5",
"@rolldown/binding-win32-arm64-msvc": "1.2.5",
"@rolldown/binding-win32-x64-msvc": "1.2.5"
"@rolldown/binding-android-arm-eabi": "1.2.6",
"@rolldown/binding-android-arm64": "1.2.6",
"@rolldown/binding-darwin-arm64": "1.2.6",
"@rolldown/binding-darwin-x64": "1.2.6",
"@rolldown/binding-freebsd-x64": "1.2.6",
"@rolldown/binding-linux-arm-gnueabihf": "1.2.6",
"@rolldown/binding-linux-arm64-gnu": "1.2.6",
"@rolldown/binding-linux-arm64-musl": "1.2.6",
"@rolldown/binding-linux-ppc64-gnu": "1.2.6",
"@rolldown/binding-linux-s390x-gnu": "1.2.6",
"@rolldown/binding-linux-x64-gnu": "1.2.6",
"@rolldown/binding-linux-x64-musl": "1.2.6",
"@rolldown/binding-openharmony-arm64": "1.2.6",
"@rolldown/binding-win32-arm64-msvc": "1.2.6",
"@rolldown/binding-win32-x64-msvc": "1.2.6"
}
},
"node_modules/router": {
@@ -3467,9 +3488,9 @@
"license": "MIT"
},
"node_modules/standardwebhooks": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.0.0.tgz",
"integrity": "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.1.1.tgz",
"integrity": "sha512-bCbX9ZEyFkWPsRz7Bl3NuQUJohmwGSev/yhr7vhaGPlc4AfIrspIRa6cPTBuI1ItmrTDJ4d/S2hCsfe4+vQGnQ==",
"license": "MIT",
"dependencies": {
"@stablelib/base64": "^1.0.0",
@@ -3955,9 +3976,9 @@
}
},
"node_modules/zod": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz",
"integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==",
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/zod/-/zod-4.5.4.tgz",
"integrity": "sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"

View File

@@ -3,7 +3,7 @@
"publishConfig": {
"access": "public"
},
"version": "0.71.0",
"version": "0.73.0",
"description": "An ACP-compatible coding agent powered by the Claude Agent SDK (TypeScript)",
"main": "dist/lib.js",
"types": "dist/lib.d.ts",
@@ -65,20 +65,20 @@
"node": ">=22"
},
"dependencies": {
"@agentclientprotocol/sdk": "1.3.0",
"@anthropic-ai/claude-agent-sdk": "0.3.238",
"@agentclientprotocol/sdk": "1.4.0",
"@anthropic-ai/claude-agent-sdk": "0.3.257",
"zod": "^4.0.0"
},
"devDependencies": {
"@anthropic-ai/sdk": "0.120.0",
"@anthropic-ai/sdk": "0.122.0",
"@eslint/js": "10.0.1",
"@tsconfig/node22": "22.0.6",
"@types/node": "26.2.0",
"@typescript-eslint/eslint-plugin": "8.67.0",
"@typescript-eslint/parser": "8.67.0",
"eslint": "10.8.1",
"@types/node": "26.4.0",
"@typescript-eslint/eslint-plugin": "8.69.0",
"@typescript-eslint/parser": "8.69.0",
"eslint": "10.9.1",
"eslint-config-prettier": "10.1.8",
"globals": "17.11.0",
"globals": "17.12.0",
"prettier": "3.9.6",
"ts-node": "10.9.2",
"typescript": "6.0.3",

View File

@@ -80,6 +80,7 @@ import {
SDKMessageOrigin,
SDKPartialAssistantMessage,
SDKUserMessage,
Settings,
SlashCommand,
ThinkingConfig,
} from "@anthropic-ai/claude-agent-sdk";
@@ -705,6 +706,14 @@ export type Session = {
* user's intent so it persists across model switches; the Fast mode config
* option is only surfaced while the selected model supports it. */
fastModeEnabled: boolean;
/** Whether the user picked a non-default effort through the ACP picker this
* session. A pin lives at the SDK's flag layer, which overrides the CLI's
* persisted effort (including the per-model `modelSettings` entries), so it
* follows the session across model switches; without one, the CLI resolves
* effort itself and the Effort option is display-only. Cleared when the
* user picks "Default" (the flag layer is cleared with it) or when a model
* switch clamps the pin away. */
effortPinnedByUser?: boolean;
/** Why the SDK currently can't serve Fast mode, when the reason is one worth
* telling the user about (see {@link FAST_MODE_UNAVAILABLE_EXPLANATIONS} —
* routine states like the SDK's own opt-in requirement normalize to
@@ -1475,11 +1484,11 @@ export interface AcpClient {
writeTextFile(params: WriteTextFileRequest): Promise<WriteTextFileResponse>;
/** `signal`, when aborted, sends `$/cancel_request` for the in-flight
* elicitation so the client can dismiss its prompt and settle our await. */
unstable_createElicitation(
createElicitation(
params: CreateElicitationRequest,
signal?: AbortSignal,
): Promise<CreateElicitationResponse>;
unstable_completeElicitation(params: CompleteElicitationNotification): Promise<void>;
completeElicitation(params: CompleteElicitationNotification): Promise<void>;
/** Send a custom (extension) notification, e.g. `_claude/sdkMessage`. */
extNotification(method: string, params: Record<string, unknown>): Promise<void>;
}
@@ -1514,7 +1523,7 @@ class ClientConnection implements AcpClient {
return this.ctx.request(methods.client.fs.writeTextFile, params);
}
unstable_createElicitation(
createElicitation(
params: CreateElicitationRequest,
signal?: AbortSignal,
): Promise<CreateElicitationResponse> {
@@ -1523,7 +1532,7 @@ class ClientConnection implements AcpClient {
});
}
unstable_completeElicitation(params: CompleteElicitationNotification): Promise<void> {
completeElicitation(params: CompleteElicitationNotification): Promise<void> {
return this.ctx.notify(methods.client.elicitation.complete, params);
}
@@ -1652,7 +1661,7 @@ async function authenticateMcpServer(
serverName,
flowAbort.signal,
);
const elicitation = host.client.unstable_createElicitation(
const elicitation = host.client.createElicitation(
{
mode: "url",
sessionId,
@@ -1675,7 +1684,7 @@ async function authenticateMcpServer(
await completed;
}
try {
await host.client.unstable_completeElicitation({ elicitationId });
await host.client.completeElicitation({ elicitationId });
} catch (error) {
if (!flowAbort.signal.aborted) {
host.logger.error(`Failed to complete MCP OAuth elicitation: ${error}`);
@@ -2767,12 +2776,20 @@ export class ClaudeAcpAgent {
*
* But an echo-less result can also be an ORPHAN: cancel() settles+removes a
* queued turn whose user message was already pushed, so the SDK still runs
* it and emits a result with no uuid to match. Promoting the head for an
* it and emits a result with no echo to match. Promoting the head for an
* orphan would misattribute its stop reason/usage to an unrelated later
* prompt. `session.pendingOrphanResults` counts exactly how many such
* orphans are still expected (FIFO, they arrive before any live turn's
* result), so we skip those and only promote once the count is drained. */
const ensureActiveTurn = () => {
* result), so we skip those and only promote once the count is drained.
*
* `resultUserMessageUuid` is the result's own join key (SDK 0.3.246+
* echoes the triggering send's client uuid on results; absent on older
* CLIs, synthetic/meta turns, and session-scoped failures). When present
* it upgrades the map lane from positional heuristics to an exact match:
* a stamp naming an orphaned command consumes the result outright, and a
* stamp naming anything else positively refutes "this is a dead turn's
* result", so the dup-over-loss one-skip must not eat it. */
const ensureActiveTurn = (resultUserMessageUuid?: string) => {
if (session.activeTurn) {
if (!isHeldOpen(session.activeTurn)) {
return;
@@ -2824,8 +2841,13 @@ export class ClaudeAcpAgent {
// double-consume it. The unexpected-transition logging in the frame
// handler is the tripwire for that class of drift.
if (session.orphanCommands?.size) {
const stampedOrphan =
resultUserMessageUuid !== undefined && session.orphanCommands.has(resultUserMessageUuid);
let consumedOrphanResult = false;
let oldestPending: string | undefined;
// The started/zombie drain applies regardless of the stamp: commands
// folded into the turn that emitted this result share it, and zombies'
// late results have already passed (or never existed).
for (const [uuid, state] of session.orphanCommands) {
if (state === "started" || state === "zombie") {
consumedOrphanResult = true;
@@ -2834,17 +2856,32 @@ export class ClaudeAcpAgent {
oldestPending ??= uuid;
}
}
if (consumedOrphanResult) {
if (stampedOrphan) {
// Exact join: the result names an orphaned command. Delete the
// matched entry even when it is still "pending" (its dispatch frame
// was lost) and consume the result — no promotion.
session.orphanCommands.delete(resultUserMessageUuid!);
return;
}
if (oldestPending !== undefined) {
// No dispatch was seen before this result, so it is very likely a
// live turn's — but a lost "started" frame would mean it IS the
// orphan's (dup-over-loss: prefer one wrong skip over
// misattributing a dead turn's outcome to a live prompt). Grant
// each pending entry exactly one skip, like the count lane did.
session.orphanCommands.delete(oldestPending);
return;
if (resultUserMessageUuid !== undefined) {
// The stamp names a send that is NOT in the orphan map, so this is
// a live turn's result: skip both the consumed-return (its folded
// orphans were drained above, but the result itself still needs a
// turn) and the dup-over-loss one-skip the stamp refutes, and fall
// through to promote the head.
} else {
if (consumedOrphanResult) {
return;
}
if (oldestPending !== undefined) {
// No dispatch was seen before this result, so it is very likely a
// live turn's — but a lost "started" frame would mean it IS the
// orphan's (dup-over-loss: prefer one wrong skip over
// misattributing a dead turn's outcome to a live prompt). Grant
// each pending entry exactly one skip, like the count lane did.
session.orphanCommands.delete(oldestPending);
return;
}
}
}
const head = firstUnsettledQueuedTurn();
@@ -3858,7 +3895,7 @@ export class ClaudeAcpAgent {
// client supports url elicitation; ignore failures otherwise.
if (this.clientCapabilities?.elicitation?.url) {
try {
await this.client.unstable_completeElicitation({
await this.client.completeElicitation({
elicitationId: message.elicitation_id,
});
} catch (error) {
@@ -3950,7 +3987,7 @@ export class ClaudeAcpAgent {
});
}
if (persistent) {
await this.syncModelAfterRefusalFallback(
await this.syncModelAfterExternalSwitch(
params.sessionId,
session,
message.fallback_model,
@@ -4065,7 +4102,7 @@ export class ClaudeAcpAgent {
// the map in that case).
if (!isAutonomousResult) {
recordResultForOrphanCommands();
ensureActiveTurn();
ensureActiveTurn(message.user_message_uuid);
// Once the submitted goal command has produced its own result,
// no older runtime update can still precede it in the ordered
// SDK stream. Stop suppressing updates even when this runtime
@@ -4289,13 +4326,28 @@ export class ClaudeAcpAgent {
isEmptyUserInterruptionDiagnostic(message) &&
(session.pendingEmptyInterruptionDiagnosticCommands?.size ?? 0) > 0
) {
const commandUuid = session
.pendingEmptyInterruptionDiagnosticCommands!.values()
.next().value;
if (commandUuid) {
session.pendingEmptyInterruptionDiagnosticCommands!.delete(commandUuid);
const pending = session.pendingEmptyInterruptionDiagnosticCommands!;
const stamped = message.user_message_uuid;
if (stamped === undefined) {
// Older producer: no join key on the result, so consume an
// arbitrary hand-off token like before.
const commandUuid = pending.values().next().value;
if (commandUuid) {
pending.delete(commandUuid);
}
break;
}
break;
if (pending.delete(stamped)) {
// Exact join (SDK 0.3.246+ echoes the triggering send's
// uuid on error results): the diagnostic names a cancelled
// command we handed a token for — swallow it.
break;
}
// Stamped but unmatched: this diagnostic belongs to a send we
// did NOT cancel — a live turn's real failure. Don't let a
// stale token eat it; fall through to the ordinary failure
// lanes (the clear below retires the stale tokens, same as
// every other non-diagnostic result path).
}
// Some CLI versions omit the cancelled cycle's diagnostic. Do
@@ -5287,8 +5339,10 @@ export class ClaudeAcpAgent {
}
// 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. Track those so the consumer skips them (see
// ensureActiveTurn) rather than misattributing them to the head.
// user echo to match (0.3.246+ CLIs do stamp results with the
// triggering send's user_message_uuid, which ensureActiveTurn uses as
// an exact join when present). Track those so the consumer skips them
// (see ensureActiveTurn) rather than misattributing them to the head.
// msg_lifecycle_v1 CLIs get per-uuid tracking drained by the command's
// own terminal lifecycle frame — exact under command coalescing, where
// N queued commands fold into ONE turn emitting one result and a plain
@@ -6376,7 +6430,7 @@ export class ClaudeAcpAgent {
try {
const response = await this.withPendingUserInput(sessionId, () =>
this.client.unstable_createElicitation(createRequest, signal),
this.client.createElicitation(createRequest, signal),
);
if (signal.aborted) {
return { action: "cancel" };
@@ -6414,7 +6468,7 @@ export class ClaudeAcpAgent {
let response;
try {
response = await this.withPendingUserInput(sessionId, () =>
this.client.unstable_createElicitation(createRequest, signal),
this.client.createElicitation(createRequest, signal),
);
} catch (error) {
// A cancellation we requested (signal aborted) settles as an aborted tool
@@ -6459,10 +6513,7 @@ export class ClaudeAcpAgent {
let response: CreateElicitationResponse;
try {
response = await this.withPendingUserInput(sessionId, () =>
this.client.unstable_createElicitation(
refusalFallbackToCreateRequest(prompt, sessionId),
signal,
),
this.client.createElicitation(refusalFallbackToCreateRequest(prompt, sessionId), signal),
);
} catch (error) {
// A cancellation we requested (signal aborted) is expected teardown;
@@ -6556,15 +6607,26 @@ export class ClaudeAcpAgent {
session.fastModeDisabledReason = undefined;
}
// Rebuild config options since effort levels depend on the selected model
// Rebuild config options since effort levels depend on the selected
// model. The effort seed depends on who owns the choice: a user pin
// made through the ACP picker this session lives at the SDK's flag
// layer and follows the session across switches, so carry it forward.
// Otherwise the CLI resolves effort itself from the persisted settings
// — the NEW model's `modelSettings` entry first (the CLI persists
// /effort per model), then the legacy top-level value — so display
// what it will actually run instead of dragging the old model's value
// along.
const effortOpt = session.configOptions.find((o) => o.id === EFFORT_CONFIG_ID);
const currentEffort =
typeof effortOpt?.currentValue === "string" ? effortOpt.currentValue : undefined;
const seedEffort = session.effortPinnedByUser
? currentEffort
: settingsEffortForModel(session.settingsManager.getSettings(), newModelInfo, value);
session.configOptions = buildConfigOptions(
session.modes,
session.models,
session.modelInfos,
currentEffort,
seedEffort,
session.agents,
session.currentAgent,
{
@@ -6578,14 +6640,25 @@ export class ClaudeAcpAgent {
},
);
// Sync effort with the SDK if it changed after the model switch
const newEffortOpt = session.configOptions.find((o) => o.id === EFFORT_CONFIG_ID);
const newEffort =
typeof newEffortOpt?.currentValue === "string" ? newEffortOpt.currentValue : undefined;
if (newEffort !== currentEffort) {
await session.query.applyFlagSettings({
effortLevel: toSdkEffortLevel(newEffort),
});
// Sync effort with the SDK only when a user pin changed across the
// switch — i.e. the new model clamped it away (buildConfigOptions
// validated the seed against the new model's levels), where the flag
// must be cleared too or the SDK would keep running the old pin
// invisibly. Settings-derived seeds are display-only: the CLI resolves
// persisted effort itself, and pinning it at the flag layer would
// shadow the per-model values on every later switch.
if (session.effortPinnedByUser) {
const newEffortOpt = session.configOptions.find((o) => o.id === EFFORT_CONFIG_ID);
const newEffort =
typeof newEffortOpt?.currentValue === "string" ? newEffortOpt.currentValue : undefined;
if (newEffort !== currentEffort) {
await session.query.applyFlagSettings({
effortLevel: toSdkEffortLevel(newEffort),
});
if (newEffort === undefined || newEffort === "default") {
session.effortPinnedByUser = false;
}
}
}
// Emit current_mode_update only after session.modes AND
@@ -6618,42 +6691,48 @@ export class ClaudeAcpAgent {
await session.query.applyFlagSettings({
effortLevel: toSdkEffortLevel(value),
});
// "Default" clears the flag layer (toSdkEffortLevel → null), handing
// effort back to the CLI's persisted per-model resolution — so it
// un-pins; any other pick pins effort for the session.
session.effortPinnedByUser = value !== "default";
}
}
}
/** Reconcile adapter model state after the SDK persistently swapped the
* session's model out from under us (refusal fallback). The SDK already
* made the switch, so this must NOT call `query.setModel` — it only
* updates our bookkeeping (currentModelId, context window, mode clamping,
* effort/Fast-mode options) via the same `applyConfigOptionValue` path a
* user-driven model change takes, then notifies the client. */
private async syncModelAfterRefusalFallback(
/** Reconcile adapter model state after the SDK switched the session's
* model out from under us — a refusal fallback, or any switch reported by
* the PostModelSwitch hook that the adapter didn't drive (e.g. a `/model`
* command typed as a prompt). The SDK already made the switch, so this
* must NOT call `query.setModel` — it only updates our bookkeeping
* (currentModelId, context window, mode clamping, effort/Fast-mode
* options) via the same `applyConfigOptionValue` path a user-driven model
* change takes, then notifies the client. */
private async syncModelAfterExternalSwitch(
sessionId: string,
session: Session,
fallbackModel: string,
switchedModel: string,
): Promise<void> {
// Map the SDK-reported model onto one of the session's model options
// (handles display names and `resolvedModel` ids). The fallback model may
// not be among the options — e.g. excluded by the user's
// (handles display names and `resolvedModel` ids). The switched-to model
// may not be among the options — e.g. excluded by the user's
// `availableModels` allowlist — in which case we track the raw id: the
// picker shows no selection, but the model-dependent bookkeeping and any
// later `setModel` round-trip stay truthful to what the SDK is running.
const resolved = resolveModelPreference(session.modelInfos, fallbackModel);
const value = resolved?.value ?? fallbackModel;
const resolved = resolveModelPreference(session.modelInfos, switchedModel);
const value = resolved?.value ?? switchedModel;
if (session.models.currentModelId === value) return;
try {
await this.updateConfigOption(sessionId, MODEL_CONFIG_ID, value);
} catch (err) {
// This runs on the consumer loop: a throw here tears down the query
// stream (failAllTurns + closeQueryStream) and bricks the session —
// far worse than stale bookkeeping. The user-driven RPC path lets the
// same errors propagate to fail just that request; here we log and
// move on, matching the setPermissionMode containment inside
// applyConfigOptionValue.
// This runs on the consumer loop (or detached from a hook callback): a
// throw here tears down the query stream (failAllTurns +
// closeQueryStream) and bricks the session — far worse than stale
// bookkeeping. The user-driven RPC path lets the same errors propagate
// to fail just that request; here we log and move on, matching the
// setPermissionMode containment inside applyConfigOptionValue.
this.logger.error(
`Failed to reconcile model state after refusal fallback to "${fallbackModel}":`,
`Failed to reconcile model state after external switch to "${switchedModel}":`,
err,
);
}
@@ -7113,6 +7192,44 @@ export class ClaudeAcpAgent {
],
},
],
// Mirror model switches the adapter didn't drive into the ACP picker
// and the model-dependent bookkeeping (context window, mode clamping,
// effort/Fast-mode options). Without this, a `/model <name>` command
// typed as a prompt — which the CLI executes as a local command —
// switches the session's model with no refusal-fallback frame, and
// the client's picker silently drifts. `source: 'sdk'` is the
// adapter's own setModel (applyConfigOptionValue already synced it),
// and 'resume' restores are read back via readResumedLiveModel, so
// only the remaining sources sync here (CLI 2.1.251+; older CLIs
// never fire the hook and keep the pre-hook behavior).
PostModelSwitch: [
...(userProvidedOptions?.hooks?.PostModelSwitch || []),
{
hooks: [
async (input) => {
if (
input.hook_event_name === "PostModelSwitch" &&
input.source !== "sdk" &&
input.source !== "resume"
) {
// Don't run the sync before answering the hook: the sync
// can issue applyFlagSettings (an outgoing control request)
// while the CLI is awaiting this hook's response, and SDK
// control requests are serialized over one channel.
// syncModelAfterExternalSwitch contains its own errors, so
// the detached call can't surface an unhandled rejection.
const toModel = input.to_model;
setImmediate(() => {
const live = this.sessions[sessionId];
if (!live) return;
void this.syncModelAfterExternalSwitch(sessionId, live, toModel);
});
}
return { continue: true };
},
],
},
],
...(fileChangeAuditSupport
? {
Stop: [
@@ -7241,7 +7358,7 @@ export class ClaudeAcpAgent {
// (current and future); the identity fields are overridden because the
// fuzzy-matched sibling's resolvedModel/displayName/description can
// describe a different context lane and would poison later resolvedModel
// matching (syncModelAfterRefusalFallback) and context-window inference
// matching (syncModelAfterExternalSwitch) and context-window inference
// (applyConfigOptionValue) if they traveled under this id.
const modelInfos = fallbackModelInfo
? [
@@ -7295,27 +7412,23 @@ export class ClaudeAcpAgent {
disabledReason: fastModeDisabledReason,
};
// The Effort picker seed mirrors what the CLI itself resolves from the
// persisted settings — the current model's `modelSettings` entry first
// (the CLI persists /effort per model), then the legacy top-level value.
// Display-only: no applyFlagSettings here. The CLI reads the same
// settings, so pinning the seed at the flag layer was always redundant —
// and it would override the CLI's own per-model restore on every later
// model switch. Only a user's explicit ACP picker choice pins the flag
// (see applyConfigOptionValue / Session.effortPinnedByUser).
const configOptions = buildConfigOptions(
modes,
models,
modelInfos,
settingsManager.getSettings().effortLevel,
settingsEffortForModel(settingsManager.getSettings(), currentModelInfo),
agents,
currentAgent,
fastMode,
);
// Apply the initial effort level to the SDK so it matches the UI default
const initialEffort = configOptions.find((o) => o.id === EFFORT_CONFIG_ID);
if (
initialEffort &&
typeof initialEffort.currentValue === "string" &&
initialEffort.currentValue !== "default"
) {
await q.applyFlagSettings({
effortLevel: toSdkEffortLevel(initialEffort.currentValue),
});
}
// Seed the context window WITHOUT any extra IPC on the session/new path.
// On session/load, the resumed session's own `getContextUsage` report — a
// response `getAvailableModels` already awaited to learn the live model
@@ -7757,6 +7870,32 @@ function toSdkEffortLevel(value: string | undefined): EffortLevel | null {
return value === undefined || value === "default" ? null : (value as EffortLevel);
}
/** The effort level the CLI itself resolves for a model from the persisted
* settings: the per-model entry (`modelSettings`, keyed by canonical model
* name — the CLI persists /effort per model since 2.1.243) wins over the
* legacy top-level `effortLevel`. Alias picker rows are looked up by their
* resolved model id first, then the row value, then the raw config value
* for models not in the picker. Exact keys only — the CLI's canonical-form
* fallback matching isn't replicated, so a miss simply falls back to the
* top-level value (best-effort display; `buildConfigOptions` still
* validates the result against the model's supported levels). */
export function settingsEffortForModel(
settings: Settings,
modelInfo: ModelInfo | undefined,
modelId?: string,
): string | undefined {
const modelSettings = settings.modelSettings;
if (modelSettings) {
for (const key of [modelInfo?.resolvedModel, modelInfo?.value, modelId]) {
const perModel = key !== undefined ? modelSettings[key]?.effortLevel : undefined;
if (typeof perModel === "string") {
return perModel;
}
}
}
return settings.effortLevel;
}
// `supportedAgents()` always returns Claude Code's built-in subagents — the
// ones used for Task-tool delegation (Explore, Plan, etc.) — even when the user
// has configured none of their own. Those aren't meaningful *main-thread*
@@ -8195,7 +8334,7 @@ export function resolveModelPreference(models: ModelInfo[], preference: string):
* 4. `resolveModelPreference` over the picker entries.
* 5. A model with no picker counterpart (e.g. excluded by an
* `availableModels` allowlist) is tracked verbatim, mirroring
* `syncModelAfterRefusalFallback`: the picker shows no selection, but the
* `syncModelAfterExternalSwitch`: the picker shows no selection, but the
* model-dependent bookkeeping stays truthful to what the SDK is running. */
export function matchResumedModel(models: ModelInfo[], liveModel: string): ModelInfo {
const live = canonicalizeModelId(liveModel);
@@ -8335,6 +8474,15 @@ export function applyAvailableModelsAllowlist(
* #596). Best-effort: a control-request failure is logged and returns nulls
* so callers keep their current choice; failing the whole session/load over
* an unreadable report would be worse. */
/** Whether a rejected `setModel` was vetoed by a user-configured
* PreModelSwitch hook. The CLI's rejection message is the stable,
* distinguishable marker ("Model switch blocked by a PreModelSwitch hook:
* <reason>"); a format change makes this stop matching and the failure
* falls back to the ordinary fail-loud path, no worse than before. */
function isPreModelSwitchHookBlock(error: unknown): boolean {
return error instanceof Error && error.message.includes("blocked by a PreModelSwitch hook");
}
async function readResumedLiveModel(
query: Query,
models: ModelInfo[],
@@ -8430,11 +8578,30 @@ async function getAvailableModels(
// containment in createSession). The SDK then stayed on the
// transcript's model, so read that back rather than reporting the
// pin the session isn't running.
if (!isResumedSession) throw error;
logger.error(`Failed to re-assert model "${currentModel.value}" on resume:`, error);
const live = await readResumedLiveModel(query, models, logger);
currentModel = live.model ?? currentModel;
resumedContextWindow = live.contextWindow;
//
// One fresh-session failure is advisory, not defining: a
// user-configured PreModelSwitch hook can veto the pin (CLI 2.1.251+;
// 'deny', or 'ask' — which headless sessions refuse), and the SDK
// rejects setModel with "Model switch blocked by a PreModelSwitch
// hook: …". Terminal Claude Code never lets a hook veto its startup
// model (the spawn model isn't a switch), so failing session/new here
// would make the same hook config fatal only over ACP. The session
// stays on the SDK's own default — report that. We can't read the
// live model back on this path: getContextUsage isn't serviced on a
// fresh session until the first prompt turn (issues #886/#880).
if (!isResumedSession) {
if (!isPreModelSwitchHookBlock(error)) throw error;
logger.error(
`Model pin "${currentModel.value}" was vetoed by a PreModelSwitch hook; staying on the default model:`,
error,
);
currentModel = models[0];
} else {
logger.error(`Failed to re-assert model "${currentModel.value}" on resume:`, error);
const live = await readResumedLiveModel(query, models, logger);
currentModel = live.model ?? currentModel;
resumedContextWindow = live.contextWindow;
}
}
}

View File

@@ -387,9 +387,7 @@ describe.skipIf(!process.env.RUN_INTEGRATION_TESTS)("ACP subprocess integration"
return { outcome: { outcome: "selected", optionId } };
}
async unstable_createElicitation(
params: CreateElicitationRequest,
): Promise<CreateElicitationResponse> {
async createElicitation(params: CreateElicitationRequest): Promise<CreateElicitationResponse> {
this.elicitations.push(params);
if (!CreateElicitationRequest.isForm(params)) {
return { action: "decline" };
@@ -466,9 +464,7 @@ describe.skipIf(!process.env.RUN_INTEGRATION_TESTS)("ACP subprocess integration"
)
.onRequest(methods.client.fs.readTextFile, (c) => client.readTextFile(c.params))
.onRequest(methods.client.fs.writeTextFile, (c) => client.writeTextFile(c.params))
.onRequest(methods.client.elicitation.create, (c) =>
client.unstable_createElicitation(c.params),
)
.onRequest(methods.client.elicitation.create, (c) => client.createElicitation(c.params))
.connect(stream);
await ctx.request(methods.agent.initialize, {
@@ -1274,6 +1270,74 @@ describe("tool conversions", () => {
});
});
it("renders a compact placeholder for a document block instead of its base64 payload", () => {
// SDK 0.3.243 moved a PDF Read's document block inside the tool_result
// content; dumping it through the default JSON.stringify branch would
// send the entire base64 payload as a text block.
const toolUse = {
type: "tool_use",
id: "toolu_01PDF",
name: "Read",
input: { file_path: "/Users/test/report.pdf" },
};
const toolResult = {
content: [
{
type: "document" as const,
title: "Quarterly report",
source: {
type: "base64" as const,
media_type: "application/pdf" as const,
data: "A".repeat(4096),
},
},
],
tool_use_id: "toolu_01PDF",
is_error: false,
type: "tool_result" as const,
};
const update = toolUpdateFromToolResult(toolResult as any, toolUse);
expect(update).toEqual({
content: [
{
type: "content",
content: {
type: "text",
text: '[document "Quarterly report": application/pdf, 3.0 KB]',
},
},
],
});
});
it("renders a url-sourced document block as its URL", () => {
const toolResult = {
content: [
{
type: "document" as const,
source: { type: "url" as const, url: "https://example.com/spec.pdf" },
},
],
tool_use_id: "toolu_01PDF",
is_error: false,
type: "tool_result" as const,
};
const update = toolUpdateFromToolResult(toolResult as any, undefined);
expect(update).toEqual({
content: [
{
type: "content",
content: { type: "text", text: "[document: https://example.com/spec.pdf]" },
},
],
});
});
it("should transform tool_reference content to valid ACP content", () => {
const toolUse = {
type: "tool_use",
@@ -1603,6 +1667,78 @@ describe("toolUpdateFromDiffToolResponse", () => {
expect(toolUpdateFromDiffToolResponse(toolResponse)).toEqual({});
});
it("emits a whole-file diff for a Write update whose structuredPatch is empty", () => {
// The empty-patch lanes (nothing changed / diff timed out) would
// otherwise leave Write's optimistic oldText:null "creation" content
// standing for an overwrite of an existing file.
expect(
toolUpdateFromDiffToolResponse({
filePath: "/Users/test/project/file.ts",
structuredPatch: [],
type: "update",
content: "new content",
originalFile: "old content",
}),
).toEqual({
content: [
{
type: "diff",
path: "/Users/test/project/file.ts",
oldText: "old content",
newText: "new content",
},
],
locations: [{ path: "/Users/test/project/file.ts" }],
});
});
it("emits a truthful note for a Write update whose previous content was too large to diff", () => {
// SDK 0.3.252 documents this lane: type "update" with an empty
// structuredPatch AND originalFile null.
expect(
toolUpdateFromDiffToolResponse({
filePath: "/Users/test/project/big.json",
structuredPatch: [],
type: "update",
content: "new content",
originalFile: null,
}),
).toEqual({
content: [
{
type: "content",
content: {
type: "text",
text: "Updated `/Users/test/project/big.json` (previous content too large to diff)",
},
},
],
locations: [{ path: "/Users/test/project/big.json" }],
});
});
it("keeps the empty return for a Write create and for Edit-shaped responses", () => {
// A create's optimistic content already shows creation semantics.
expect(
toolUpdateFromDiffToolResponse({
filePath: "/Users/test/project/new.ts",
structuredPatch: [],
type: "create",
content: "new content",
originalFile: null,
}),
).toEqual({});
// FileEditOutput carries no `type`; its optimistic old/new diff is
// truthful, so an empty patch still means "nothing to correct".
expect(
toolUpdateFromDiffToolResponse({
filePath: "/Users/test/project/file.ts",
structuredPatch: [],
originalFile: "old content",
}),
).toEqual({});
});
});
describe("stripLocalCommandMetadata", () => {
@@ -3611,7 +3747,7 @@ describe("subagent permission attribution (issue #851)", () => {
requests.push(params);
return { outcome: { outcome: "selected", optionId: "allow-once" } };
},
unstable_createElicitation: async (request: CreateElicitationRequest) => {
createElicitation: async (request: CreateElicitationRequest) => {
elicitations.push(request);
return {
action: "accept",
@@ -11840,6 +11976,95 @@ describe("post-error recovery", () => {
await agent.sessions["test-session"]?.consumer;
});
it("uses a result's user_message_uuid to consume exactly the orphan it names", async () => {
// SDK 0.3.246+ stamps results with the triggering send's client uuid.
// With TWO cancelled commands pending (their dispatch frames lost), a
// stamped orphan result must consume the entry it NAMES — not the oldest
// — and the later /compact result, stamped with its own send, must not
// be eaten by the dup-over-loss one-skip that the leftover pending entry
// would otherwise trigger (which would hang the /compact prompt).
const agent = createMockAgent();
let release!: () => void;
const gate = new Promise<void>((resolve) => (release = resolve));
injectGeneratorSession(agent, (input) => {
async function* messageGenerator() {
const iter = input[Symbol.asyncIterator]();
const u1 = await iter.next();
yield lifecycleInit;
yield userEcho(u1.value); // turn 1 active
await iter.next(); // turn 2 queued (cancelled below; every frame lost)
const u3 = await iter.next(); // turn 3 queued (cancelled below)
await gate;
yield { type: "system", subtype: "session_state_changed", state: "idle" }; // turn 1 settles cancelled
// Both dispatch frames are LOST; only turn 3's dead turn flushes a
// result, stamped with the send it answers.
yield {
...createResultMessage({
subtype: "error_during_execution",
stop_reason: null,
is_error: true,
}),
user_message_uuid: u3.value.uuid,
};
const u4 = await iter.next(); // /compact's pushed message
yield {
type: "system",
subtype: "status",
status: "compacting",
session_id: "test-session",
};
yield {
...createResultMessage({ subtype: "success", stop_reason: "end_turn", is_error: false }),
user_message_uuid: u4.value.uuid, // a live result names its own send
};
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);
await waitFor(() => !!agent.sessions["test-session"]?.msgLifecycleV1);
const second = agent.prompt({
sessionId: "test-session",
prompt: [{ type: "text", text: "second" }],
});
const third = agent.prompt({
sessionId: "test-session",
prompt: [{ type: "text", text: "third" }],
});
await waitFor(() => (agent.sessions["test-session"]?.turnQueue?.length ?? 0) >= 3);
await agent.cancel({ sessionId: "test-session" });
expect(agent.sessions["test-session"]?.orphanCommands?.size).toBe(2);
const compact = agent.prompt({
sessionId: "test-session",
prompt: [{ type: "text", text: "/compact" }],
});
release();
await expect(first).resolves.toEqual({
stopReason: "cancelled",
usage: cancelledTurnUsage,
_meta: cancelledTurnQuotaMeta,
});
await expect(second).resolves.toEqual({ stopReason: "cancelled" });
await expect(third).resolves.toEqual({ stopReason: "cancelled" });
// Settles on its OWN stamped result — under positional attribution the
// one-skip would have consumed it for turn 2 and hung this prompt.
const compactResult = await compact;
expect(compactResult.stopReason).toBe("end_turn");
expect(compactResult.usage?.inputTokens).toBe(10);
// Turn 3's entry was consumed by name; turn 2's leftover pending entry
// is swept by /compact's activation.
expect(agent.sessions["test-session"]?.orphanCommands?.size).toBe(0);
await agent.sessions["test-session"]?.consumer;
});
it("deletes (not zombifies) an orphan whose `cancelled` frame arrives AFTER its error result", async () => {
// The live-observed abort ordering is started → error result → cancelled.
// The result deletes the "started" entry when it is skipped; the late
@@ -14253,7 +14478,7 @@ describe("turn steering (_session/steering)", () => {
{
sessionUpdate: async () => {},
requestPermission: userInputRequest,
unstable_createElicitation: userInputRequest,
createElicitation: userInputRequest,
} as unknown as AcpClient,
{ log: () => {}, error: () => {} },
);
@@ -14867,6 +15092,119 @@ describe("turn steering (_session/steering)", () => {
expect(JSON.stringify(updates)).not.toContain("[Request interrupted by user]");
});
it("swallows a stamped interruption diagnostic that names the cancelled command", async () => {
// Same replay ordering as above, but the diagnostic carries the SDK
// 0.3.246+ user_message_uuid stamp naming the cancelled first prompt —
// the exact-join path must consume precisely that hand-off token.
const agent = createMockAgent();
let release!: () => void;
const gate = new Promise<void>((resolve) => (release = resolve));
injectGeneratorSession(agent, (input) => {
async function* messageGenerator() {
const iter = input[Symbol.asyncIterator]();
const original = await iter.next();
await gate;
const replacement = await iter.next();
yield userEcho(original.value);
yield userEcho({
uuid: randomUUID(),
message: { role: "user", content: "[Request interrupted by user]" },
});
yield userEcho(replacement.value);
yield {
...createResultMessage(),
subtype: "success",
is_error: true,
result: "[ede_diagnostic] result_type=user last_content_type=n/a stop_reason=null",
user_message_uuid: original.value.uuid,
};
yield createAssistantText("replacement answer");
yield createResultMessage();
yield idleMessage();
}
return messageGenerator();
});
const first = agent.prompt({
sessionId: "test-session",
prompt: [{ type: "text", text: "first" }],
});
await waitFor(
() =>
agent.sessions["test-session"]?.turnQueue?.length === 1 &&
agent.sessions["test-session"]?.activeTurn == null,
);
await agent.cancel({ sessionId: "test-session" });
await expect(first).resolves.toEqual({ stopReason: "cancelled" });
const second = agent.prompt({
sessionId: "test-session",
prompt: [{ type: "text", text: "second" }],
});
release();
await expect(second).resolves.toEqual(expect.objectContaining({ stopReason: "end_turn" }));
expect(
agent.sessions["test-session"]?.pendingEmptyInterruptionDiagnosticCommands?.size ?? 0,
).toBe(0);
});
it("does not let a stale hand-off token swallow a live turn's stamped interruption diagnostic", async () => {
// The diagnostic-shaped error is stamped with the LIVE replacement
// prompt's uuid — not the cancelled command the token was handed out for.
// The stamp positively refutes "this is the cancelled cycle's
// diagnostic", so it must reach the ordinary failure lanes and fail the
// replacement prompt instead of being silently eaten.
const agent = createMockAgent();
let release!: () => void;
const gate = new Promise<void>((resolve) => (release = resolve));
injectGeneratorSession(agent, (input) => {
async function* messageGenerator() {
const iter = input[Symbol.asyncIterator]();
const original = await iter.next();
await gate;
const replacement = await iter.next();
yield userEcho(original.value);
yield userEcho({
uuid: randomUUID(),
message: { role: "user", content: "[Request interrupted by user]" },
});
yield userEcho(replacement.value);
yield {
...createResultMessage(),
subtype: "success",
is_error: true,
result: "[ede_diagnostic] result_type=user last_content_type=n/a stop_reason=null",
user_message_uuid: replacement.value.uuid,
};
yield idleMessage();
}
return messageGenerator();
});
const first = agent.prompt({
sessionId: "test-session",
prompt: [{ type: "text", text: "first" }],
});
await waitFor(
() =>
agent.sessions["test-session"]?.turnQueue?.length === 1 &&
agent.sessions["test-session"]?.activeTurn == null,
);
await agent.cancel({ sessionId: "test-session" });
await expect(first).resolves.toEqual({ stopReason: "cancelled" });
const second = agent.prompt({
sessionId: "test-session",
prompt: [{ type: "text", text: "second" }],
});
release();
await expect(second).rejects.toThrow("[ede_diagnostic]");
});
it("does not hide an empty user-interruption diagnostic without a cancelled predecessor", async () => {
const agent = createMockAgent();
injectGeneratorSession(agent, (input) => {

View File

@@ -13,6 +13,8 @@ import * as path from "node:path";
let capturedOptions: Options | undefined;
let contextUsageResult: (() => Promise<{ rawMaxTokens: number; model?: string }>) | undefined;
let initModels: Record<string, unknown>[] | undefined;
let setModelImpl: ((model: string) => Promise<void>) | undefined;
let mcpServerStatusResult: () => Promise<Array<{ name: string; status: string }>>;
let mcpAuthenticateImpl: (serverName: string) => Promise<{
authUrl?: string;
@@ -30,7 +32,7 @@ vi.mock("@anthropic-ai/claude-agent-sdk", async () => {
capturedOptions = args.options;
return makeMockQuery({
initializationResult: async () => ({
models: [
models: initModels ?? [
{
value: "claude-sonnet-4-6",
displayName: "Claude Sonnet",
@@ -39,6 +41,7 @@ vi.mock("@anthropic-ai/claude-agent-sdk", async () => {
},
],
}),
setModel: (model: string) => (setModelImpl ? setModelImpl(model) : Promise.resolve()),
getContextUsage: () =>
contextUsageResult ? contextUsageResult() : Promise.resolve(DEFAULT_CONTEXT_USAGE),
mcpServerStatus: () => mcpServerStatusResult(),
@@ -72,6 +75,8 @@ describe("createSession options merging", () => {
beforeEach(async () => {
capturedOptions = undefined;
contextUsageResult = undefined;
initModels = undefined;
setModelImpl = undefined;
mcpServerStatusResult = async () => [];
mcpAuthenticateImpl = async () => ({
requiresUserAction: false,
@@ -685,8 +690,8 @@ describe("createSession options merging", () => {
}));
const completeElicitation = vi.fn(async () => {});
Object.assign((agent as unknown as { client: object }).client, {
unstable_createElicitation: createElicitation,
unstable_completeElicitation: completeElicitation,
createElicitation,
completeElicitation,
});
await agent.initialize({
@@ -871,9 +876,8 @@ describe("createSession options merging", () => {
});
await agent.newSession({ cwd: process.cwd(), mcpServers: [] });
const createElicitation = vi.fn();
(
agent as unknown as { client: { unstable_createElicitation: unknown } }
).client.unstable_createElicitation = createElicitation;
(agent as unknown as { client: { createElicitation: unknown } }).client.createElicitation =
createElicitation;
return { onUserDialog: capturedOptions!.onUserDialog!, createElicitation };
}
@@ -969,4 +973,140 @@ describe("createSession options merging", () => {
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining("client exploded"));
});
});
describe("model switches and PreModelSwitch hooks", () => {
const TWO_MODELS = [
{
value: "claude-sonnet-4-6",
displayName: "Claude Sonnet",
description: "Fast",
supportsAutoMode: true,
},
{
value: "claude-opus-4-8",
displayName: "Claude Opus",
description: "Capable",
supportsAutoMode: true,
},
];
let originalAnthropicModel: string | undefined;
beforeEach(() => {
originalAnthropicModel = process.env.ANTHROPIC_MODEL;
delete process.env.ANTHROPIC_MODEL;
});
afterEach(() => {
if (originalAnthropicModel !== undefined) {
process.env.ANTHROPIC_MODEL = originalAnthropicModel;
} else {
delete process.env.ANTHROPIC_MODEL;
}
});
it("tolerates a PreModelSwitch hook vetoing the fresh-session model pin", async () => {
// CLI 2.1.251+: a user-configured PreModelSwitch hook can deny (or
// 'ask', which headless sessions refuse) the pin's setModel. Terminal
// Claude Code never lets a hook veto its startup model, so the same
// config must not fail session/new over ACP — the session stays on the
// SDK's default, and we report that.
initModels = TWO_MODELS;
process.env.ANTHROPIC_MODEL = "opus";
setModelImpl = () =>
Promise.reject(new Error("Model switch blocked by a PreModelSwitch hook: pinned by IT"));
const errorSpy = vi.fn();
(agent as any).logger = { log: () => {}, error: errorSpy };
const response = await agent.newSession({ cwd: process.cwd(), mcpServers: [] });
expect((agent as any).sessions[response.sessionId].models.currentModelId).toBe(
"claude-sonnet-4-6",
);
expect(errorSpy).toHaveBeenCalledWith(
expect.stringContaining("vetoed by a PreModelSwitch hook"),
expect.anything(),
);
});
it("still fails session/new loudly on a non-hook setModel failure", async () => {
initModels = TWO_MODELS;
process.env.ANTHROPIC_MODEL = "opus";
setModelImpl = () => Promise.reject(new Error("transport exploded"));
await expect(agent.newSession({ cwd: process.cwd(), mcpServers: [] })).rejects.toThrow(
"transport exploded",
);
});
it("syncs adapter model state when a PostModelSwitch hook reports an external switch", async () => {
// A `/model <name>` command typed as a prompt switches the session's
// model with no refusal-fallback frame; the registered PostModelSwitch
// hook is what keeps the ACP picker truthful.
initModels = TWO_MODELS;
const response = await agent.newSession({ cwd: process.cwd(), mcpServers: [] });
const sessionId = response.sessionId;
const matchers = capturedOptions!.hooks?.PostModelSwitch;
expect(matchers).toBeDefined();
const callback = (matchers!.at(-1) as any).hooks[0];
const hookInput = {
hook_event_name: "PostModelSwitch",
session_id: sessionId,
transcript_path: "",
cwd: process.cwd(),
from_model: "claude-sonnet-4-6",
to_model: "claude-opus-4-8",
requested_model: "opus",
source: "command",
context_tokens: 0,
prompt_cache_warm: false,
cache_ttl: "5m",
estimated_cache_write_usd: 0,
pricing: "catalog",
};
const out = await callback(hookInput, undefined, {
signal: new AbortController().signal,
});
expect(out).toEqual({ continue: true });
// The sync is detached from the hook response (control requests are
// serialized); wait for it to land.
await vi.waitFor(() => {
expect((agent as any).sessions[sessionId].models.currentModelId).toBe("claude-opus-4-8");
});
});
it("ignores PostModelSwitch reports for the adapter's own setModel calls", async () => {
initModels = TWO_MODELS;
const response = await agent.newSession({ cwd: process.cwd(), mcpServers: [] });
const sessionId = response.sessionId;
const callback = (capturedOptions!.hooks!.PostModelSwitch!.at(-1) as any).hooks[0];
await callback(
{
hook_event_name: "PostModelSwitch",
session_id: sessionId,
transcript_path: "",
cwd: process.cwd(),
from_model: "claude-sonnet-4-6",
to_model: "claude-opus-4-8",
requested_model: "opus",
source: "sdk",
context_tokens: 0,
prompt_cache_warm: false,
cache_ttl: "5m",
estimated_cache_write_usd: 0,
pricing: "catalog",
},
undefined,
{ signal: new AbortController().signal },
);
// Detached-sync window: give a stray sync the chance to land, then
// assert nothing moved.
await new Promise((resolve) => setImmediate(resolve));
await new Promise((resolve) => setImmediate(resolve));
expect((agent as any).sessions[sessionId].models.currentModelId).toBe("claude-sonnet-4-6");
});
});
});

View File

@@ -126,8 +126,8 @@ async function createHarness(): Promise<LiveHarness> {
},
readTextFile: async () => ({ content: "" }),
writeTextFile: async () => ({}),
unstable_createElicitation: async () => ({ action: "decline" as const }),
unstable_completeElicitation: async () => {},
createElicitation: async () => ({ action: "decline" as const }),
completeElicitation: async () => {},
extNotification: async (method: string, params: JsonObject) => {
if (method !== "_claude/sdkMessage") return;
const message = object(params.message);

View File

@@ -130,8 +130,8 @@ async function createHarness(): Promise<LiveHarness> {
},
readTextFile: async () => ({ content: "" }),
writeTextFile: async () => ({}),
unstable_createElicitation: async () => ({ action: "decline" as const }),
unstable_completeElicitation: async () => {},
createElicitation: async () => ({ action: "decline" as const }),
completeElicitation: async () => {},
extNotification: async (method: string, params: JsonObject) => {
if (method !== "_claude/sdkMessage") return;
const message = object(params.message);

View File

@@ -6,6 +6,7 @@ import {
resolveModelPreference,
applyAvailableModelsAllowlist,
matchResumedModel,
settingsEffortForModel,
} from "../acp-agent.js";
// Mirrors a real `supportedModels()` response: alias rows carry
@@ -270,3 +271,43 @@ describe.skipIf(!process.env.RUN_INTEGRATION_TESTS)("model resolution (real SDK)
}
}, 30000);
});
describe("settingsEffortForModel", () => {
const SONNET: ModelInfo = {
value: "sonnet",
resolvedModel: "claude-sonnet-5",
displayName: "Sonnet",
description: "Sonnet 5 · Efficient for routine tasks",
};
it("prefers the per-model entry keyed by the resolved model id", () => {
const settings = {
effortLevel: "high" as const,
modelSettings: { "claude-sonnet-5": { effortLevel: "low" as const } },
};
expect(settingsEffortForModel(settings, SONNET)).toBe("low");
});
it("falls back to the picker row value, then the raw model id", () => {
const byValue = { modelSettings: { sonnet: { effortLevel: "medium" as const } } };
expect(settingsEffortForModel(byValue, SONNET)).toBe("medium");
const byRawId = { modelSettings: { "claude-x": { effortLevel: "xhigh" as const } } };
expect(settingsEffortForModel(byRawId, undefined, "claude-x")).toBe("xhigh");
});
it("falls back to the top-level effortLevel when no per-model entry matches", () => {
expect(settingsEffortForModel({ effortLevel: "high" }, SONNET)).toBe("high");
expect(
settingsEffortForModel(
{ effortLevel: "high", modelSettings: { "claude-opus-4-8": { effortLevel: "low" } } },
SONNET,
),
).toBe("high");
});
it("returns undefined when neither layer sets an effort", () => {
expect(settingsEffortForModel({}, SONNET)).toBeUndefined();
expect(settingsEffortForModel({ modelSettings: {} }, undefined)).toBeUndefined();
});
});

View File

@@ -110,7 +110,7 @@ describe("session config options", () => {
input: null,
cancelled: false,
permissionMode: "default",
settingsManager: {},
settingsManager: { getSettings: () => ({}) },
modes: structuredClone(MOCK_MODES),
models: structuredClone(MOCK_MODELS),
modelInfos: MOCK_MODELS.availableModels.map((m): ModelInfo => ({
@@ -419,14 +419,18 @@ describe("session config options", () => {
const effortOption = response.configOptions.find((o) => o.id === "effort");
expect(effortOption).toBeUndefined();
expect(applyFlagSettingsSpy).toHaveBeenCalledWith({ effortLevel: null });
// Nothing was pinned at the flag layer (effort was "default"), so there
// is nothing to clear — the CLI resolves its own effort for the new model.
expect(applyFlagSettingsSpy).not.toHaveBeenCalled();
});
it("clamps effort in returned configOptions when new model has different supported levels", async () => {
// Set current effort to "max" which the new model won't support
// Set current effort to "max" which the new model won't support —
// pinned, as a user's ACP picker choice would be.
const session = (agent as unknown as { sessions: Record<string, any> }).sessions[SESSION_ID];
const effortOpt = session.configOptions.find((o: any) => o.id === "effort");
if (effortOpt) effortOpt.currentValue = "max";
session.effortPinnedByUser = true;
session.modelInfos = [
{
@@ -458,10 +462,12 @@ describe("session config options", () => {
});
it("preserves effort in returned configOptions when new model supports same level", async () => {
// Set effort to "low" first
// Set effort to "low" first — pinned, as a user's ACP picker choice
// would be (an unpinned value re-seeds from settings on a switch).
const session = (agent as unknown as { sessions: Record<string, any> }).sessions[SESSION_ID];
const effortOpt = session.configOptions.find((o: any) => o.id === "effort");
if (effortOpt) effortOpt.currentValue = "low";
session.effortPinnedByUser = true;
const response = await agent.setSessionConfigOption({
sessionId: SESSION_ID,
@@ -630,7 +636,7 @@ describe("session config options", () => {
expect(effortOption).toBeUndefined();
});
it("clears effort via applyFlagSettings when switching to a model without effort", async () => {
it("clears a pinned effort via applyFlagSettings when switching to a model without effort", async () => {
const session = (agent as unknown as { sessions: Record<string, any> }).sessions[SESSION_ID];
session.modelInfos = [
{
@@ -648,6 +654,14 @@ describe("session config options", () => {
},
];
// Pin an effort the way a user would, then switch to a model that
// cannot serve it: the flag layer must be cleared alongside, or the
// SDK would keep running the old pin invisibly.
await agent.setSessionConfigOption({
sessionId: SESSION_ID,
configId: "effort",
value: "high",
});
await agent.setSessionConfigOption({
sessionId: SESSION_ID,
configId: "model",
@@ -655,6 +669,8 @@ describe("session config options", () => {
});
expect(applyFlagSettingsSpy).toHaveBeenCalledWith({ effortLevel: null });
// The clamp un-pins: a later switch back re-seeds from settings.
expect(session.effortPinnedByUser).toBe(false);
});
it("adds effort option when switching to a model that supports effort", async () => {
@@ -693,9 +709,11 @@ describe("session config options", () => {
it("clamps effort to valid value when new model has different supported levels", async () => {
const session = (agent as unknown as { sessions: Record<string, any> }).sessions[SESSION_ID];
// Set current effort to "max" (not supported by sonnet in our mock)
// Set current effort to "max" (not supported by sonnet in our mock) —
// pinned, as a user's ACP picker choice would be.
const effortOpt = session.configOptions.find((o: any) => o.id === "effort");
if (effortOpt) effortOpt.currentValue = "max";
session.effortPinnedByUser = true;
session.modelInfos = [
{
@@ -748,6 +766,48 @@ describe("session config options", () => {
// applyFlagSettings was called once for the effort change, but not again for the model switch
expect(applyFlagSettingsSpy).toHaveBeenCalledTimes(1);
});
it("seeds effort from the new model's persisted modelSettings entry on an unpinned switch", async () => {
// The CLI persists /effort per model (settings.modelSettings); with no
// user pin this session, the picker should show what the CLI will
// actually run on the new model, not drag the old model's value along.
const session = (agent as unknown as { sessions: Record<string, any> }).sessions[SESSION_ID];
session.settingsManager = {
getSettings: () => ({
effortLevel: "high",
modelSettings: { "claude-sonnet-4-6": { effortLevel: "low" } },
}),
};
const response = await agent.setSessionConfigOption({
sessionId: SESSION_ID,
configId: "model",
value: "claude-sonnet-4-6",
});
const effortOption = response.configOptions.find((o) => o.id === "effort");
expect(effortOption?.currentValue).toBe("low");
// Display-only: the CLI resolves persisted effort itself; pinning it at
// the flag layer would shadow the per-model values on later switches.
expect(applyFlagSettingsSpy).not.toHaveBeenCalled();
});
it("falls back to the top-level settings effort when the new model has no per-model entry", async () => {
const session = (agent as unknown as { sessions: Record<string, any> }).sessions[SESSION_ID];
session.settingsManager = {
getSettings: () => ({ effortLevel: "medium" }),
};
const response = await agent.setSessionConfigOption({
sessionId: SESSION_ID,
configId: "model",
value: "claude-sonnet-4-6",
});
const effortOption = response.configOptions.find((o) => o.id === "effort");
expect(effortOption?.currentValue).toBe("medium");
expect(applyFlagSettingsSpy).not.toHaveBeenCalled();
});
});
describe("bidirectional consistency", () => {

View File

@@ -64,7 +64,7 @@ export function mockSessionState(
modes: { currentModeId: "default", availableModes: [] },
models: { currentModelId: "default", availableModels: [] },
modelInfos: [],
settingsManager: { dispose: vi.fn() },
settingsManager: { dispose: vi.fn(), getSettings: () => ({}) },
accumulatedUsage: {
inputTokens: 0,
outputTokens: 0,

View File

@@ -2596,6 +2596,71 @@ describe("Agent/Task tool_result rendering from tool_use_result", () => {
});
});
const PARTIAL_NOTE =
"NOTE: this agent stopped at its 30-turn limit before finishing. The text below is PARTIAL output; treat it as incomplete. Send the agent a message (SendMessage) to let it continue from where it stopped.";
const PARTIAL_LABEL = "[Agent stopped at its turn limit — the output below is partial]";
it("replaces the maxTurns partial-output note in the structured lane", () => {
// A maxTurns-stopped subagent still ships status "completed", with a
// model-directed note block leading the report — the SendMessage
// instruction is meaningless over ACP, but the partial-output fact isn't.
const update = toolUpdateFromToolResult(rawResult, agentToolUse, false, {
...structured,
content: [
{ type: "text", text: PARTIAL_NOTE },
{ type: "text", text: "The partial report." },
],
});
expect(update.content).toEqual([
{ type: "content", content: { type: "text", text: PARTIAL_LABEL } },
{ type: "content", content: { type: "text", text: "The partial report." } },
]);
});
it("replaces the note variant for an agent that produced no report", () => {
const update = toolUpdateFromToolResult(rawResult, agentToolUse, false, {
...structured,
content: [
{
type: "text",
text: "NOTE: this agent stopped at its 5-turn limit before finishing. It was still calling tools and had produced no report.",
},
],
});
expect(update.content).toEqual([
{ type: "content", content: { type: "text", text: PARTIAL_LABEL } },
]);
});
it("replaces the note paragraph in the raw fallback and still strips the trailer", () => {
const result: ToolResultBlockParam = {
type: "tool_result",
tool_use_id: "toolu_agent",
content: [{ type: "text", text: `${PARTIAL_NOTE}\n\nThe report.${TRAILER}` }],
};
const update = toolUpdateFromToolResult(result, agentToolUse, false);
expect(update.content).toEqual([
{ type: "content", content: { type: "text", text: `${PARTIAL_LABEL}\n\nThe report.` } },
]);
});
it("leaves a report that merely mentions the note text mid-block alone", () => {
const update = toolUpdateFromToolResult(rawResult, agentToolUse, false, {
...structured,
content: [{ type: "text", text: `Quoting the harness: "${PARTIAL_NOTE}"` }],
});
expect(update.content).toEqual([
{
type: "content",
content: { type: "text", text: `Quoting the harness: "${PARTIAL_NOTE}"` },
},
]);
});
it("strips the trailer from the raw fallback when tool_use_result is absent", () => {
// Replayed sessions and older CLIs have no structured report; the
// tail-anchored strip is the only cleanup available there.

View File

@@ -30,6 +30,7 @@ import {
WebSearchOutput,
} from "@anthropic-ai/claude-agent-sdk/sdk-tools.js";
import {
DocumentBlockParam,
ImageBlockParam,
TextBlockParam,
ToolResultBlockParam,
@@ -69,6 +70,7 @@ import path from "node:path";
*/
type ToolResultContent =
| TextBlockParam
| DocumentBlockParam
| ImageBlockParam
| BetaImageBlockParam
| BetaToolReferenceBlock
@@ -574,6 +576,50 @@ function stripAgentTrailerFromContent(content: unknown): unknown {
return content;
}
/** Leading model-directed note the CLI prepends to a subagent's report when
* the agent stopped at its maxTurns limit (CLI 2.1.246+); the result still
* ships as `status: "completed"`. Two body variants follow this prefix, and
* the trailing "Send the agent a message (SendMessage) …" sentence is
* omitted for some agent types — anchor only the stable prefix so a format
* change makes the replacement stop matching rather than mangle a report. */
const PARTIAL_OUTPUT_NOTE = /^NOTE: this agent stopped at its \d+-turn limit before finishing\./;
/** Client-facing replacement: the partial-output fact matters to the user,
* but the SendMessage continuation instruction is model-directed and
* meaningless over ACP. */
const PARTIAL_OUTPUT_LABEL = "[Agent stopped at its turn limit — the output below is partial]";
/** Replace a leading partial-output note paragraph with the concise
* client-facing label, leaving the report that follows intact. */
function replacePartialNoteInText(text: string): string {
if (!PARTIAL_OUTPUT_NOTE.test(text)) return text;
const paragraphEnd = text.indexOf("\n\n");
const report = paragraphEnd === -1 ? "" : text.slice(paragraphEnd + 2).trimStart();
return report ? `${PARTIAL_OUTPUT_LABEL}\n\n${report}` : PARTIAL_OUTPUT_LABEL;
}
/** Apply {@link replacePartialNoteInText} to an Agent/Task result `content`.
* In the structured AgentOutput lane the note is its own leading text block;
* in the raw lane it is the first paragraph of the text — both reduce to
* transforming the first text block (or the plain string). */
function replacePartialOutputNote(content: unknown): unknown {
if (typeof content === "string") {
return replacePartialNoteInText(content);
}
if (Array.isArray(content) && content.length > 0) {
const [first, ...rest] = content;
if (
first !== null &&
typeof first === "object" &&
first.type === "text" &&
typeof first.text === "string"
) {
return [{ ...first, text: replacePartialNoteInText(first.text) }, ...rest];
}
}
return content;
}
export function toolUpdateFromToolResult(
toolResult:
| ToolResultBlockParam
@@ -847,7 +893,10 @@ export function toolUpdateFromToolResult(
structured.content.length > 0
) {
return toAcpContentUpdate(
structured.content,
// A maxTurns-stopped subagent still completes, with a model-directed
// partial-output note prepended to its report — swap it for a
// client-facing label (see PARTIAL_OUTPUT_NOTE).
replacePartialOutputNote(structured.content),
"is_error" in toolResult ? toolResult.is_error : false,
);
}
@@ -859,7 +908,9 @@ export function toolUpdateFromToolResult(
// format changes it simply stops matching and the full raw text
// renders, no worse than before.
return toAcpContentUpdate(
stripAgentTrailerFromContent(toolResult.content),
// Head and tail cleanups are independent: the partial-output note
// leads the raw text the same way it leads the structured content.
replacePartialOutputNote(stripAgentTrailerFromContent(toolResult.content)),
"is_error" in toolResult ? toolResult.is_error : false,
);
}
@@ -925,6 +976,14 @@ function formatWebSearchHit(hit: { title: string; url: string }): string {
return `${hit.title} (${hit.url})`;
}
/** Human-readable size for the document placeholder ("312 B", "2.4 KB",
* "1.3 MB"). */
function formatByteSize(bytes: number): string {
if (bytes < 1024) return `${bytes} B`;
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}
function toAcpContentUpdate(
content: any,
isError: boolean = false,
@@ -988,6 +1047,33 @@ function toAcpContentBlock(content: ToolResultContent, isError: boolean): Conten
: "[image: file reference]",
);
case "document": {
// A PDF Read delivers its raw `document` block inside the tool_result
// content (SDK 0.3.243 moved it here from a separate follow-up user
// message; the MultiRead documents lane always lived here). ACP has no
// document block and the base64 payload can be megabytes — render a
// compact placeholder, never the data, matching the CLI's own compact
// "Read PDF (size)" rendering.
const title =
typeof content.title === "string" && content.title.length > 0 ? ` "${content.title}"` : "";
const source = content.source;
switch (source.type) {
case "url":
return wrapText(`[document${title}: ${source.url}]`);
case "base64":
case "text":
// base64 inflates the byte count by 4/3; plain text is 1:1.
return wrapText(
`[document${title}: ${source.media_type}, ${formatByteSize(
source.type === "base64"
? Math.floor((source.data.length * 3) / 4)
: source.data.length,
)}]`,
);
default:
return wrapText(`[document${title}]`);
}
}
case "tool_reference":
return wrapText(`Tool: ${content.tool_name}`);
case "tool_search_tool_search_result":
@@ -1286,6 +1372,14 @@ interface DiffToolResponseHunk {
interface DiffToolResponse {
filePath?: string;
structuredPatch?: DiffToolResponseHunk[];
/** FileWriteOutput only (FileEditOutput carries no `type`): whether the
* write created the file or overwrote an existing one. */
type?: "create" | "update";
/** FileWriteOutput only: the content that was written. */
content?: string;
/** FileWriteOutput only: the pre-write content — null on create, or on an
* update whose previous content was too large to include. */
originalFile?: string | null;
}
/**
@@ -1330,6 +1424,35 @@ export function toolUpdateFromDiffToolResponse(toolResponse: unknown): {
}
}
// A Write `update` can arrive with an empty structuredPatch — nothing
// changed, the diff timed out, or the previous content was too large to
// diff (originalFile null; SDK 0.3.252 documents the lane). Returning `{}`
// would leave Write's optimistic tool_use-time content standing, and that
// was built with `oldText: null` — "creation" semantics — so an overwrite
// of a large existing file would render as creating it. Emit a truthful
// replacement instead. Gated on `type` so Edit (whose output carries no
// `type` and whose optimistic old/new diff is already truthful) keeps the
// empty-return behavior.
if (content.length === 0 && response.type === "update" && typeof response.content === "string") {
locations.push({ path: response.filePath });
content.push(
typeof response.originalFile === "string"
? {
type: "diff",
path: response.filePath,
oldText: response.originalFile,
newText: response.content,
}
: {
type: "content",
content: {
type: "text",
text: `Updated \`${response.filePath}\` (previous content too large to diff)`,
},
},
);
}
const result: { content?: ToolCallContent[]; locations?: ToolCallLocation[] } = {};
if (content.length > 0) result.content = content;
if (locations.length > 0) result.locations = locations;