Compare commits

...

41 Commits

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


##
[0.64.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.63.0...v0.64.0)
(2026-07-30)


### Features

* **deps:** Bump actions/checkout from 7.0.0 to 7.0.1
([#925](https://github.com/agentclientprotocol/claude-agent-acp/issues/925))
([8e099e8](8e099e8442))
* **deps:** Bump the minor group with 7 updates
([#928](https://github.com/agentclientprotocol/claude-agent-acp/issues/928))
([3f60921](3f60921959))


### Bug Fixes

* **steering:** add opt-in host-owned fallback
([#919](https://github.com/agentclientprotocol/claude-agent-acp/issues/919))
([43af4ec](43af4ec29e)),
closes
[#903](https://github.com/agentclientprotocol/claude-agent-acp/issues/903)

---
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-07-30 17:36:36 +02:00
ranzhenyu
43af4ec29e fix(steering): add opt-in host-owned fallback (#919)
Add an opt-in host-owned fallback for the `_session/steering` race while
preserving the existing default contract.

Fixes #903
2026-07-30 17:16:22 +02:00
nikita-ashihmin
d7a65ce1d0 Mark custom answer elicitation fields (#929)
## Why
AskUserQuestion emits a per-question free-text field that should be
rendered as the custom-answer companion for the preceding select
question. ACP clients need a stable metadata marker for that
relationship instead of guessing from field ids, titles, or
descriptions.

## What changed
- Mark each AskUserQuestion custom-answer companion field with
`_meta._askUserQuestionCustomAnswer`.
- Use the same non-agent-specific key that Codex and other bridges can
emit, so clients can parse one shared marker instead of maintaining
per-agent metadata names.
- Keep the existing response folding behavior unchanged.

## Tests
- Updated `includes a per-question optional free-text custom-answer
field` to assert the shared `_askUserQuestionCustomAnswer` marker on
each companion field.

```sh
npm test -- src/tests/elicitation.test.ts
```
2026-07-29 09:42:14 +00:00
dependabot[bot]
3f60921959 feat(deps): Bump the minor group with 7 updates (#928)
Bumps the minor group with 7 updates:

| Package | From | To |
| --- | --- | --- |
|
[@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node)
| `26.1.1` | `26.1.2` |
|
[@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk)
| `1.29.0` | `1.30.0` |
|
[@napi-rs/wasm-runtime](https://github.com/napi-rs/napi-rs/tree/HEAD/wasm-runtime)
| `1.1.6` | `1.2.0` |
| [acorn](https://github.com/acornjs/acorn) | `8.17.0` | `8.18.0` |
| [minimatch](https://github.com/isaacs/minimatch) | `10.2.5` | `10.2.6`
|
| [postcss](https://github.com/postcss/postcss) | `8.5.23` | `8.5.24` |
| [tinyrainbow](https://github.com/tinylibs/tinyrainbow) | `3.1.0` |
`3.1.1` |

Updates `@types/node` from 26.1.1 to 26.1.2
<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 `@modelcontextprotocol/sdk` from 1.29.0 to 1.30.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/modelcontextprotocol/typescript-sdk/releases">@​modelcontextprotocol/sdk's
releases</a>.</em></p>
<blockquote>
<h2>1.30.0</h2>
<h2>What's Changed</h2>
<ul>
<li>fix(server): prioritize zod issues and format them by <a
href="https://github.com/mozmo15"><code>@​mozmo15</code></a> in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/1503">modelcontextprotocol/typescript-sdk#1503</a></li>
<li>chore(ci): switch publish to OIDC trusted publishing by <a
href="https://github.com/felixweinberger"><code>@​felixweinberger</code></a>
in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/1839">modelcontextprotocol/typescript-sdk#1839</a></li>
<li>Add end-to-end test suite by <a
href="https://github.com/felixweinberger"><code>@​felixweinberger</code></a>
in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/2167">modelcontextprotocol/typescript-sdk#2167</a></li>
<li>v1 stdio buffer limit by <a
href="https://github.com/KKonstantinov"><code>@​KKonstantinov</code></a>
in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/2239">modelcontextprotocol/typescript-sdk#2239</a></li>
<li>fix: support Zod 3.25 method literals by <a
href="https://github.com/mattzcarey"><code>@​mattzcarey</code></a> in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/2368">modelcontextprotocol/typescript-sdk#2368</a></li>
<li>Validate Content-Type by parsed media type instead of substring
match (v1.x) by <a
href="https://github.com/felixweinberger"><code>@​felixweinberger</code></a>
in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/2444">modelcontextprotocol/typescript-sdk#2444</a></li>
<li>fix: send SSE keep-alive comment frames from Streamable HTTP server
transport (v1.x) by <a
href="https://github.com/mattzcarey"><code>@​mattzcarey</code></a> in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/2538">modelcontextprotocol/typescript-sdk#2538</a></li>
<li>fix(deps): widen <code>@​hono/node-server</code> past
GHSA-frvp-7c67-39w9 by <a
href="https://github.com/arimu1"><code>@​arimu1</code></a> in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/2549">modelcontextprotocol/typescript-sdk#2549</a></li>
<li>Fix SSE keep-alive timer lifecycle in Streamable HTTP server
transport (v1.x) by <a
href="https://github.com/felixweinberger"><code>@​felixweinberger</code></a>
in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/2547">modelcontextprotocol/typescript-sdk#2547</a></li>
<li>chore: bump version to 1.30.0 by <a
href="https://github.com/felixweinberger"><code>@​felixweinberger</code></a>
in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/2563">modelcontextprotocol/typescript-sdk#2563</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/mozmo15"><code>@​mozmo15</code></a> made
their first contribution in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/1503">modelcontextprotocol/typescript-sdk#1503</a></li>
<li><a href="https://github.com/arimu1"><code>@​arimu1</code></a> made
their first contribution in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/2549">modelcontextprotocol/typescript-sdk#2549</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/modelcontextprotocol/typescript-sdk/compare/v1.29.0...1.30.0">https://github.com/modelcontextprotocol/typescript-sdk/compare/v1.29.0...1.30.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="2d889f2b32"><code>2d889f2</code></a>
chore: bump version to 1.30.0 (<a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/issues/2563">#2563</a>)</li>
<li><a
href="e3f3daa12c"><code>e3f3daa</code></a>
Fix SSE keep-alive timer lifecycle in Streamable HTTP server transport
(v1.x)...</li>
<li><a
href="bb5a718cbf"><code>bb5a718</code></a>
fix(deps): widen <code>@​hono/node-server</code> past
GHSA-frvp-7c67-39w9 (<a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/issues/2549">#2549</a>)</li>
<li><a
href="1dad2634ce"><code>1dad263</code></a>
fix: send SSE keep-alive comment frames from Streamable HTTP server
transport...</li>
<li><a
href="69749aa508"><code>69749aa</code></a>
Validate Content-Type by parsed media type instead of substring match
(v1.x) ...</li>
<li><a
href="369513df7b"><code>369513d</code></a>
fix: support Zod 3.25 method literals (<a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/issues/2368">#2368</a>)</li>
<li><a
href="e7ee57c2f3"><code>e7ee57c</code></a>
v1 stdio buffer limit (<a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/issues/2239">#2239</a>)</li>
<li><a
href="c36e1ef5bb"><code>c36e1ef</code></a>
Add end-to-end test suite (<a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/issues/2167">#2167</a>)</li>
<li><a
href="bf1e022bd2"><code>bf1e022</code></a>
chore(ci): switch publish to OIDC trusted publishing (<a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/issues/1839">#1839</a>)</li>
<li><a
href="9edbab7a09"><code>9edbab7</code></a>
fix(server): prioritize zod issues and format them (<a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/issues/1503">#1503</a>)</li>
<li>See full diff in <a
href="https://github.com/modelcontextprotocol/typescript-sdk/compare/v1.29.0...1.30.0">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a
href="https://www.npmjs.com/~GitHub%20Actions">GitHub Actions</a>, a new
releaser for <code>@​modelcontextprotocol/sdk</code> since your current
version.</p>
</details>
<br />

Updates `@napi-rs/wasm-runtime` from 1.1.6 to 1.2.0
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/napi-rs/napi-rs/blob/main/wasm-runtime/CHANGELOG.md">@​napi-rs/wasm-runtime's
changelog</a>.</em></p>
<blockquote>
<h1><a
href="https://github.com/napi-rs/napi-rs/compare/@napi-rs/wasm-runtime@1.1.6...@napi-rs/wasm-runtime@1.2.0">1.2.0</a>
(2026-07-28)</h1>
<h3>Features</h3>
<ul>
<li><strong>cli:</strong> support non-threaded WASI targets (<a
href="https://redirect.github.com/napi-rs/napi-rs/issues/3353">#3353</a>)
(<a
href="b5ecfa3d6a">b5ecfa3</a>),
closes <a
href="https://redirect.github.com/napi-rs/napi-rs/issues/3352">#3352</a>
<a
href="https://redirect.github.com/napi-rs/napi-rs/issues/3400">#3400</a>
<a
href="https://redirect.github.com/napi-rs/napi-rs/issues/3405">#3405</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="fb91bcd84e"><code>fb91bcd</code></a>
chore(release): publish</li>
<li><a
href="c646a46fb7"><code>c646a46</code></a>
chore: upgrade memfs</li>
<li><a
href="b5ecfa3d6a"><code>b5ecfa3</code></a>
feat(cli): support non-threaded WASI targets (<a
href="https://github.com/napi-rs/napi-rs/tree/HEAD/wasm-runtime/issues/3353">#3353</a>)</li>
<li>See full diff in <a
href="https://github.com/napi-rs/napi-rs/commits/@napi-rs/wasm-runtime@1.2.0/wasm-runtime">compare
view</a></li>
</ul>
</details>
<br />

Updates `acorn` from 8.17.0 to 8.18.0
<details>
<summary>Commits</summary>
<ul>
<li><a
href="d788421b24"><code>d788421</code></a>
Mark version 8.18.0</li>
<li><a
href="b61cb9a4f2"><code>b61cb9a</code></a>
Add startLocation option</li>
<li><a
href="c8d515c7f9"><code>c8d515c</code></a>
Don't compute curLine when locations are off</li>
<li><a
href="a7bd3cd157"><code>a7bd3cd</code></a>
Reject an unparenthesized arrow function as the left operand of
<code>**</code></li>
<li><a
href="b7c31b626e"><code>b7c31b6</code></a>
Fix mistake in previous patch</li>
<li><a
href="5c4af50af1"><code>5c4af50</code></a>
Avoid scanning from start of document in loose parser's resetTo</li>
<li>See full diff in <a
href="https://github.com/acornjs/acorn/compare/8.17.0...8.18.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `minimatch` from 10.2.5 to 10.2.6
<details>
<summary>Commits</summary>
<ul>
<li><a
href="ded1bbd01b"><code>ded1bbd</code></a>
10.2.6</li>
<li><a
href="0215dfef07"><code>0215dfe</code></a>
update deps and lint</li>
<li><a
href="10968eae89"><code>10968ea</code></a>
improve test for .. eating drive letters</li>
<li>See full diff in <a
href="https://github.com/isaacs/minimatch/compare/v10.2.5...v10.2.6">compare
view</a></li>
</ul>
</details>
<br />

Updates `postcss` from 8.5.23 to 8.5.24
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/postcss/postcss/releases">postcss's
releases</a>.</em></p>
<blockquote>
<h2>8.5.24</h2>
<ul>
<li>Preserve the BOM after the processing (by <a
href="https://github.com/hdimer"><code>@​hdimer</code></a>).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/postcss/postcss/blob/main/CHANGELOG.md">postcss's
changelog</a>.</em></p>
<blockquote>
<h2>8.5.24</h2>
<ul>
<li>Preserve the BOM after the processing (by <a
href="https://github.com/hdimer"><code>@​hdimer</code></a>).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="0ebe8ad591"><code>0ebe8ad</code></a>
Release 8.5.24 version</li>
<li><a
href="73218c6424"><code>73218c6</code></a>
Update dependencies</li>
<li><a
href="9a114f62b0"><code>9a114f6</code></a>
Preserve the BOM when stringifying (<a
href="https://redirect.github.com/postcss/postcss/issues/2119">#2119</a>)</li>
<li><a
href="9069261912"><code>9069261</code></a>
Fix types check</li>
<li>See full diff in <a
href="https://github.com/postcss/postcss/compare/8.5.23...8.5.24">compare
view</a></li>
</ul>
</details>
<br />

Updates `tinyrainbow` from 3.1.0 to 3.1.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/tinylibs/tinyrainbow/releases">tinyrainbow's
releases</a>.</em></p>
<blockquote>
<h2>v3.1.1</h2>
<h2>What's Changed</h2>
<ul>
<li>chore: bump devDependencies &amp; add publish workflow by <a
href="https://github.com/43081j"><code>@​43081j</code></a> in <a
href="https://redirect.github.com/tinylibs/tinyrainbow/pull/7">tinylibs/tinyrainbow#7</a></li>
<li>test: add browser tests by <a
href="https://github.com/43081j"><code>@​43081j</code></a> in <a
href="https://redirect.github.com/tinylibs/tinyrainbow/pull/9">tinylibs/tinyrainbow#9</a></li>
<li>chore: only use clean-publish for cleaning by <a
href="https://github.com/43081j"><code>@​43081j</code></a> in <a
href="https://redirect.github.com/tinylibs/tinyrainbow/pull/10">tinylibs/tinyrainbow#10</a></li>
<li>chore: enable <code>sideEffects: false</code> by <a
href="https://github.com/43081j"><code>@​43081j</code></a> in <a
href="https://redirect.github.com/tinylibs/tinyrainbow/pull/11">tinylibs/tinyrainbow#11</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/43081j"><code>@​43081j</code></a> made
their first contribution in <a
href="https://redirect.github.com/tinylibs/tinyrainbow/pull/7">tinylibs/tinyrainbow#7</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/tinylibs/tinyrainbow/compare/v3.1.0...v3.1.1">https://github.com/tinylibs/tinyrainbow/compare/v3.1.0...v3.1.1</a></p>
<h2>v3.1.1-pre.1</h2>
<h2>What's Changed</h2>
<ul>
<li>chore: only use clean-publish for cleaning by <a
href="https://github.com/43081j"><code>@​43081j</code></a> in <a
href="https://redirect.github.com/tinylibs/tinyrainbow/pull/10">tinylibs/tinyrainbow#10</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/tinylibs/tinyrainbow/compare/v3.1.1-pre.0...v3.1.1-pre.1">https://github.com/tinylibs/tinyrainbow/compare/v3.1.1-pre.0...v3.1.1-pre.1</a></p>
<h2>v3.1.1-pre.0</h2>
<h2>What's Changed</h2>
<ul>
<li>chore: bump devDependencies &amp; add publish workflow by <a
href="https://github.com/43081j"><code>@​43081j</code></a> in <a
href="https://redirect.github.com/tinylibs/tinyrainbow/pull/7">tinylibs/tinyrainbow#7</a></li>
<li>test: add browser tests by <a
href="https://github.com/43081j"><code>@​43081j</code></a> in <a
href="https://redirect.github.com/tinylibs/tinyrainbow/pull/9">tinylibs/tinyrainbow#9</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/43081j"><code>@​43081j</code></a> made
their first contribution in <a
href="https://redirect.github.com/tinylibs/tinyrainbow/pull/7">tinylibs/tinyrainbow#7</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/tinylibs/tinyrainbow/compare/v3.1.0...v3.1.1-pre.0">https://github.com/tinylibs/tinyrainbow/compare/v3.1.0...v3.1.1-pre.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="c1ec6f409a"><code>c1ec6f4</code></a>
chore: enable <code>sideEffects: false</code> (<a
href="https://redirect.github.com/tinylibs/tinyrainbow/issues/11">#11</a>)</li>
<li><a
href="4abe3ae182"><code>4abe3ae</code></a>
chore: only use clean-publish for cleaning (<a
href="https://redirect.github.com/tinylibs/tinyrainbow/issues/10">#10</a>)</li>
<li><a
href="ccdf732245"><code>ccdf732</code></a>
test: add browser tests (<a
href="https://redirect.github.com/tinylibs/tinyrainbow/issues/9">#9</a>)</li>
<li><a
href="f591d01b3e"><code>f591d01</code></a>
chore: bump devDependencies &amp; add publish workflow (<a
href="https://redirect.github.com/tinylibs/tinyrainbow/issues/7">#7</a>)</li>
<li><a
href="5285a3c0f3"><code>5285a3c</code></a>
chore: add script</li>
<li>See full diff in <a
href="https://github.com/tinylibs/tinyrainbow/compare/v3.1.0...v3.1.1">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a
href="https://www.npmjs.com/~GitHub%20Actions">GitHub Actions</a>, a new
releaser for tinyrainbow since your current version.</p>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 16:41:06 +02:00
dependabot[bot]
8e099e8442 feat(deps): Bump actions/checkout from 7.0.0 to 7.0.1 (#925)
Bumps [actions/checkout](https://github.com/actions/checkout) from 7.0.0
to 7.0.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/checkout/releases">actions/checkout's
releases</a>.</em></p>
<blockquote>
<h2>v7.0.1</h2>
<h2>What's Changed</h2>
<ul>
<li>skip running unsafe pr check if input is default by <a
href="https://github.com/aiqiaoy"><code>@​aiqiaoy</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2518">actions/checkout#2518</a></li>
<li>trim only ascii whitespace for branch by <a
href="https://github.com/aiqiaoy"><code>@​aiqiaoy</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2521">actions/checkout#2521</a></li>
<li>escape values passed to --unset by <a
href="https://github.com/aiqiaoy"><code>@​aiqiaoy</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2530">actions/checkout#2530</a></li>
<li>Various dependency updates</li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/checkout/compare/v7...v7.0.1">https://github.com/actions/checkout/compare/v7...v7.0.1</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/actions/checkout/blob/main/CHANGELOG.md">actions/checkout's
changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h2>v7.0.1</h2>
<ul>
<li>Skip running unsafe pr check if input is default by <a
href="https://github.com/aiqiaoy"><code>@​aiqiaoy</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2518">actions/checkout#2518</a></li>
<li>Trim only ascii whitespace for branch by <a
href="https://github.com/aiqiaoy"><code>@​aiqiaoy</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2521">actions/checkout#2521</a></li>
<li>Escape values passed to --unset by <a
href="https://github.com/aiqiaoy"><code>@​aiqiaoy</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2530">actions/checkout#2530</a></li>
<li>Various dependency updates</li>
</ul>
<h2>v7.0.0</h2>
<ul>
<li>Block checking out fork PR for pull_request_target and workflow_run
by <a href="https://github.com/aiqiaoy"><code>@​aiqiaoy</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2454">actions/checkout#2454</a></li>
<li>Various dependency updates</li>
</ul>
<h2>v6.0.3</h2>
<ul>
<li>Fix checkout init for SHA-256 repositories by <a
href="https://github.com/yaananth"><code>@​yaananth</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2439">actions/checkout#2439</a></li>
<li>fix: expand merge commit SHA regex and add SHA-256 test cases by <a
href="https://github.com/yaananth"><code>@​yaananth</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2414">actions/checkout#2414</a></li>
</ul>
<h2>v6.0.2</h2>
<ul>
<li>Fix tag handling: preserve annotations and explicit fetch-tags by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2356">actions/checkout#2356</a></li>
</ul>
<h2>v6.0.1</h2>
<ul>
<li>Add worktree support for persist-credentials includeIf by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2327">actions/checkout#2327</a></li>
</ul>
<h2>v6.0.0</h2>
<ul>
<li>Persist creds to a separate file by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2286">actions/checkout#2286</a></li>
<li>Update README to include Node.js 24 support details and requirements
by <a href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/2248">actions/checkout#2248</a></li>
</ul>
<h2>v5.0.1</h2>
<ul>
<li>Port v6 cleanup to v5 by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2301">actions/checkout#2301</a></li>
</ul>
<h2>v5.0.0</h2>
<ul>
<li>Update actions checkout to use node 24 by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2226">actions/checkout#2226</a></li>
</ul>
<h2>v4.3.1</h2>
<ul>
<li>Port v6 cleanup to v4 by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2305">actions/checkout#2305</a></li>
</ul>
<h2>v4.3.0</h2>
<ul>
<li>docs: update README.md by <a
href="https://github.com/motss"><code>@​motss</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1971">actions/checkout#1971</a></li>
<li>Add internal repos for checking out multiple repositories by <a
href="https://github.com/mouismail"><code>@​mouismail</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1977">actions/checkout#1977</a></li>
<li>Documentation update - add recommended permissions to Readme by <a
href="https://github.com/benwells"><code>@​benwells</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2043">actions/checkout#2043</a></li>
<li>Adjust positioning of user email note and permissions heading by <a
href="https://github.com/joshmgross"><code>@​joshmgross</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2044">actions/checkout#2044</a></li>
<li>Update README.md by <a
href="https://github.com/nebuk89"><code>@​nebuk89</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2194">actions/checkout#2194</a></li>
<li>Update CODEOWNERS for actions by <a
href="https://github.com/TingluoHuang"><code>@​TingluoHuang</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/2224">actions/checkout#2224</a></li>
<li>Update package dependencies by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2236">actions/checkout#2236</a></li>
</ul>
<h2>v4.2.2</h2>
<ul>
<li><code>url-helper.ts</code> now leverages well-known environment
variables by <a href="https://github.com/jww3"><code>@​jww3</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/1941">actions/checkout#1941</a></li>
<li>Expand unit test coverage for <code>isGhes</code> by <a
href="https://github.com/jww3"><code>@​jww3</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1946">actions/checkout#1946</a></li>
</ul>
<h2>v4.2.1</h2>
<ul>
<li>Check out other refs/* by commit if provided, fall back to ref by <a
href="https://github.com/orhantoy"><code>@​orhantoy</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1924">actions/checkout#1924</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="3d3c42e5aa"><code>3d3c42e</code></a>
prep v7.0.1 release (<a
href="https://redirect.github.com/actions/checkout/issues/2531">#2531</a>)</li>
<li><a
href="28802689a1"><code>2880268</code></a>
escape values passed to --unset (<a
href="https://redirect.github.com/actions/checkout/issues/2530">#2530</a>)</li>
<li><a
href="12cd2235ef"><code>12cd223</code></a>
trim only ascii whitespace for branch (<a
href="https://redirect.github.com/actions/checkout/issues/2521">#2521</a>)</li>
<li><a
href="62661c4e71"><code>62661c4</code></a>
skip running unsafe pr check if input is default (<a
href="https://redirect.github.com/actions/checkout/issues/2518">#2518</a>)</li>
<li><a
href="e8d4307400"><code>e8d4307</code></a>
Bump the minor-actions-dependencies group with 2 updates (<a
href="https://redirect.github.com/actions/checkout/issues/2499">#2499</a>)</li>
<li><a
href="631c942040"><code>631c942</code></a>
eslint 9 (<a
href="https://redirect.github.com/actions/checkout/issues/2474">#2474</a>)</li>
<li><a
href="4f1f4aec02"><code>4f1f4ae</code></a>
Bump actions/upload-artifact from 4 to 7 (<a
href="https://redirect.github.com/actions/checkout/issues/2476">#2476</a>)</li>
<li><a
href="ba097532fb"><code>ba09753</code></a>
Bump actions/checkout from 6 to 7 (<a
href="https://redirect.github.com/actions/checkout/issues/2488">#2488</a>)</li>
<li><a
href="b9e0990d21"><code>b9e0990</code></a>
Bump docker/login-action from 3.3.0 to 4.2.0 (<a
href="https://redirect.github.com/actions/checkout/issues/2479">#2479</a>)</li>
<li><a
href="e8cb398be4"><code>e8cb398</code></a>
Bump docker/build-push-action from 6.5.0 to 7.2.0 (<a
href="https://redirect.github.com/actions/checkout/issues/2478">#2478</a>)</li>
<li>Additional commits viewable in <a
href="9c091bb21b...3d3c42e5aa">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=7.0.0&new-version=7.0.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-27 18:11:12 +02:00
acp-release-bot[bot]
15979bba79 chore(main): release 0.63.0 (#922)
🤖 I have created a release *beep* *boop*
---


##
[0.63.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.62.0...v0.63.0)
(2026-07-27)


### Features

* Update to claude agent sdk v0.3.220
([#921](https://github.com/agentclientprotocol/claude-agent-acp/issues/921))
([4c7b897](4c7b897183))


### Bug Fixes

* Only resolve a denied tool call the client was told about
([#923](https://github.com/agentclientprotocol/claude-agent-acp/issues/923))
([8f67b6a](8f67b6a92b)),
closes
[#918](https://github.com/agentclientprotocol/claude-agent-acp/issues/918)
* Report tool_progress heartbeats against the tool call they describe
([#916](https://github.com/agentclientprotocol/claude-agent-acp/issues/916))
([5559ba8](5559ba890c))
* **tools:** key Bash terminal metas off the announced tool_use id
([#917](https://github.com/agentclientprotocol/claude-agent-acp/issues/917))
([d060414](d0604140f9))

---
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-07-27 14:03:48 +02:00
Ben Brandt
8f67b6a92b fix: Only resolve a denied tool call the client was told about (#923)
`permission_denied` forwarded an SDK frame as a `tool_call_update`
without checking that the referenced `tool_call` was ever announced,
leaving it the last emitter without that check after #916.

The id is the executing call's own, and the frame is enqueued from
inside canUseTool, so it lands between the call's `tool_use` and the
`tool_result` carrying the rejection — verified against the bundled CLI,
where the deny short-circuit only emits for pre-ask decisions (rules,
mode, classifier, asyncAgent) and fires exactly once per denial. In that
flow the call is announced and in flight, so the guard changes nothing.

It changes the denials that arrive outside that window. The
cancelled-turn guard drops the assistant message carrying the tool_use,
so no `tool_call` is emitted, and a denial for it can still land
afterwards — the same premise the `tool_result` fallback in
`toAcpNotifications` gates on `wasEmitted` for. Ids leave
`emittedToolCalls` at `tool_result`, so the check also stops a denial
trailing its own result from flipping a call the client has seen finish
back to failed.

While here: a denial raised inside a subagent identifies it by
`agent_id`, never by the Agent/Task call that spawned it, so the update
went out unattributed while the `tool_call` it resolves sits in the
subagent's transcript. Resolve the parent through `liveBackgroundTasks`,
as the canUseTool path already does, and leave the update unattributed
rather than dropped when `task_started` was never seen for that agent
id.

`permission_denied` had no test coverage. Add cases for the in-flight
resolve, the reason fallback, the never-announced and already-resolved
drops, and subagent attribution with and without a recorded parent.

Fixes #918

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 10:49:30 +00:00
Daan De Meyer
5559ba890c fix: Report tool_progress heartbeats against the tool call they describe (#916)
Heartbeat beats reach the bridge under a derived
`<tool_use_id>-heartbeat-<n>` id that no `tool_call` was ever emitted
for, and the handler forwarded it verbatim. Clients that expect a
`tool_call_update` to reference a tool call they have been told about
saw a reference they could not resolve, and since the counter makes
every beat a fresh id, nothing deduplicated them: a ten-minute Bash call
produced roughly twenty phantom entries. Zed renders each as a failed
"Tool call not found" card; compozy/compozy#257 and
agent-of-empires/agent-of-empires#3084 report the same shape.

Strip the derived suffix so the beat is reported against the call it
describes, and drop it when that call is not in `emittedToolCalls`. Ids
enter that set at `tool_use` and leave it at `tool_result`, so the guard
covers both an id that was never announced and a straggling beat that
races past completion, which would otherwise flip a call the client has
seen finish back to `in_progress`.

The SDK sets `heartbeat: true` on these messages, but that flag alone
cannot recover the original id, and `parent_tool_use_id` is the subagent
parent rather than the call being reported on, so the suffix still has
to be stripped.

`tool_progress` had no test coverage. Add cases for the retarget across
a climbing counter, the not-in-flight drop, meta pass-through, and an
unsuffixed id being left alone.

Fixes #913

---------

Signed-off-by: Daan De Meyer <daan@amutable.com>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 10:16:20 +00:00
Patrick Clawson
d0604140f9 fix(tools): key Bash terminal metas off the announced tool_use id (#917)
## Problem

The `Bash` branch of `toolUpdateFromToolResult` derives its terminal id
from the result block and falls back to the empty string:

```ts
const terminalId = "tool_use_id" in toolResult ? String(toolResult.tool_use_id) : "";
```

On that fallback it still emits the whole terminal payload — `content:
[{ type: "terminal", terminalId: "" }]` plus `terminal_info`,
`terminal_output` and `terminal_exit`, every one keyed on `""`.

No client has a terminal under that id. `toolInfoFromToolUse` announces
the terminal as `toolUse.id` (`src/tools.ts:161`), so the empty id
matches nothing that was created. A client that buffers output for
terminals it has not been told about then holds the data indefinitely
behind an id that never arrives — Zed keeps it in
`pending_terminal_output` / `pending_terminal_exit` and drains only on a
matching create — so the command's output is silently stranded and the
user is left looking at an empty terminal.

It fails quietly, which is what makes it worth fixing: nothing logs, and
the update looks well-formed on the wire.

## Change

Prefer `toolUse.id` — the id that was actually announced — and keep the
result block's `tool_use_id` as a fallback. The two agree whenever both
are present, since the caller looks the tool_use up by that id, so this
only changes behaviour when one is missing.

When neither yields an id, fall through to the code-block rendering that
already exists immediately below rather than emitting a terminal
reference the client cannot resolve.

## Tests

Three cases added to `src/tests/tools.test.ts`:

- metas are keyed off the tool_use id even when the result block
disagrees
- the result block's `tool_use_id` is still used when the tool_use is
unavailable
- no id at all degrades to the `console` code block, with no `_meta`

Two of the three fail on `main` and pass with the change; the third
guards the preserved fallback. `npm run build`, `npm run check` and
`vitest run src/tests/tools.test.ts` (112 passed) are clean.

Note: `src/tests/create-session-options.test.ts > session/load seeds the
window from the resumed session's getContextUsage report` fails on a
clean `main` checkout here too — pre-existing and unrelated.

## How I got here

Found while tracing the "Tool call not found" rows in #913 — unrelated
cause, same file. The `terminal_id: ""` path came up while ruling the
terminal-streaming path out as the source of those.

---------

Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 10:05:38 +00:00
Ben Brandt
4c7b897183 feat: Update to claude agent sdk v0.3.220 (#921)
- **feat: Update to claude-agent-sdk 0.3.220**
- **Explain unavailable Fast mode reasons**
2026-07-27 09:38:15 +00:00
nikita-ashihmin
8663170f18 Add structured Bash titles and nested subagent transcripts (#881)
## Motivation

ACP clients currently have two gaps when rendering Claude Code activity:

1. Bash tool calls expose the shell command as the standard ACP title,
but the concise human-readable `description` produced by Claude Code is
lost.
2. ACP 1.2 has no standard subagent tool kind or nested-message
relationship, so subagent prose and child tools are either flattened
into the main conversation or filtered out entirely.

This PR adds namespaced metadata and an explicit client capability so
richer clients can render both cases without changing the standard ACP
schema or regressing legacy clients.

## What changed

### Bash descriptions

- Keeps the shell command in the standard ACP `title`, preserving
command-oriented clients.
- Exposes the optional Claude Code Bash `description` as
`_meta.claudeCode.title`.
- Applies the metadata consistently to eager permission tool calls,
normal tool calls, and streamed input refinements.
- Falls back to the command title when no description is available.

### Nested subagent transcripts

- Adds the opt-in client capability
`clientCapabilities._meta["subagent-transcript"] = true`.
- Enables the Claude Agent SDK `forwardSubagentText` option when the
client advertises support.
- Preserves the pre-existing caller-provided `forwardSubagentText: true`
option for compatibility.
- Marks Agent and legacy Task launches with `_meta.claudeCode.subagent =
true`.
- Relates nested text, thinking, and child tool calls to the launching
Agent/Task call through `_meta.claudeCode.parentToolUseId`.
- Preserves parent attribution during session-history replay, not only
during live streaming.
- Handles nested Agent-to-Agent launches without inferring subagents
from the generic ACP `think` kind.

## Compatibility

Clients that do not advertise `subagent-transcript` retain the previous
behavior: subagent text and thinking remain filtered from the top-level
feed.

The normal Agent/Task tool result is still emitted in every mode as the
protocol-compatible fallback. No existing result or standard ACP field
is removed or redefined.

## Validation

- `npm run build`
- `npm run check`
- `npm run test:run`
- 623 tests passed, 20 skipped

Coverage includes live and replayed nested text/thinking, child tool
attribution, Agent/Task launch markers, nested Agent launches, legacy
filtering, and SDK-option compatibility.
2026-07-27 07:22:07 +00:00
acp-release-bot[bot]
53a0c36ce3 chore(main): release 0.62.0 (#901)
🤖 I have created a release *beep* *boop*
---


##
[0.62.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.61.0...v0.62.0)
(2026-07-24)


### Features

* **deps:** Bump @hono/node-server from 1.19.14 to 1.19.15
([#908](https://github.com/agentclientprotocol/claude-agent-acp/issues/908))
([5193604](51936049e1))
* **deps:** Bump media-typer from 1.1.0 to 1.1.1
([#909](https://github.com/agentclientprotocol/claude-agent-acp/issues/909))
([5d35001](5d35001563))
* **deps:** Bump the minor group with 2 updates
([#900](https://github.com/agentclientprotocol/claude-agent-acp/issues/900))
([809d41c](809d41c6b7))
* **deps:** Bump the minor group with 2 updates
([#907](https://github.com/agentclientprotocol/claude-agent-acp/issues/907))
([14d0627](14d06273c0))
* Update to claude-agent-sdk 0.3.218
([#904](https://github.com/agentclientprotocol/claude-agent-acp/issues/904))
([8cbaf97](8cbaf97254))

---
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-07-24 19:44:48 +02:00
Mark Tkachenko
c91f943f86 @anthropic-ai/claude-agent-sdk 0.3.218 -> 0.3.219 (#912) 2026-07-24 19:40:43 +02:00
dependabot[bot]
5d35001563 feat(deps): Bump media-typer from 1.1.0 to 1.1.1 (#909)
Bumps [media-typer](https://github.com/jshttp/media-typer) from 1.1.0 to
1.1.1.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="3aa86fae28"><code>3aa86fa</code></a>
1.1.1</li>
<li><a
href="a1155b00d3"><code>a1155b0</code></a>
Allow empty strings (<a
href="https://redirect.github.com/jshttp/media-typer/issues/58">#58</a>)</li>
<li><a
href="2fd770228e"><code>2fd7702</code></a>
build(deps): bump github/codeql-action from 4.32.0 to 4.32.4 (<a
href="https://redirect.github.com/jshttp/media-typer/issues/46">#46</a>)</li>
<li><a
href="e20c9f1b1b"><code>e20c9f1</code></a>
build(deps): bump actions/upload-artifact from 5.0.0 to 7.0.0 (<a
href="https://redirect.github.com/jshttp/media-typer/issues/45">#45</a>)</li>
<li><a
href="fbf0ebcbd0"><code>fbf0ebc</code></a>
build(deps): bump actions/checkout from 5.0.1 to 6.0.2 (<a
href="https://redirect.github.com/jshttp/media-typer/issues/43">#43</a>)</li>
<li><a
href="59a40a2399"><code>59a40a2</code></a>
build(deps): bump github/codeql-action from 4.31.5 to 4.32.0 (<a
href="https://redirect.github.com/jshttp/media-typer/issues/44">#44</a>)</li>
<li><a
href="eadf79911a"><code>eadf799</code></a>
build(deps): bump github/codeql-action from 4.31.2 to 4.31.5 (<a
href="https://redirect.github.com/jshttp/media-typer/issues/37">#37</a>)</li>
<li><a
href="24697a994b"><code>24697a9</code></a>
build(deps): bump actions/checkout from 2.7.0 to 6.0.0 (<a
href="https://redirect.github.com/jshttp/media-typer/issues/38">#38</a>)</li>
<li><a
href="10f5ba17a1"><code>10f5ba1</code></a>
build(deps): bump github/codeql-action from 3.29.11 to 4.31.2 (<a
href="https://redirect.github.com/jshttp/media-typer/issues/35">#35</a>)</li>
<li><a
href="659c5b2233"><code>659c5b2</code></a>
build(deps): bump actions/upload-artifact from 3.1.3 to 5.0.0 (<a
href="https://redirect.github.com/jshttp/media-typer/issues/36">#36</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/jshttp/media-typer/compare/v1.1.0...v1.1.1">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a
href="https://www.npmjs.com/~blakeembrey">blakeembrey</a>, a new
releaser for media-typer since your current version.</p>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=media-typer&package-manager=npm_and_yarn&previous-version=1.1.0&new-version=1.1.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-24 16:51:32 +02:00
dependabot[bot]
51936049e1 feat(deps): Bump @hono/node-server from 1.19.14 to 1.19.15 (#908)
Bumps [@hono/node-server](https://github.com/honojs/node-server) from
1.19.14 to 1.19.15.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/honojs/node-server/commits">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@hono/node-server&package-manager=npm_and_yarn&previous-version=1.19.14&new-version=1.19.15)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-24 16:48:33 +02:00
dependabot[bot]
14d06273c0 feat(deps): Bump the minor group with 2 updates (#907)
Bumps the minor group with 2 updates:
[@anthropic-ai/sdk](https://github.com/anthropics/anthropic-sdk-typescript)
and [hono](https://github.com/honojs/hono).

Updates `@anthropic-ai/sdk` from 0.113.0 to 0.114.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.114.0</h2>
<h2>0.114.0 (2026-07-23)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.113.0...sdk-v0.114.0">sdk-v0.113.0...sdk-v0.114.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> add new stop reason
'model_context_window_exceeded' (<a
href="1ec71c165d">1ec71c1</a>)</li>
</ul>
</blockquote>
</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.114.0 (2026-07-23)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.113.0...sdk-v0.114.0">sdk-v0.113.0...sdk-v0.114.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> add new stop reason
'model_context_window_exceeded' (<a
href="1ec71c165d">1ec71c1</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="2d314b4127"><code>2d314b4</code></a>
chore: release main</li>
<li><a
href="3d051dfe49"><code>3d051df</code></a>
feat(api): add new stop reason 'model_context_window_exceeded'</li>
<li>See full diff in <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.113.0...sdk-v0.114.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `hono` from 4.12.31 to 4.12.32
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/honojs/hono/releases">hono's
releases</a>.</em></p>
<blockquote>
<h2>v4.12.32</h2>
<h2>What's Changed</h2>
<ul>
<li>ci: enable reports for type &amp; bundle size check in <a
href="https://redirect.github.com/honojs/hono/pull/5148">honojs/hono#5148</a></li>
<li>fix(aws-lambda): add jwt and lambda authorizer types for API Gateway
v2 in <a
href="https://redirect.github.com/honojs/hono/pull/5142">honojs/hono#5142</a></li>
<li>fix(sse): emit empty id field to reset Last-Event-ID in <a
href="https://redirect.github.com/honojs/hono/pull/5138">honojs/hono#5138</a></li>
<li>test(cloudflare-workers): add coverage for onClose, onError, send,
and close in Cloudflare Workers websocket adapter in <a
href="https://redirect.github.com/honojs/hono/pull/5145">honojs/hono#5145</a></li>
<li>fix: use <code>Object.create(null)</code> when parsing query,
headers, and params in <a
href="https://redirect.github.com/honojs/hono/pull/5161">honojs/hono#5161</a></li>
<li>fix(secure-headers): keep CSP callbacks scoped to their header in <a
href="https://redirect.github.com/honojs/hono/pull/5147">honojs/hono#5147</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/honojs/hono/compare/v4.12.31...v4.12.32">https://github.com/honojs/hono/compare/v4.12.31...v4.12.32</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="26d8e42bff"><code>26d8e42</code></a>
4.12.32</li>
<li><a
href="402eb3abe5"><code>402eb3a</code></a>
fix(secure-headers): keep CSP callbacks scoped to their header (<a
href="https://redirect.github.com/honojs/hono/issues/5147">#5147</a>)</li>
<li><a
href="c85aead088"><code>c85aead</code></a>
fix: use <code>Object.create(null)</code> when parsing query, headers,
and params (<a
href="https://redirect.github.com/honojs/hono/issues/5161">#5161</a>)</li>
<li><a
href="a88c89dac6"><code>a88c89d</code></a>
test(cloudflare-workers): add coverage for onClose, onError, send, and
close ...</li>
<li><a
href="44f884321a"><code>44f8843</code></a>
fix(sse): emit empty id field to reset Last-Event-ID (<a
href="https://redirect.github.com/honojs/hono/issues/5138">#5138</a>)</li>
<li><a
href="e36f57dac0"><code>e36f57d</code></a>
fix(aws-lambda): add jwt and lambda authorizer types for API Gateway v2
(<a
href="https://redirect.github.com/honojs/hono/issues/5142">#5142</a>)</li>
<li><a
href="bf8608cb77"><code>bf8608c</code></a>
ci: enable reports for type &amp; bundle size check (<a
href="https://redirect.github.com/honojs/hono/issues/5148">#5148</a>)</li>
<li>See full diff in <a
href="https://github.com/honojs/hono/compare/v4.12.31...v4.12.32">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-24 16:48:16 +02:00
Ben Brandt
8cbaf97254 feat: Update to claude-agent-sdk 0.3.218 (#904) 2026-07-23 15:28:36 +02:00
dependabot[bot]
809d41c6b7 feat(deps): Bump the minor group with 2 updates (#900)
Bumps the minor group with 2 updates:
[@eslint-community/eslint-utils](https://github.com/eslint-community/eslint-utils)
and
[eslint-visitor-keys](https://github.com/eslint/js/tree/HEAD/packages/eslint-visitor-keys).

Updates `@eslint-community/eslint-utils` from 4.10.0 to 4.10.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/eslint-community/eslint-utils/releases">@​eslint-community/eslint-utils's
releases</a>.</em></p>
<blockquote>
<h2>v4.10.1</h2>
<h2><a
href="https://github.com/eslint-community/eslint-utils/compare/v4.10.0...v4.10.1">4.10.1</a>
(2026-07-22)</h2>
<h3>Bug Fixes</h3>
<ul>
<li>add missing typings <code>@​types/node</code> (<a
href="https://redirect.github.com/eslint-community/eslint-utils/issues/336">#336</a>)
(<a
href="f16575f3ab">f16575f</a>)</li>
<li>Revert &quot;feat: upgrade eslint-visitor-keys v5&quot; (<a
href="https://redirect.github.com/eslint-community/eslint-utils/issues/333">#333</a>)
(<a
href="c160dba12e">c160dba</a>)</li>
<li>Revert &quot;feat!: update Node.js version support to drop Node
18&quot; (<a
href="https://redirect.github.com/eslint-community/eslint-utils/issues/332">#332</a>)
(<a
href="330a309511">330a309</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="f16575f3ab"><code>f16575f</code></a>
fix: add missing typings <code>@​types/node</code> (<a
href="https://redirect.github.com/eslint-community/eslint-utils/issues/336">#336</a>)</li>
<li><a
href="9594631b0b"><code>9594631</code></a>
chore: run Prettier</li>
<li><a
href="330a309511"><code>330a309</code></a>
fix: Revert &quot;feat!: update Node.js version support to drop Node
18&quot; (<a
href="https://redirect.github.com/eslint-community/eslint-utils/issues/332">#332</a>)</li>
<li><a
href="c160dba12e"><code>c160dba</code></a>
fix: Revert &quot;feat: upgrade eslint-visitor-keys v5&quot; (<a
href="https://redirect.github.com/eslint-community/eslint-utils/issues/333">#333</a>)</li>
<li>See full diff in <a
href="https://github.com/eslint-community/eslint-utils/compare/v4.10.0...v4.10.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `eslint-visitor-keys` from 5.0.1 to 3.4.3
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/eslint/js/blob/main/packages/eslint-visitor-keys/CHANGELOG.md">eslint-visitor-keys's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/eslint/js/compare/eslint-visitor-keys-v5.0.0...eslint-visitor-keys-v5.0.1">5.0.1</a>
(2026-02-20)</h2>
<h3>Bug Fixes</h3>
<ul>
<li>improve tree-shaking (<a
href="https://redirect.github.com/eslint/js/issues/725">#725</a>) (<a
href="b699f3200c">b699f32</a>)</li>
</ul>
<h2><a
href="https://github.com/eslint/js/compare/eslint-visitor-keys-v4.2.1...eslint-visitor-keys-v5.0.0">5.0.0</a>
(2025-11-07)</h2>
<h3>⚠ BREAKING CHANGES</h3>
<ul>
<li>Require Node.js <code>^20.19.0 || ^22.13.0 || &gt;=24</code> (<a
href="https://redirect.github.com/eslint/js/issues/696">#696</a>)</li>
</ul>
<h3>Features</h3>
<ul>
<li>Require Node.js <code>^20.19.0 || ^22.13.0 || &gt;=24</code> (<a
href="https://redirect.github.com/eslint/js/issues/696">#696</a>) (<a
href="9c84ecb6be">9c84ecb</a>)</li>
</ul>
<h2><a
href="https://github.com/eslint/js/compare/eslint-visitor-keys-v4.2.0...eslint-visitor-keys-v4.2.1">4.2.1</a>
(2025-06-09)</h2>
<h3>Bug Fixes</h3>
<ul>
<li>order visitor keys for <code>ExportSpecifier</code> in source code
order (<a
href="https://redirect.github.com/eslint/js/issues/656">#656</a>) (<a
href="dbad7d8aea">dbad7d8</a>)</li>
</ul>
<h2><a
href="https://github.com/eslint/js/compare/eslint-visitor-keys-v4.1.0...eslint-visitor-keys-v4.2.0">4.2.0</a>
(2024-10-29)</h2>
<h3>Features</h3>
<ul>
<li>add support for Import Attributes and RegExp Modifiers (<a
href="https://redirect.github.com/eslint/js/issues/639">#639</a>) (<a
href="2fd422278b">2fd4222</a>)</li>
</ul>
<h2><a
href="https://github.com/eslint/js/compare/eslint-visitor-keys-v4.0.0...eslint-visitor-keys-v4.1.0">4.1.0</a>
(2024-09-27)</h2>
<h3>Features</h3>
<ul>
<li>add the <code>eslint-visitor-keys</code> package (<a
href="https://redirect.github.com/eslint/js/issues/620">#620</a>) (<a
href="5903929cb6">5903929</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>Update dependencies to avoid build failure (<a
href="https://redirect.github.com/eslint/js/issues/631">#631</a>) (<a
href="e8cd107d73">e8cd107</a>)</li>
</ul>
<h2><a
href="https://github.com/eslint/eslint-visitor-keys/compare/v3.4.3...v4.0.0">4.0.0</a>
(2024-02-08)</h2>
<h3>⚠ BREAKING CHANGES</h3>
<ul>
<li>Require Node.js <code>^18.18.0 || ^20.9.0 || &gt;=21.1.0</code> (<a
href="https://redirect.github.com/eslint/eslint-visitor-keys/issues/63">#63</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/eslint/js/commits/v3.4.3/packages/eslint-visitor-keys">compare
view</a></li>
</ul>
</details>
<details>
<summary>Install script changes</summary>
<p>This version adds <code>prepare</code> script that runs during
installation. Review the package contents before updating.</p>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-22 17:14:56 +02:00
acp-release-bot[bot]
c19bddcf79 chore(main): release 0.61.0 (#898)
🤖 I have created a release *beep* *boop*
---


##
[0.61.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.60.0...v0.61.0)
(2026-07-22)


### Features

* **deps:** Bump actions/setup-node from 6.4.0 to 7.0.0
([#897](https://github.com/agentclientprotocol/claude-agent-acp/issues/897))
([d9bd36d](d9bd36d8b0))
* **deps:** Update to @anthropic-ai/claude-agent-sdk 0.3.217
([#899](https://github.com/agentclientprotocol/claude-agent-acp/issues/899))
([edf3af0](edf3af043b))

---
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-07-22 15:42:44 +02:00
dependabot[bot]
edf3af043b feat(deps): Update to @anthropic-ai/claude-agent-sdk 0.3.217 (#899)
Bumps the minor group with 22 updates:

| Package | From | To |
| --- | --- | --- |
|
[@anthropic-ai/claude-agent-sdk](https://github.com/anthropics/claude-agent-sdk-typescript)
| `0.3.215` | `0.3.216` |
|
[@anthropic-ai/sdk](https://github.com/anthropics/anthropic-sdk-typescript)
| `0.112.3` | `0.112.4` |
|
[@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin)
| `8.64.0` | `8.65.0` |
|
[@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser)
| `8.64.0` | `8.65.0` |
| [prettier](https://github.com/prettier/prettier) | `3.9.5` | `3.9.6` |
| @anthropic-ai/claude-agent-sdk-darwin-arm64 | `0.3.215` | `0.3.216` |
| @anthropic-ai/claude-agent-sdk-darwin-x64 | `0.3.215` | `0.3.216` |
| @anthropic-ai/claude-agent-sdk-linux-arm64 | `0.3.215` | `0.3.216` |
| @anthropic-ai/claude-agent-sdk-linux-arm64-musl | `0.3.215` |
`0.3.216` |
| @anthropic-ai/claude-agent-sdk-linux-x64 | `0.3.215` | `0.3.216` |
| @anthropic-ai/claude-agent-sdk-linux-x64-musl | `0.3.215` | `0.3.216`
|
| @anthropic-ai/claude-agent-sdk-win32-arm64 | `0.3.215` | `0.3.216` |
| @anthropic-ai/claude-agent-sdk-win32-x64 | `0.3.215` | `0.3.216` |
|
[@typescript-eslint/project-service](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/project-service)
| `8.64.0` | `8.65.0` |
|
[@typescript-eslint/scope-manager](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/scope-manager)
| `8.64.0` | `8.65.0` |
|
[@typescript-eslint/tsconfig-utils](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/tsconfig-utils)
| `8.64.0` | `8.65.0` |
|
[@typescript-eslint/type-utils](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/type-utils)
| `8.64.0` | `8.65.0` |
|
[@typescript-eslint/types](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/types)
| `8.64.0` | `8.65.0` |
|
[@typescript-eslint/typescript-estree](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-estree)
| `8.64.0` | `8.65.0` |
|
[@typescript-eslint/utils](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/utils)
| `8.64.0` | `8.65.0` |
|
[@typescript-eslint/visitor-keys](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/visitor-keys)
| `8.64.0` | `8.65.0` |
| [postcss](https://github.com/postcss/postcss) | `8.5.20` | `8.5.21` |

Updates `@anthropic-ai/claude-agent-sdk` from 0.3.215 to 0.3.216
<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.216</h2>
<ul>
<li>Added optional <code>skippedLinks</code> count to
<code>rewindFiles</code> responses for paths the rewind safety guards
refused to restore or delete</li>
<li>Added <code>tool_result_meta</code> sidecar to user messages
(<code>non_execution_kind</code>, <code>user_feedback</code>) so
consumers can classify denied, interrupted, or cancelled tool calls
without string-matching result prose</li>
<li>Added optional <code>user_message_uuid</code> and
<code>request_sent_wall_ms</code> fields to the success result message
for cross-host request-latency correlation</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="e77c325bdb"><code>e77c325</code></a>
chore: Update CHANGELOG.md</li>
<li>See full diff in <a
href="https://github.com/anthropics/claude-agent-sdk-typescript/compare/v0.3.215...v0.3.216">compare
view</a></li>
</ul>
</details>
<br />

Updates `@anthropic-ai/sdk` from 0.112.3 to 0.112.4
<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.112.4</h2>
<h2>0.112.4 (2026-07-20)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.112.3...sdk-v0.112.4">sdk-v0.112.3...sdk-v0.112.4</a></p>
<h3>Bug Fixes</h3>
<ul>
<li><strong>aws:</strong> preserve AWS options and auth mode across
withOptions() (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/214">#214</a>)
(<a
href="3f7665c541">3f7665c</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><strong>docs:</strong> small updates (<a
href="97f68a88e1">97f68a8</a>)</li>
</ul>
</blockquote>
</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.112.4 (2026-07-20)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.112.3...sdk-v0.112.4">sdk-v0.112.3...sdk-v0.112.4</a></p>
<h3>Bug Fixes</h3>
<ul>
<li><strong>aws:</strong> preserve AWS options and auth mode across
withOptions() (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/214">#214</a>)
(<a
href="3f7665c541">3f7665c</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><strong>docs:</strong> small updates (<a
href="97f68a88e1">97f68a8</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="3e9a2e1e68"><code>3e9a2e1</code></a>
chore(aws): raise minimum <code>@​anthropic-ai/sdk</code> version to
0.112.4</li>
<li><a
href="1cdfa7f40e"><code>1cdfa7f</code></a>
chore: release main</li>
<li><a
href="0cd535b3ec"><code>0cd535b</code></a>
fix(aws): remove duplicate loggerFor import breaking the build (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/235">#235</a>)</li>
<li><a
href="ff7d62078c"><code>ff7d620</code></a>
fix(aws): preserve AWS options and auth mode across withOptions() (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/214">#214</a>)</li>
<li><a
href="0b39044da0"><code>0b39044</code></a>
chore(docs): small updates</li>
<li>See full diff in <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.112.3...sdk-v0.112.4">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/eslint-plugin` from 8.64.0 to 8.65.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.65.0</h2>
<h2>8.65.0 (2026-07-20)</h2>
<h3>🚀 Features</h3>
<ul>
<li>add warning when TS 7 is detected (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12529">#12529</a>)</li>
<li><strong>eslint-plugin:</strong> [no-restricted-imports] deprecate
extension rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12527">#12527</a>,
<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/issues/19562">#19562</a>,
<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/issues/11889">#11889</a>)</li>
<li><strong>eslint-plugin:</strong> [no-shadow] specialized error on
enum declaration and member shadowing (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12578">#12578</a>)</li>
<li><strong>parser:</strong> add onUnsupportedTypeScriptVersion option
to error on unsupported TypeScript versions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12465">#12465</a>)</li>
<li><strong>typescript-estree:</strong> throw for invalid import defer
syntax (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12552">#12552</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [prefer-string-starts-ends-with]
handle escaped $ ending regex literals (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12515">#12515</a>)</li>
<li><strong>eslint-plugin:</strong> [unbound-method] report unbound
methods accessed via member expression on union types (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12448">#12448</a>)</li>
<li><strong>eslint-plugin:</strong>
[no-unnecessary-parameter-property-assignment] don't flag computed
assignments with a variable key (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12568">#12568</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Goldberg ✨</li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>Serhii Leniv <a
href="https://github.com/Serhii-Leniv"><code>@​Serhii-Leniv</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.65.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.65.0 (2026-07-20)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-shadow] specialized error on
enum declaration and member shadowing (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12578">#12578</a>)</li>
<li>add warning when TS 7 is detected (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12529">#12529</a>)</li>
<li><strong>eslint-plugin:</strong> [no-restricted-imports] deprecate
extension rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12527">#12527</a>,
<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/issues/19562">#19562</a>,
<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/issues/11889">#11889</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong>
[no-unnecessary-parameter-property-assignment] don't flag computed
assignments with a variable key (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12568">#12568</a>)</li>
<li><strong>eslint-plugin:</strong> [unbound-method] report unbound
methods accessed via member expression on union types (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12448">#12448</a>)</li>
<li><strong>eslint-plugin:</strong> [prefer-string-starts-ends-with]
handle escaped $ ending regex literals (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12515">#12515</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Goldberg ✨</li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>Serhii Leniv <a
href="https://github.com/Serhii-Leniv"><code>@​Serhii-Leniv</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.65.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="63ba81b6ac"><code>63ba81b</code></a>
chore(release): publish 8.65.0</li>
<li><a
href="aa602bdf3f"><code>aa602bd</code></a>
fix(eslint-plugin): [no-unnecessary-parameter-property-assignment] don't
flag...</li>
<li><a
href="1e90d67332"><code>1e90d67</code></a>
feat(eslint-plugin): [no-shadow] specialized error on enum declaration
and me...</li>
<li><a
href="b32fb34305"><code>b32fb34</code></a>
docs: fix broken code samples in no-extraneous-class and
prefer-function-type...</li>
<li><a
href="eaf457631a"><code>eaf4576</code></a>
feat: add warning when TS 7 is detected (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin/issues/12529">#12529</a>)</li>
<li><a
href="18c01c7049"><code>18c01c7</code></a>
feat(eslint-plugin): [no-restricted-imports] deprecate extension rule
(<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin/issues/12527">#12527</a>)</li>
<li><a
href="96e8fe2558"><code>96e8fe2</code></a>
fix(eslint-plugin): [unbound-method] report unbound methods accessed via
memb...</li>
<li><a
href="0d06406f31"><code>0d06406</code></a>
chore: add attw validation to repo (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin/issues/12437">#12437</a>)</li>
<li><a
href="9d9973bb18"><code>9d9973b</code></a>
fix(eslint-plugin): [prefer-string-starts-ends-with] handle escaped $
ending ...</li>
<li><a
href="c2386e49ec"><code>c2386e4</code></a>
chore(deps): update dependency prettier to v3.9.5 (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin/issues/12486">#12486</a>)</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.65.0/packages/eslint-plugin">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/parser` from 8.64.0 to 8.65.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.65.0</h2>
<h2>8.65.0 (2026-07-20)</h2>
<h3>🚀 Features</h3>
<ul>
<li>add warning when TS 7 is detected (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12529">#12529</a>)</li>
<li><strong>eslint-plugin:</strong> [no-restricted-imports] deprecate
extension rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12527">#12527</a>,
<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/issues/19562">#19562</a>,
<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/issues/11889">#11889</a>)</li>
<li><strong>eslint-plugin:</strong> [no-shadow] specialized error on
enum declaration and member shadowing (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12578">#12578</a>)</li>
<li><strong>parser:</strong> add onUnsupportedTypeScriptVersion option
to error on unsupported TypeScript versions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12465">#12465</a>)</li>
<li><strong>typescript-estree:</strong> throw for invalid import defer
syntax (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12552">#12552</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [prefer-string-starts-ends-with]
handle escaped $ ending regex literals (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12515">#12515</a>)</li>
<li><strong>eslint-plugin:</strong> [unbound-method] report unbound
methods accessed via member expression on union types (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12448">#12448</a>)</li>
<li><strong>eslint-plugin:</strong>
[no-unnecessary-parameter-property-assignment] don't flag computed
assignments with a variable key (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12568">#12568</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Goldberg ✨</li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>Serhii Leniv <a
href="https://github.com/Serhii-Leniv"><code>@​Serhii-Leniv</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.65.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.65.0 (2026-07-20)</h2>
<h3>🚀 Features</h3>
<ul>
<li>add warning when TS 7 is detected (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12529">#12529</a>)</li>
<li><strong>parser:</strong> add onUnsupportedTypeScriptVersion option
to error on unsupported TypeScript versions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12465">#12465</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Goldberg ✨</li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.65.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="63ba81b6ac"><code>63ba81b</code></a>
chore(release): publish 8.65.0</li>
<li><a
href="eaf457631a"><code>eaf4576</code></a>
feat: add warning when TS 7 is detected (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser/issues/12529">#12529</a>)</li>
<li><a
href="d8f1044702"><code>d8f1044</code></a>
feat(parser): add onUnsupportedTypeScriptVersion option to error on
unsupport...</li>
<li><a
href="0d06406f31"><code>0d06406</code></a>
chore: add attw validation to repo (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser/issues/12437">#12437</a>)</li>
<li><a
href="c2386e49ec"><code>c2386e4</code></a>
chore(deps): update dependency prettier to v3.9.5 (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser/issues/12486">#12486</a>)</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.65.0/packages/parser">compare
view</a></li>
</ul>
</details>
<br />

Updates `prettier` from 3.9.5 to 3.9.6
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/prettier/prettier/releases">prettier's
releases</a>.</em></p>
<blockquote>
<h2>3.9.6</h2>
<h2>What's Changed</h2>
<ul>
<li>Preserve quotes for methods named <code>new</code> (<a
href="https://redirect.github.com/prettier/prettier/pull/19621">prettier/prettier#19621</a>
by <a href="https://github.com/kovsu"><code>@​kovsu</code></a>)</li>
<li>Support <code>import defer</code> in <code>typescript</code> parser
(<a
href="https://redirect.github.com/prettier/prettier/pull/19624">prettier/prettier#19624</a>,
<a
href="https://redirect.github.com/prettier/prettier/pull/19675">prettier/prettier#19675</a>
by <a href="https://github.com/fisker"><code>@​fisker</code></a>)</li>
<li>Added a new official plugin <a
href="https://github.com/prettier/prettier/tree/3.9.6/packages/plugin-yuku"><code>@prettier/plugin-yuku</code>
🚀</a> (<a
href="https://redirect.github.com/prettier/prettier/pull/19628">prettier/prettier#19628</a>,
<a
href="https://redirect.github.com/prettier/prettier/pull/19629">prettier/prettier#19629</a>
by <a href="https://github.com/fisker"><code>@​fisker</code></a>)</li>
</ul>
<p>🔗 <a
href="https://github.com/prettier/prettier/blob/3.9.6/CHANGELOG.md#396">Changelog</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/prettier/prettier/blob/main/CHANGELOG.md">prettier's
changelog</a>.</em></p>
<blockquote>
<h1>3.9.6</h1>
<p><a
href="https://github.com/prettier/prettier/compare/3.9.5...3.9.6">diff</a></p>
<h4>TypeScript: Preserve quotes for methods named <code>new</code> (<a
href="https://redirect.github.com/prettier/prettier/pull/19621">#19621</a>
by <a href="https://github.com/kovsu"><code>@​kovsu</code></a>)</h4>
<!-- raw HTML omitted -->
<pre lang="tsx"><code>// Input
interface Container {
  &quot;new&quot;(id: string): number;
}
<p>// Prettier 3.9.5<br />
interface Container {<br />
new(id: string): number;<br />
}</p>
<p>// Prettier 3.9.6<br />
interface Container {<br />
&quot;new&quot;(id: string): number;<br />
}<br />
</code></pre></p>
<h4>TypeScript: Support <code>import defer</code> (<a
href="https://redirect.github.com/prettier/prettier/pull/19624">#19624</a>,
<a
href="https://redirect.github.com/prettier/prettier/pull/19675">#19675</a>
by <a href="https://github.com/fisker"><code>@​fisker</code></a>)</h4>
<!-- raw HTML omitted -->
<pre lang="tsx"><code>// Input
import defer * as foo from &quot;foo&quot;;
<p>// Prettier 3.9.5<br />
import * as foo from &quot;foo&quot;;</p>
<p>// Prettier 3.9.6<br />
import defer * as foo from &quot;foo&quot;;<br />
</code></pre></p>
<h4>JavaScript: Added a new official plugin
<code>@prettier/plugin-yuku</code> (<a
href="https://redirect.github.com/prettier/prettier/pull/19628">#19628</a>,
<a
href="https://redirect.github.com/prettier/prettier/pull/19629">#19629</a>
by <a href="https://github.com/fisker"><code>@​fisker</code></a>)</h4>
<p><code>@prettier/plugin-yuku</code> is powered by <a
href="https://yuku.fyi/">Yuku</a> (A high-performance
JavaScript/TypeScript compiler toolchain written in Zig).</p>
<p>This plugin includes two new parsers: <code>yuku</code> (JavaScript
syntax) and <code>yuku-ts</code> (TypeScript syntax).</p>
<p><strong>To use this plugin:</strong></p>
<ol>
<li>
<p>Install the plugin:</p>
<pre lang="bash"><code>yarn add --dev prettier @prettier/plugin-yuku
</code></pre>
</li>
</ol>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="8f0c95057c"><code>8f0c950</code></a>
Release 3.9.6</li>
<li><a
href="e9107647d0"><code>e910764</code></a>
Update changelog</li>
<li><a
href="ec3f1c7bd7"><code>ec3f1c7</code></a>
Update typescript-eslint to v8.65.0 (<a
href="https://redirect.github.com/prettier/prettier/issues/19675">#19675</a>)</li>
<li><a
href="73d2efc2c6"><code>73d2efc</code></a>
Update Yuku parser to v0.7.0 (<a
href="https://redirect.github.com/prettier/prettier/issues/19664">#19664</a>)</li>
<li><a
href="dd5e24eabe"><code>dd5e24e</code></a>
Preserve quotes for <code>TSMethodSignature</code> nodes named
<code>new</code> (<a
href="https://redirect.github.com/prettier/prettier/issues/19621">#19621</a>)</li>
<li><a
href="c03ab4e71c"><code>c03ab4e</code></a>
Update dependency eslint-plugin-unicorn to v72 (<a
href="https://redirect.github.com/prettier/prettier/issues/19633">#19633</a>)</li>
<li><a
href="b74dd53076"><code>b74dd53</code></a>
Update Yuku parser to v0.6.5 (<a
href="https://redirect.github.com/prettier/prettier/issues/19654">#19654</a>)</li>
<li><a
href="f1b594ea1d"><code>f1b594e</code></a>
Update dependency eslint-plugin-simple-import-sort to v14 (<a
href="https://redirect.github.com/prettier/prettier/issues/19655">#19655</a>)</li>
<li><a
href="0d9dfb6153"><code>0d9dfb6</code></a>
Update Yuku parser to v0.6.4 (<a
href="https://redirect.github.com/prettier/prettier/issues/19650">#19650</a>)</li>
<li><a
href="3bbb8159eb"><code>3bbb815</code></a>
Remove <code>typescript-only</code> directory (<a
href="https://redirect.github.com/prettier/prettier/issues/19636">#19636</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/prettier/prettier/compare/3.9.5...3.9.6">compare
view</a></li>
</ul>
</details>
<br />

Updates `@anthropic-ai/claude-agent-sdk-darwin-arm64` from 0.3.215 to
0.3.216

Updates `@anthropic-ai/claude-agent-sdk-darwin-x64` from 0.3.215 to
0.3.216

Updates `@anthropic-ai/claude-agent-sdk-linux-arm64` from 0.3.215 to
0.3.216

Updates `@anthropic-ai/claude-agent-sdk-linux-arm64-musl` from 0.3.215
to 0.3.216

Updates `@anthropic-ai/claude-agent-sdk-linux-x64` from 0.3.215 to
0.3.216

Updates `@anthropic-ai/claude-agent-sdk-linux-x64-musl` from 0.3.215 to
0.3.216

Updates `@anthropic-ai/claude-agent-sdk-win32-arm64` from 0.3.215 to
0.3.216

Updates `@anthropic-ai/claude-agent-sdk-win32-x64` from 0.3.215 to
0.3.216

Updates `@typescript-eslint/project-service` from 8.64.0 to 8.65.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/project-service's
releases</a>.</em></p>
<blockquote>
<h2>v8.65.0</h2>
<h2>8.65.0 (2026-07-20)</h2>
<h3>🚀 Features</h3>
<ul>
<li>add warning when TS 7 is detected (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12529">#12529</a>)</li>
<li><strong>eslint-plugin:</strong> [no-restricted-imports] deprecate
extension rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12527">#12527</a>,
<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/issues/19562">#19562</a>,
<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/issues/11889">#11889</a>)</li>
<li><strong>eslint-plugin:</strong> [no-shadow] specialized error on
enum declaration and member shadowing (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12578">#12578</a>)</li>
<li><strong>parser:</strong> add onUnsupportedTypeScriptVersion option
to error on unsupported TypeScript versions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12465">#12465</a>)</li>
<li><strong>typescript-estree:</strong> throw for invalid import defer
syntax (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12552">#12552</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [prefer-string-starts-ends-with]
handle escaped $ ending regex literals (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12515">#12515</a>)</li>
<li><strong>eslint-plugin:</strong> [unbound-method] report unbound
methods accessed via member expression on union types (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12448">#12448</a>)</li>
<li><strong>eslint-plugin:</strong>
[no-unnecessary-parameter-property-assignment] don't flag computed
assignments with a variable key (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12568">#12568</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Goldberg ✨</li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>Serhii Leniv <a
href="https://github.com/Serhii-Leniv"><code>@​Serhii-Leniv</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.65.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/project-service/CHANGELOG.md">@​typescript-eslint/project-service's
changelog</a>.</em></p>
<blockquote>
<h2>8.65.0 (2026-07-20)</h2>
<p>This was a version bump only for project-service 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.65.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="63ba81b6ac"><code>63ba81b</code></a>
chore(release): publish 8.65.0</li>
<li><a
href="0d06406f31"><code>0d06406</code></a>
chore: add attw validation to repo (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/project-service/issues/12437">#12437</a>)</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.65.0/packages/project-service">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/scope-manager` from 8.64.0 to 8.65.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/scope-manager's
releases</a>.</em></p>
<blockquote>
<h2>v8.65.0</h2>
<h2>8.65.0 (2026-07-20)</h2>
<h3>🚀 Features</h3>
<ul>
<li>add warning when TS 7 is detected (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12529">#12529</a>)</li>
<li><strong>eslint-plugin:</strong> [no-restricted-imports] deprecate
extension rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12527">#12527</a>,
<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/issues/19562">#19562</a>,
<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/issues/11889">#11889</a>)</li>
<li><strong>eslint-plugin:</strong> [no-shadow] specialized error on
enum declaration and member shadowing (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12578">#12578</a>)</li>
<li><strong>parser:</strong> add onUnsupportedTypeScriptVersion option
to error on unsupported TypeScript versions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12465">#12465</a>)</li>
<li><strong>typescript-estree:</strong> throw for invalid import defer
syntax (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12552">#12552</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [prefer-string-starts-ends-with]
handle escaped $ ending regex literals (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12515">#12515</a>)</li>
<li><strong>eslint-plugin:</strong> [unbound-method] report unbound
methods accessed via member expression on union types (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12448">#12448</a>)</li>
<li><strong>eslint-plugin:</strong>
[no-unnecessary-parameter-property-assignment] don't flag computed
assignments with a variable key (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12568">#12568</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Goldberg ✨</li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>Serhii Leniv <a
href="https://github.com/Serhii-Leniv"><code>@​Serhii-Leniv</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.65.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/scope-manager/CHANGELOG.md">@​typescript-eslint/scope-manager's
changelog</a>.</em></p>
<blockquote>
<h2>8.65.0 (2026-07-20)</h2>
<p>This was a version bump only for scope-manager 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.65.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="63ba81b6ac"><code>63ba81b</code></a>
chore(release): publish 8.65.0</li>
<li><a
href="0d06406f31"><code>0d06406</code></a>
chore: add attw validation to repo (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/scope-manager/issues/12437">#12437</a>)</li>
<li><a
href="c2386e49ec"><code>c2386e4</code></a>
chore(deps): update dependency prettier to v3.9.5 (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/scope-manager/issues/12486">#12486</a>)</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.65.0/packages/scope-manager">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/tsconfig-utils` from 8.64.0 to 8.65.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/tsconfig-utils's
releases</a>.</em></p>
<blockquote>
<h2>v8.65.0</h2>
<h2>8.65.0 (2026-07-20)</h2>
<h3>🚀 Features</h3>
<ul>
<li>add warning when TS 7 is detected (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12529">#12529</a>)</li>
<li><strong>eslint-plugin:</strong> [no-restricted-imports] deprecate
extension rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12527">#12527</a>,
<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/issues/19562">#19562</a>,
<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/issues/11889">#11889</a>)</li>
<li><strong>eslint-plugin:</strong> [no-shadow] specialized error on
enum declaration and member shadowing (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12578">#12578</a>)</li>
<li><strong>parser:</strong> add onUnsupportedTypeScriptVersion option
to error on unsupported TypeScript versions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12465">#12465</a>)</li>
<li><strong>typescript-estree:</strong> throw for invalid import defer
syntax (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12552">#12552</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [prefer-string-starts-ends-with]
handle escaped $ ending regex literals (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12515">#12515</a>)</li>
<li><strong>eslint-plugin:</strong> [unbound-method] report unbound
methods accessed via member expression on union types (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12448">#12448</a>)</li>
<li><strong>eslint-plugin:</strong>
[no-unnecessary-parameter-property-assignment] don't flag computed
assignments with a variable key (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12568">#12568</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Goldberg ✨</li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>Serhii Leniv <a
href="https://github.com/Serhii-Leniv"><code>@​Serhii-Leniv</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.65.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/tsconfig-utils/CHANGELOG.md">@​typescript-eslint/tsconfig-utils's
changelog</a>.</em></p>
<blockquote>
<h2>8.65.0 (2026-07-20)</h2>
<p>This was a version bump only for tsconfig-utils 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.65.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="63ba81b6ac"><code>63ba81b</code></a>
chore(release): publish 8.65.0</li>
<li><a
href="0d06406f31"><code>0d06406</code></a>
chore: add attw validation to repo (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/tsconfig-utils/issues/12437">#12437</a>)</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.65.0/packages/tsconfig-utils">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/type-utils` from 8.64.0 to 8.65.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/type-utils's
releases</a>.</em></p>
<blockquote>
<h2>v8.65.0</h2>
<h2>8.65.0 (2026-07-20)</h2>
<h3>🚀 Features</h3>
<ul>
<li>add warning when TS 7 is detected (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12529">#12529</a>)</li>
<li><strong>eslint-plugin:</strong> [no-restricted-imports] deprecate
extension rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12527">#12527</a>,
<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/issues/19562">#19562</a>,
<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/issues/11889">#11889</a>)</li>
<li><strong>eslint-plugin:</strong> [no-shadow] specialized error on
enum declaration and member shadowing (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12578">#12578</a>)</li>
<li><strong>parser:</strong> add onUnsupportedTypeScriptVersion option
to error on unsupported TypeScript versions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12465">#12465</a>)</li>
<li><strong>typescript-estree:</strong> throw for invalid import defer
syntax (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12552">#12552</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [prefer-string-starts-ends-with]
handle escaped $ ending regex literals (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12515">#12515</a>)</li>
<li><strong>eslint-plugin:</strong> [unbound-method] report unbound
methods accessed via member expression on union types (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12448">#12448</a>)</li>
<li><strong>eslint-plugin:</strong>
[no-unnecessary-parameter-property-assignment] don't flag computed
assignments with a variable key (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12568">#12568</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Goldberg ✨</li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>Serhii Leniv <a
href="https://github.com/Serhii-Leniv"><code>@​Serhii-Leniv</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.65.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/type-utils/CHANGELOG.md">@​typescript-eslint/type-utils's
changelog</a>.</em></p>
<blockquote>
<h2>8.65.0 (2026-07-20)</h2>
<p>This was a version bump only for type-utils 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.65.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="63ba81b6ac"><code>63ba81b</code></a>
chore(release): publish 8.65.0</li>
<li><a
href="0d06406f31"><code>0d06406</code></a>
chore: add attw validation to repo (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/type-utils/issues/12437">#12437</a>)</li>
<li><a
href="c2386e49ec"><code>c2386e4</code></a>
chore(deps): update dependency prettier to v3.9.5 (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/type-utils/issues/12486">#12486</a>)</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.65.0/packages/type-utils">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/types` from 8.64.0 to 8.65.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/types's
releases</a>.</em></p>
<blockquote>
<h2>v8.65.0</h2>
<h2>8.65.0 (2026-07-20)</h2>
<h3>🚀 Features</h3>
<ul>
<li>add warning when TS 7 is detected (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12529">#12529</a>)</li>
<li><strong>eslint-plugin:</strong> [no-restricted-imports] deprecate
extension rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12527">#12527</a>,
<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/issues/19562">#19562</a>,
<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/issues/11889">#11889</a>)</li>
<li><strong>eslint-plugin:</strong> [no-shadow] specialized error on
enum declaration and member shadowing (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12578">#12578</a>)</li>
<li><strong>parser:</strong> add onUnsupportedTypeScriptVersion option
to error on unsupported TypeScript versions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12465">#12465</a>)</li>
<li><strong>typescript-estree:</strong> throw for invalid import defer
syntax (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12552">#12552</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [prefer-string-starts-ends-with]
handle escaped $ ending regex literals (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12515">#12515</a>)</li>
<li><strong>eslint-plugin:</strong> [unbound-method] report unbound
methods accessed via member expression on union types (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12448">#12448</a>)</li>
<li><strong>eslint-plugin:</strong>
[no-unnecessary-parameter-property-assignment] don't flag computed
assignments with a variable key (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12568">#12568</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Goldberg ✨</li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>Serhii Leniv <a
href="https://github.com/Serhii-Leniv"><code>@​Serhii-Leniv</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.65.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/types/CHANGELOG.md">@​typescript-eslint/types's
changelog</a>.</em></p>
<blockquote>
<h2>8.65.0 (2026-07-20)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>parser:</strong> add onUnsupportedTypeScriptVersion option
to error on unsupported TypeScript versions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12465">#12465</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Goldberg ✨</li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.65.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="63ba81b6ac"><code>63ba81b</code></a>
chore(release): publish 8.65.0</li>
<li><a
href="d8f1044702"><code>d8f1044</code></a>
feat(parser): add onUnsupportedTypeScriptVersion option to error on
unsupport...</li>
<li><a
href="0d06406f31"><code>0d06406</code></a>
chore: add attw validation to repo (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/types/issues/12437">#12437</a>)</li>
<li><a
href="c2386e49ec"><code>c2386e4</code></a>
chore(deps): update dependency prettier to v3.9.5 (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/types/issues/12486">#12486</a>)</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.65.0/packages/types">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/typescript-estree` from 8.64.0 to 8.65.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/typescript-estree's
releases</a>.</em></p>
<blockquote>
<h2>v8.65.0</h2>
<h2>8.65.0 (2026-07-20)</h2>
<h3>🚀 Features</h3>
<ul>
<li>add warning when TS 7 is detected (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12529">#12529</a>)</li>
<li><strong>eslint-plugin:</strong> [no-restricted-imports] deprecate
extension rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12527">#12527</a>,
<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/issues/19562">#19562</a>,
<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/issues/11889">#11889</a>)</li>
<li><strong>eslint-plugin:</strong> [no-shadow] specialized error on
enum declaration and member shadowing (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12578">#12578</a>)</li>
<li><strong>parser:</strong> add onUnsupportedTypeScriptVersion option
to error on unsupported TypeScript versions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12465">#12465</a>)</li>
<li><strong>typescript-estree:</strong> throw for invalid import defer
syntax (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12552">#12552</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [prefer-string-starts-ends-with]
handle escaped $ ending regex literals (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12515">#12515</a>)</li>
<li><strong>eslint-plugin:</strong> [unbound-method] report unbound
methods accessed via member expression on union types (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12448">#12448</a>)</li>
<li><strong>eslint-plugin:</strong>
[no-unnecessary-parameter-property-assignment] don't flag computed
assignments with a variable key (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12568">#12568</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Goldberg ✨</li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>Serhii Leniv <a
href="https://github.com/Serhii-Leniv"><code>@​Serhii-Leniv</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.65.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/typescript-estree/CHANGELOG.md">@​typescript-eslint/typescript-estree's
changelog</a>.</em></p>
<blockquote>
<h2>8.65.0 (2026-07-20)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>parser:</strong> add onUnsupportedTypeScriptVersion option
to error on unsupported TypeScript versions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12465">#12465</a>)</li>
<li><strong>typescript-estree:</strong> throw for invalid import defer
syntax (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12552">#12552</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Goldberg ✨</li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.65.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="63ba81b6ac"><code>63ba81b</code></a>
chore(release): publish 8.65.0</li>
<li><a
href="d8f1044702"><code>d8f1044</code></a>
feat(parser): add onUnsupportedTypeScriptVersion option to error on
unsupport...</li>
<li><a
href="0d06406f31"><code>0d06406</code></a>
chore: add attw validation to repo (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-estree/issues/12437">#12437</a>)</li>
<li><a
href="4b410e951c"><code>4b410e9</code></a>
feat(typescript-estree): throw for invalid import defer syntax (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-estree/issues/12552">#12552</a>)</li>
<li><a
href="c2386e49ec"><code>c2386e4</code></a>
chore(deps): update dependency prettier to v3.9.5 (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-estree/issues/12486">#12486</a>)</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.65.0/packages/typescript-estree">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/utils` from 8.64.0 to 8.65.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/utils's
releases</a>.</em></p>
<blockquote>
<h2>v8.65.0</h2>
<h2>8.65.0 (2026-07-20)</h2>
<h3>🚀 Features</h3>
<ul>
<li>add warning when TS 7 is detected (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12529">#12529</a>)</li>
<li><strong>eslint-plugin:</strong> [no-restricted-imports] deprecate
extension rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12527">#12527</a>,
<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/issues/19562">#19562</a>,
<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/issues/11889">#11889</a>)</li>
<li><strong>eslint-plugin:</strong> [no-shadow] specialized error on
enum declaration and member shadowing (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12578">#12578</a>)</li>
<li><strong>parser:</strong> add onUnsupportedTypeScriptVersion option
to error on unsupported TypeScript versions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12465">#12465</a>)</li>
<li><strong>typescript-estree:</strong> throw for invalid import defer
syntax (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12552">#12552</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [prefer-string-starts-ends-with]
handle escaped $ ending regex literals (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12515">#12515</a>)</li>
<li><strong>eslint-plugin:</strong> [unbound-method] report unbound
methods accessed via member expression on union types (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12448">#12448</a>)</li>
<li><strong>eslint-plugin:</strong>
[no-unnecessary-parameter-property-assignment] don't flag computed
assignments with a variable key (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12568">#12568</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Goldberg ✨</li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>Serhii Leniv <a
href="https://github.com/Serhii-Leniv"><code>@​Serhii-Leniv</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.65.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/utils/CHANGELOG.md">@​typescript-eslint/utils's
changelog</a>.</em></p>
<blockquote>
<h2>8.65.0 (2026-07-20)</h2>
<p>This was a version bump only for utils 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.65.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="63ba81b6ac"><code>63ba81b</code></a>
chore(release): publish 8.65.0</li>
<li><a
href="0d06406f31"><code>0d06406</code></a>
chore: add attw validation to repo (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/utils/issues/12437">#12437</a>)</li>
<li><a
href="c2386e49ec"><code>c2386e4</code></a>
chore(deps): update dependency prettier to v3.9.5 (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/utils/issues/12486">#12486</a>)</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.65.0/packages/utils">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/visitor-keys` from 8.64.0 to 8.65.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/visitor-keys's
releases</a>.</em></p>
<blockquote>
<h2>v8.65.0</h2>
<h2>8.65.0 (2026-07-20)</h2>
<h3>🚀 Features</h3>
<ul>
<li>add warning when TS 7 is detected (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12529">#12529</a>)</li>
<li><strong>eslint-plugin:</strong> [no-restricted-imports] deprecate
extension rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12527">#12527</a>,
<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/issues/19562">#19562</a>,
<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/issues/11889">#11889</a>)</li>
<li><strong>eslint-plugin:</strong> [no-shadow] specialized error on
enum declaration and member shadowing (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12578">#12578</a>)</li>
<li><strong>parser:</strong> add onUnsupportedTypeScriptVersion option
to error on unsupported TypeScript versions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12465">#12465</a>)</li>
<li><strong>typescript-estree:</strong> throw for invalid import defer
syntax (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12552">#12552</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [prefer-string-starts-ends-with]
handle escaped $ ending regex literals (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12515">#12515</a>)</li>
<li><strong>eslint-plugin:</strong> [unbound-method] report unbound
methods accessed via member expression on union types (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12448">#12448</a>)</li>
<li><strong>eslint-plugin:</strong>
[no-unnecessary-parameter-property-assignment] don't flag computed
assignments with a variable key (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12568">#12568</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Josh Goldberg ✨</li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>Serhii Leniv <a
href="https://github.com/Serhii-Leniv"><code>@​Serhii-Leniv</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.65.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/visitor-keys/CHANGELOG.md">@​typescript-eslint/visitor-keys's
changelog</a>.</em></p>
<blockquote>
<h2>8.65.0 (2026-07-20)</h2>
<p>This was a version bump only for visitor-keys 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.65.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="63ba81b6ac"><code>63ba81b</code></a>
chore(release): publish 8.65.0</li>
<li><a
href="0d06406f31"><code>0d06406</code></a>
chore: add attw validation to repo (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/visitor-keys/issues/12437">#12437</a>)...

_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-07-22 12:34:14 +02:00
Mark Tkachenko
980ac8dc56 Steering implementation (#885) 2026-07-21 18:53:56 +02:00
dependabot[bot]
d9bd36d8b0 feat(deps): Bump actions/setup-node from 6.4.0 to 7.0.0 (#897)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from
6.4.0 to 7.0.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/setup-node/releases">actions/setup-node's
releases</a>.</em></p>
<blockquote>
<h2>v7.0.0</h2>
<h2>What's Changed</h2>
<h3>Enhancements:</h3>
<ul>
<li>Add cache-primary-key and cache-matched-key as outputs by <a
href="https://github.com/gowridurgad"><code>@​gowridurgad</code></a> in
<a
href="https://redirect.github.com/actions/setup-node/pull/1577">actions/setup-node#1577</a></li>
<li>Migrate to ESM and upgrade dependencies by <a
href="https://github.com/gowridurgad"><code>@​gowridurgad</code></a> in
<a
href="https://redirect.github.com/actions/setup-node/pull/1574">actions/setup-node#1574</a></li>
</ul>
<h3>Bug fixes:</h3>
<ul>
<li>Remove dummy NODE_AUTH_TOKEN export by <a
href="https://github.com/gowridurgad"><code>@​gowridurgad</code></a> in
<a
href="https://redirect.github.com/actions/setup-node/pull/1558">actions/setup-node#1558</a></li>
<li>Only use <code>mirrorToken</code> in <code>getManifest</code> if
it's provided by <a
href="https://github.com/deiga"><code>@​deiga</code></a> in <a
href="https://redirect.github.com/actions/setup-node/pull/1548">actions/setup-node#1548</a></li>
</ul>
<h3>Documentation updates:</h3>
<ul>
<li>Add documentation for publishing to npm with Trusted Publisher
(OIDC) by <a
href="https://github.com/chiranjib-swain"><code>@​chiranjib-swain</code></a>
in <a
href="https://redirect.github.com/actions/setup-node/pull/1536">actions/setup-node#1536</a></li>
<li>docs: Update restore-only cache documentation by <a
href="https://github.com/priya-kinthali"><code>@​priya-kinthali</code></a>
in <a
href="https://redirect.github.com/actions/setup-node/pull/1550">actions/setup-node#1550</a></li>
<li>docs: Update caching recommendations to mitigate cache poisoning
risks by <a
href="https://github.com/chiranjib-swain"><code>@​chiranjib-swain</code></a>
in <a
href="https://redirect.github.com/actions/setup-node/pull/1567">actions/setup-node#1567</a></li>
</ul>
<h3>Dependency update:</h3>
<ul>
<li>Upgrade <code>@​actions/cache</code> to 5.1.0, log cache write
denied by <a
href="https://github.com/jasongin"><code>@​jasongin</code></a> in <a
href="https://redirect.github.com/actions/setup-node/pull/1569">actions/setup-node#1569</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/chiranjib-swain"><code>@​chiranjib-swain</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/setup-node/pull/1536">actions/setup-node#1536</a></li>
<li><a href="https://github.com/deiga"><code>@​deiga</code></a> made
their first contribution in <a
href="https://redirect.github.com/actions/setup-node/pull/1548">actions/setup-node#1548</a></li>
<li><a href="https://github.com/jasongin"><code>@​jasongin</code></a>
made their first contribution in <a
href="https://redirect.github.com/actions/setup-node/pull/1569">actions/setup-node#1569</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/setup-node/compare/v6...v7.0.0">https://github.com/actions/setup-node/compare/v6...v7.0.0</a></p>
<h2>v6.5.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Update <code>@​actions/cache</code> to 5.1.0 and add security
overrides for undici and fast-xml-parser by <a
href="https://github.com/HarithaVattikuti"><code>@​HarithaVattikuti</code></a>
in <a
href="https://redirect.github.com/actions/setup-node/pull/1579">actions/setup-node#1579</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/setup-node/compare/v6.4.0...v6.5.0">https://github.com/actions/setup-node/compare/v6.4.0...v6.5.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="8207627860"><code>8207627</code></a>
Migrate to ESM and upgrade dependencies (<a
href="https://redirect.github.com/actions/setup-node/issues/1574">#1574</a>)</li>
<li><a
href="04be95cf35"><code>04be95c</code></a>
Add cache-primary-key and cache-matched-key as outputs (<a
href="https://redirect.github.com/actions/setup-node/issues/1577">#1577</a>)</li>
<li><a
href="7c2c68d20d"><code>7c2c68d</code></a>
docs: Update caching recommendations to mitigate cache poisoning risks
(<a
href="https://redirect.github.com/actions/setup-node/issues/1567">#1567</a>)</li>
<li><a
href="6a61c0375d"><code>6a61c03</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/setup-node/issues/1569">#1569</a>
from jasongin/update-actions-cache-5.1.0</li>
<li><a
href="30eb73b41d"><code>30eb73b</code></a>
Resolve high-severity audit issues</li>
<li><a
href="4e1a87a501"><code>4e1a87a</code></a>
Update dist</li>
<li><a
href="360237f0c0"><code>360237f</code></a>
Strict equality</li>
<li><a
href="4f8aac5beb"><code>4f8aac5</code></a>
Bump <code>@​actions/cache</code> to 5.1.0, log cache write denied</li>
<li><a
href="f4a67bbeca"><code>f4a67bb</code></a>
Only use <code>mirrorToken</code> in <code>getManifest</code> if it's
provided (<a
href="https://redirect.github.com/actions/setup-node/issues/1548">#1548</a>)</li>
<li><a
href="0355742c94"><code>0355742</code></a>
Remove dummy NODE_AUTH_TOKEN export (<a
href="https://redirect.github.com/actions/setup-node/issues/1558">#1558</a>)</li>
<li>Additional commits viewable in <a
href="48b55a011b...8207627860">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/setup-node&package-manager=github_actions&previous-version=6.4.0&new-version=7.0.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-20 17:48:27 +02:00
acp-release-bot[bot]
0a88de2a83 chore(main): release 0.60.0 (#884)
🤖 I have created a release *beep* *boop*
---


##
[0.60.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.59.0...v0.60.0)
(2026-07-20)


### Features

* **deps:** Update to claude-agent-sdk 0.3.215
([#890](https://github.com/agentclientprotocol/claude-agent-acp/issues/890))
([92548f0](92548f0435))
* implement configurable LLM providers
([#853](https://github.com/agentclientprotocol/claude-agent-acp/issues/853))
([82cd692](82cd692e50))


### Bug Fixes

* parse Agent/Task trailers without regex
([#879](https://github.com/agentclientprotocol/claude-agent-acp/issues/879))
([06c3d7b](06c3d7bdbd))
* remove ~15s stall on session/new and model switch by seeding the
context window synchronously
([#894](https://github.com/agentclientprotocol/claude-agent-acp/issues/894))
([ff9b96d](ff9b96d462))
* Silence missing PostToolUse callbacks
([#895](https://github.com/agentclientprotocol/claude-agent-acp/issues/895))
([1887ada](1887ada215)),
closes
[#889](https://github.com/agentclientprotocol/claude-agent-acp/issues/889)

---
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-07-20 14:51:04 +02:00
Luca Dibattista
ff9b96d462 fix: remove ~15s stall on session/new and model switch by seeding the context window synchronously (#894)
## Summary

`session/new` and switching models via `configOptions` regressed to ~15s
each in v0.59.0. This removes the blocking call responsible and seeds
the context window synchronously instead, restoring sub-second behavior
on both paths.

## Root cause

v0.59.0 added an inline `await query.getContextUsage()` on the
session-creation and model-switch paths. That SDK control request is not
serviced by the CLI until the session's first prompt turn has run —
before then it stalls ~15s. And because SDK control requests are
serialized over a single stdin/stdout channel, an awaited `setModel`
queued behind the wedged `getContextUsage` inherited the full stall. So
both paths blocked ~15s before returning.

Verified with the real CLI (setModel alone pre-prompt: ~1ms;
getContextUsage alone pre-prompt: ~16s; getContextUsage-then-setModel:
both resolve together at ~16s, confirming the serialization).

## Fix

Drop `getContextUsage` from both hot paths. The context window is seeded
synchronously by `immediateContextWindow(...)`:

1. an authoritative value from a per-(provider, model) cache, if
learned; else
2. a synchronous text heuristic (`\b1m\b` → 1,000,000); else
3. `DEFAULT_CONTEXT_WINDOW` (200,000).

The authoritative window is learned the moment it's actually available —
from each turn's `result.modelUsage` — and written into the cache, so
later sessions/switches to the same model seed the correct window
immediately with no IPC. A model that has never run a turn shows the
heuristic/default window until its first response, matching pre-0.59.0
behavior.

### Cache design

- **Keyed on `(provider, resolved model id)`.** The resolved id
(`ModelInfo.resolvedModel`, e.g. `claude-sonnet-5[1m]`) matches the
`result.modelUsage` keys, so the write key and the read keys line up.
Keying on the resolved id (not the picker value) dedups aliases that
resolve to the same model; keying on the provider keeps two providers
that reuse an id string for different windows from crossing wires
(per-session provider config, #853). The provider is captured at session
creation, since the effective provider can change process-wide over the
adapter's lifetime.
- **Non-positive/NaN windows are ignored** — they neither overwrite the
session's learned window nor enter the cache.

## Impact (measured, real CLI)

| Path | Before | After |
| --- | --- | --- |
| `session/new` | ~17.4s | ~0.9s |
| model switch (before first prompt) | ~15s | ~3ms |
| model switch (after a turn) | ~14.7s | <1ms |

## Tests

- New end-to-end cache test: a turn's authoritative window (learned
under the resolved id even when the assistant message reports the bare
id) is served on a later switch with no `getContextUsage`.
- New provider-isolation test: the same resolved id on a different
provider does not read another provider's cached window; the same
provider does.
- New non-positive-window test: a nonsensical `contextWindow` is ignored
and never poisons the cache.
- Rewrote the existing context-window tests to the new contract (assert
no `getContextUsage` on session/new and switch; window from
cache/inference/default).

Closes #886
Closes #880

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
2026-07-20 12:48:40 +00:00
Ben Brandt
1887ada215 fix: Silence missing PostToolUse callbacks (#895)
Closes #889
2026-07-20 11:43:08 +00:00
José Gurruchaga
06c3d7bdbd fix: parse Agent/Task trailers without regex (#879)
## Summary

Closes:
https://github.com/agentclientprotocol/claude-agent-acp/issues/878
Fixes the potential ReDoS warning reported by CodeQL in
`stripAgentTrailer()`.

- Replaces regex-based trailer stripping with bounded linear parsing
- Preserves valid `<usage>…</usage>` and `agentId: … (…)` trailer
handling
- Adds coverage for malformed trailers, which are left unchanged

## Testing

- `npm test -- --run src/tests/tools.test.ts`

---------

Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 11:21:52 +00:00
dependabot[bot]
92548f0435 feat(deps): Update to claude-agent-sdk 0.3.215 (#890)
Bumps the minor group with 7 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
|
[@anthropic-ai/claude-agent-sdk](https://github.com/anthropics/claude-agent-sdk-typescript)
| `0.3.207` | `0.3.212` |
|
[@anthropic-ai/sdk](https://github.com/anthropics/anthropic-sdk-typescript)
| `0.111.0` | `0.112.1` |
|
[@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin)
| `8.63.0` | `8.64.0` |
|
[@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser)
| `8.63.0` | `8.64.0` |
|
[express-rate-limit](https://github.com/express-rate-limit/express-rate-limit)
| `8.5.2` | `8.6.0` |
| [obug](https://github.com/sxzz/obug) | `2.1.3` | `2.1.4` |
| [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) |
`8.1.4` | `8.1.5` |


Updates `@anthropic-ai/claude-agent-sdk` from 0.3.207 to 0.3.212
<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.212</h2>
<h2>What's changed</h2>
<ul>
<li>Fixed dash-leading <code>resumeSessionAt</code> and
<code>sessionId</code> values being passed to the CLI as separate argv
tokens; both now use equals-form (<code>--flag=value</code>)</li>
<li>Agent tool output now includes the resolved model when a mid-turn
model swap changed the subagent's model</li>
</ul>
<h2>Update</h2>
<pre lang="sh"><code>npm install @anthropic-ai/claude-agent-sdk@0.3.212
# or
yarn add @anthropic-ai/claude-agent-sdk@0.3.212
# or
pnpm add @anthropic-ai/claude-agent-sdk@0.3.212
# or
bun add @anthropic-ai/claude-agent-sdk@0.3.212
</code></pre>
<h2>v0.3.211</h2>
<h2>What's changed</h2>
<ul>
<li>Fixed <code>--replay-user-messages</code> with
<code>--include-partial-messages</code> emitting the turn-start user
replay after the first content block instead of before the turn's
content events</li>
<li>Added <code>SDKAssistantMessage.timestamp</code> (ISO-8601) to the
live stream, matching <code>SDKUserMessage</code>; older emitters omit
it, consumers should fall back to receive time</li>
<li>Added rate-limit message prefix buckets
(<code>USAGE_LIMIT_ERROR_PREFIXES</code> and siblings) as
<code>@alpha</code> exports for classifying rate-limit messages without
hand-mirrored lists</li>
<li>Improved process-exit errors to include the CLI's stderr output, so
a failed child reports its actual cause instead of only an exit
code</li>
</ul>
<h2>Update</h2>
<pre lang="sh"><code>npm install @anthropic-ai/claude-agent-sdk@0.3.211
# or
yarn add @anthropic-ai/claude-agent-sdk@0.3.211
# or
pnpm add @anthropic-ai/claude-agent-sdk@0.3.211
# or
bun add @anthropic-ai/claude-agent-sdk@0.3.211
</code></pre>
<h2>v0.3.210</h2>
<h2>What's changed</h2>
<ul>
<li>Added <code>timedOutAfterMs</code> to <code>BashToolOutput</code>,
set when a command is auto-backgrounded on timeout</li>
</ul>
<h2>Update</h2>
<pre lang="sh"><code>npm install @anthropic-ai/claude-agent-sdk@0.3.210
# or
yarn add @anthropic-ai/claude-agent-sdk@0.3.210
# or
&lt;/tr&gt;&lt;/table&gt; 
</code></pre>
</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.212</h2>
<ul>
<li>Fixed dash-leading <code>resumeSessionAt</code> and
<code>sessionId</code> values being passed to the CLI as separate argv
tokens; both now use equals-form (<code>--flag=value</code>)</li>
<li>Agent tool output now includes the resolved model when a mid-turn
model swap changed the subagent's model</li>
</ul>
<h2>0.3.211</h2>
<ul>
<li>Fixed <code>--replay-user-messages</code> with
<code>--include-partial-messages</code> emitting the turn-start user
replay after the first content block instead of before the turn's
content events</li>
<li>Added <code>SDKAssistantMessage.timestamp</code> (ISO-8601) to the
live stream, matching <code>SDKUserMessage</code>; older emitters omit
it, consumers should fall back to receive time</li>
<li>Added rate-limit message prefix buckets
(<code>USAGE_LIMIT_ERROR_PREFIXES</code> and siblings) as
<code>@alpha</code> exports for classifying rate-limit messages without
hand-mirrored lists</li>
<li>Improved process-exit errors to include the CLI's stderr output, so
a failed child reports its actual cause instead of only an exit
code</li>
</ul>
<h2>0.3.210</h2>
<ul>
<li>Added <code>timedOutAfterMs</code> to <code>BashToolOutput</code>,
set when a command is auto-backgrounded on timeout</li>
</ul>
<h2>0.3.209</h2>
<ul>
<li>Updated to parity with Claude Code v2.1.209</li>
</ul>
<h2>0.3.208</h2>
<ul>
<li>Fixed a caller abort during a pending SDK hook callback being
converted into hook success, which let PreToolUse-gated tools execute
after the abort</li>
<li>Fixed a per-query resource leak in the SDK's process tracking when
spawning the CLI fails (nonexistent or inaccessible executable
path)</li>
<li>Fixed an SDK <code>UserPromptSubmit</code> hook callback exceeding
its timeout killing the entire query with an empty error; it now blocks
the prompt with a clear timeout message and the session continues</li>
<li>Fixed <code>extraArgs</code> values that look like flags (e.g.
<code>resume: '--version'</code>) being parsed as their own CLI flags;
dash-leading values are now bound with equals-form argv</li>
<li>Fixed an abort-listener leak: streaming queries sharing one
<code>AbortController</code> no longer accumulate <code>abort</code>
listeners on its signal after each completed query</li>
<li>Fixed <code>createSdkMcpServer</code> docs pointing at a nonexistent
env var; the MCP tool-call timeout knob is
<code>MCP_TOOL_TIMEOUT</code></li>
<li>Fixed an uncaught exception when writing to stdin after the Claude
Code subprocess has exited</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="f1206afbf0"><code>f1206af</code></a>
chore: Update CHANGELOG.md</li>
<li><a
href="d7f244a55e"><code>d7f244a</code></a>
chore: Update CHANGELOG.md</li>
<li><a
href="018efaa0c3"><code>018efaa</code></a>
chore: Update CHANGELOG.md</li>
<li><a
href="d9eca208ac"><code>d9eca20</code></a>
chore: Update CHANGELOG.md</li>
<li><a
href="34426a18f0"><code>34426a1</code></a>
Escape untrusted issue fields in the Slack notification workflow (<a
href="https://redirect.github.com/anthropics/claude-agent-sdk-typescript/issues/375">#375</a>)</li>
<li><a
href="96e19a548a"><code>96e19a5</code></a>
chore: Update CHANGELOG.md</li>
<li><a
href="02b0475071"><code>02b0475</code></a>
chore: Update CHANGELOG.md</li>
<li>See full diff in <a
href="https://github.com/anthropics/claude-agent-sdk-typescript/compare/v0.3.207...v0.3.212">compare
view</a></li>
</ul>
</details>
<br />

Updates `@anthropic-ai/sdk` from 0.111.0 to 0.112.1
<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.112.1</h2>
<h2>0.112.1 (2026-07-16)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.112.0...sdk-v0.112.1">sdk-v0.112.0...sdk-v0.112.1</a></p>
<h2>sdk: v0.112.0</h2>
<h2>0.112.0 (2026-07-16)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.111.0...sdk-v0.112.0">sdk-v0.111.0...sdk-v0.112.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> add support for MCP Tunnels (<a
href="597ac4181f">597ac41</a>)</li>
</ul>
</blockquote>
</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.112.1 (2026-07-16)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.112.0...sdk-v0.112.1">sdk-v0.112.0...sdk-v0.112.1</a></p>
<h2>0.112.0 (2026-07-16)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.111.0...sdk-v0.112.0">sdk-v0.111.0...sdk-v0.112.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> add support for MCP Tunnels (<a
href="597ac4181f">597ac41</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="dd61964bbe"><code>dd61964</code></a>
chore: release main</li>
<li><a
href="b48ac4b46a"><code>b48ac4b</code></a>
ci: force version bump (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/225">#225</a>)</li>
<li><a
href="4aac845a7a"><code>4aac845</code></a>
chore: release main</li>
<li><a
href="8db6f8449c"><code>8db6f84</code></a>
ci: Update readme to force release (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/224">#224</a>)</li>
<li><a
href="d4aa171e5e"><code>d4aa171</code></a>
chore: release main</li>
<li><a
href="bc2d2674b3"><code>bc2d267</code></a>
codegen metadata</li>
<li><a
href="d05ddf6961"><code>d05ddf6</code></a>
feat(api): add support for MCP Tunnels</li>
<li>See full diff in <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.111.0...sdk-v0.112.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/eslint-plugin` from 8.63.0 to 8.64.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.64.0</h2>
<h2>8.64.0 (2026-07-13)</h2>
<h3>🚀 Features</h3>
<ul>
<li>support parsing <code>import defer</code> (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12513">#12513</a>)</li>
<li><strong>eslint-plugin:</strong> [no-loop-func] support
<code>using</code> / <code>await using</code> declarations and deprecate
the rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12500">#12500</a>)</li>
<li><strong>typescript-estree:</strong> throw for invalid definite
assignment in class properties (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12543">#12543</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [require-array-sort-compare] handle
constrained arrays (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12512">#12512</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.64.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.64.0 (2026-07-13)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-loop-func] support
<code>using</code> / <code>await using</code> declarations and deprecate
the rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12500">#12500</a>)</li>
<li><strong>typescript-estree:</strong> throw for invalid definite
assignment in class properties (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12543">#12543</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [require-array-sort-compare] handle
constrained arrays (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12512">#12512</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.64.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="414d9abbf6"><code>414d9ab</code></a>
chore(release): publish 8.64.0</li>
<li><a
href="bcfe16fd2c"><code>bcfe16f</code></a>
feat(eslint-plugin): [no-loop-func] support <code>using</code> /
<code>await using</code> declarati...</li>
<li><a
href="5b5bfdeb8f"><code>5b5bfde</code></a>
fix(eslint-plugin): [require-array-sort-compare] handle constrained
arrays (#...</li>
<li><a
href="321856cdad"><code>321856c</code></a>
chore: cleanup cspell config (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin/issues/12526">#12526</a>)</li>
<li><a
href="b418cf577f"><code>b418cf5</code></a>
feat(typescript-estree): throw for invalid definite assignment in class
prope...</li>
<li><a
href="737de2b569"><code>737de2b</code></a>
docs: align unified-signatures correct example parameter (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin/issues/12506">#12506</a>)</li>
<li><a
href="a63c17ab74"><code>a63c17a</code></a>
docs: fix shadowed parameter name in unified-signatures example (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin/issues/12502">#12502</a>)</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.64.0/packages/eslint-plugin">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/parser` from 8.63.0 to 8.64.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.64.0</h2>
<h2>8.64.0 (2026-07-13)</h2>
<h3>🚀 Features</h3>
<ul>
<li>support parsing <code>import defer</code> (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12513">#12513</a>)</li>
<li><strong>eslint-plugin:</strong> [no-loop-func] support
<code>using</code> / <code>await using</code> declarations and deprecate
the rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12500">#12500</a>)</li>
<li><strong>typescript-estree:</strong> throw for invalid definite
assignment in class properties (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12543">#12543</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [require-array-sort-compare] handle
constrained arrays (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12512">#12512</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.64.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.64.0 (2026-07-13)</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.64.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="414d9abbf6"><code>414d9ab</code></a>
chore(release): publish 8.64.0</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.64.0/packages/parser">compare
view</a></li>
</ul>
</details>
<br />

Updates `@anthropic-ai/claude-agent-sdk-darwin-arm64` from 0.3.207 to
0.3.212

Updates `@anthropic-ai/claude-agent-sdk-darwin-x64` from 0.3.207 to
0.3.212

Updates `@anthropic-ai/claude-agent-sdk-linux-arm64` from 0.3.207 to
0.3.212

Updates `@anthropic-ai/claude-agent-sdk-linux-arm64-musl` from 0.3.207
to 0.3.212

Updates `@anthropic-ai/claude-agent-sdk-linux-x64` from 0.3.207 to
0.3.212

Updates `@anthropic-ai/claude-agent-sdk-linux-x64-musl` from 0.3.207 to
0.3.212

Updates `@anthropic-ai/claude-agent-sdk-win32-arm64` from 0.3.207 to
0.3.212

Updates `@anthropic-ai/claude-agent-sdk-win32-x64` from 0.3.207 to
0.3.212

Updates `@typescript-eslint/project-service` from 8.63.0 to 8.64.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/project-service's
releases</a>.</em></p>
<blockquote>
<h2>v8.64.0</h2>
<h2>8.64.0 (2026-07-13)</h2>
<h3>🚀 Features</h3>
<ul>
<li>support parsing <code>import defer</code> (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12513">#12513</a>)</li>
<li><strong>eslint-plugin:</strong> [no-loop-func] support
<code>using</code> / <code>await using</code> declarations and deprecate
the rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12500">#12500</a>)</li>
<li><strong>typescript-estree:</strong> throw for invalid definite
assignment in class properties (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12543">#12543</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [require-array-sort-compare] handle
constrained arrays (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12512">#12512</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.64.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/project-service/CHANGELOG.md">@​typescript-eslint/project-service's
changelog</a>.</em></p>
<blockquote>
<h2>8.64.0 (2026-07-13)</h2>
<p>This was a version bump only for project-service 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.64.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="414d9abbf6"><code>414d9ab</code></a>
chore(release): publish 8.64.0</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.64.0/packages/project-service">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/scope-manager` from 8.63.0 to 8.64.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/scope-manager's
releases</a>.</em></p>
<blockquote>
<h2>v8.64.0</h2>
<h2>8.64.0 (2026-07-13)</h2>
<h3>🚀 Features</h3>
<ul>
<li>support parsing <code>import defer</code> (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12513">#12513</a>)</li>
<li><strong>eslint-plugin:</strong> [no-loop-func] support
<code>using</code> / <code>await using</code> declarations and deprecate
the rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12500">#12500</a>)</li>
<li><strong>typescript-estree:</strong> throw for invalid definite
assignment in class properties (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12543">#12543</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [require-array-sort-compare] handle
constrained arrays (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12512">#12512</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.64.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/scope-manager/CHANGELOG.md">@​typescript-eslint/scope-manager's
changelog</a>.</em></p>
<blockquote>
<h2>8.64.0 (2026-07-13)</h2>
<h3>🚀 Features</h3>
<ul>
<li>support parsing <code>import defer</code> (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12513">#12513</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.64.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="414d9abbf6"><code>414d9ab</code></a>
chore(release): publish 8.64.0</li>
<li><a
href="07af21b912"><code>07af21b</code></a>
feat: support parsing <code>import defer</code> (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/scope-manager/issues/12513">#12513</a>)</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.64.0/packages/scope-manager">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/tsconfig-utils` from 8.63.0 to 8.64.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/tsconfig-utils's
releases</a>.</em></p>
<blockquote>
<h2>v8.64.0</h2>
<h2>8.64.0 (2026-07-13)</h2>
<h3>🚀 Features</h3>
<ul>
<li>support parsing <code>import defer</code> (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12513">#12513</a>)</li>
<li><strong>eslint-plugin:</strong> [no-loop-func] support
<code>using</code> / <code>await using</code> declarations and deprecate
the rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12500">#12500</a>)</li>
<li><strong>typescript-estree:</strong> throw for invalid definite
assignment in class properties (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12543">#12543</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [require-array-sort-compare] handle
constrained arrays (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12512">#12512</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.64.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/tsconfig-utils/CHANGELOG.md">@​typescript-eslint/tsconfig-utils's
changelog</a>.</em></p>
<blockquote>
<h2>8.64.0 (2026-07-13)</h2>
<p>This was a version bump only for tsconfig-utils 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.64.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="414d9abbf6"><code>414d9ab</code></a>
chore(release): publish 8.64.0</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.64.0/packages/tsconfig-utils">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/type-utils` from 8.63.0 to 8.64.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/type-utils's
releases</a>.</em></p>
<blockquote>
<h2>v8.64.0</h2>
<h2>8.64.0 (2026-07-13)</h2>
<h3>🚀 Features</h3>
<ul>
<li>support parsing <code>import defer</code> (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12513">#12513</a>)</li>
<li><strong>eslint-plugin:</strong> [no-loop-func] support
<code>using</code> / <code>await using</code> declarations and deprecate
the rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12500">#12500</a>)</li>
<li><strong>typescript-estree:</strong> throw for invalid definite
assignment in class properties (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12543">#12543</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [require-array-sort-compare] handle
constrained arrays (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12512">#12512</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.64.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/type-utils/CHANGELOG.md">@​typescript-eslint/type-utils's
changelog</a>.</em></p>
<blockquote>
<h2>8.64.0 (2026-07-13)</h2>
<p>This was a version bump only for type-utils 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.64.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="414d9abbf6"><code>414d9ab</code></a>
chore(release): publish 8.64.0</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.64.0/packages/type-utils">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/types` from 8.63.0 to 8.64.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/types's
releases</a>.</em></p>
<blockquote>
<h2>v8.64.0</h2>
<h2>8.64.0 (2026-07-13)</h2>
<h3>🚀 Features</h3>
<ul>
<li>support parsing <code>import defer</code> (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12513">#12513</a>)</li>
<li><strong>eslint-plugin:</strong> [no-loop-func] support
<code>using</code> / <code>await using</code> declarations and deprecate
the rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12500">#12500</a>)</li>
<li><strong>typescript-estree:</strong> throw for invalid definite
assignment in class properties (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12543">#12543</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [require-array-sort-compare] handle
constrained arrays (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12512">#12512</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.64.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/types/CHANGELOG.md">@​typescript-eslint/types's
changelog</a>.</em></p>
<blockquote>
<h2>8.64.0 (2026-07-13)</h2>
<p>This was a version bump only for types 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.64.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="414d9abbf6"><code>414d9ab</code></a>
chore(release): publish 8.64.0</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.64.0/packages/types">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/typescript-estree` from 8.63.0 to 8.64.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/typescript-estree's
releases</a>.</em></p>
<blockquote>
<h2>v8.64.0</h2>
<h2>8.64.0 (2026-07-13)</h2>
<h3>🚀 Features</h3>
<ul>
<li>support parsing <code>import defer</code> (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12513">#12513</a>)</li>
<li><strong>eslint-plugin:</strong> [no-loop-func] support
<code>using</code> / <code>await using</code> declarations and deprecate
the rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12500">#12500</a>)</li>
<li><strong>typescript-estree:</strong> throw for invalid definite
assignment in class properties (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12543">#12543</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [require-array-sort-compare] handle
constrained arrays (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12512">#12512</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.64.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/typescript-estree/CHANGELOG.md">@​typescript-eslint/typescript-estree's
changelog</a>.</em></p>
<blockquote>
<h2>8.64.0 (2026-07-13)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>typescript-estree:</strong> throw for invalid definite
assignment in class properties (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12543">#12543</a>)</li>
<li>support parsing <code>import defer</code> (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12513">#12513</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.64.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="414d9abbf6"><code>414d9ab</code></a>
chore(release): publish 8.64.0</li>
<li><a
href="b418cf577f"><code>b418cf5</code></a>
feat(typescript-estree): throw for invalid definite assignment in class
prope...</li>
<li><a
href="07af21b912"><code>07af21b</code></a>
feat: support parsing <code>import defer</code> (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-estree/issues/12513">#12513</a>)</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.64.0/packages/typescript-estree">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/utils` from 8.63.0 to 8.64.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/utils's
releases</a>.</em></p>
<blockquote>
<h2>v8.64.0</h2>
<h2>8.64.0 (2026-07-13)</h2>
<h3>🚀 Features</h3>
<ul>
<li>support parsing <code>import defer</code> (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12513">#12513</a>)</li>
<li><strong>eslint-plugin:</strong> [no-loop-func] support
<code>using</code> / <code>await using</code> declarations and deprecate
the rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12500">#12500</a>)</li>
<li><strong>typescript-estree:</strong> throw for invalid definite
assignment in class properties (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12543">#12543</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [require-array-sort-compare] handle
constrained arrays (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12512">#12512</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.64.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/utils/CHANGELOG.md">@​typescript-eslint/utils's
changelog</a>.</em></p>
<blockquote>
<h2>8.64.0 (2026-07-13)</h2>
<p>This was a version bump only for utils 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.64.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="414d9abbf6"><code>414d9ab</code></a>
chore(release): publish 8.64.0</li>
<li><a
href="321856cdad"><code>321856c</code></a>
chore: cleanup cspell config (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/utils/issues/12526">#12526</a>)</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.64.0/packages/utils">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/visitor-keys` from 8.63.0 to 8.64.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/visitor-keys's
releases</a>.</em></p>
<blockquote>
<h2>v8.64.0</h2>
<h2>8.64.0 (2026-07-13)</h2>
<h3>🚀 Features</h3>
<ul>
<li>support parsing <code>import defer</code> (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12513">#12513</a>)</li>
<li><strong>eslint-plugin:</strong> [no-loop-func] support
<code>using</code> / <code>await using</code> declarations and deprecate
the rule (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12500">#12500</a>)</li>
<li><strong>typescript-estree:</strong> throw for invalid definite
assignment in class properties (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12543">#12543</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [require-array-sort-compare] handle
constrained arrays (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12512">#12512</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.64.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/visitor-keys/CHANGELOG.md">@​typescript-eslint/visitor-keys's
changelog</a>.</em></p>
<blockquote>
<h2>8.64.0 (2026-07-13)</h2>
<p>This was a version bump only for visitor-keys 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.64.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="414d9abbf6"><code>414d9ab</code></a>
chore(release): publish 8.64.0</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.64.0/packages/visitor-keys">compare
view</a></li>
</ul>
</details>
<br />

Updates `express-rate-limit` from 8.5.2 to 8.6.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/express-rate-limit/express-rate-limit/releases">express-rate-limit's
releases</a>.</em></p>
<blockquote>
<h2>v8.6.0</h2>
<p>You can view the changelog <a
href="https://express-rate-limit.mintlify.app/reference/changelog">here</a>.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="fffb3c4d6e"><code>fffb3c4</code></a>
8.6.0</li>
<li><a
href="f366b2d360"><code>f366b2d</code></a>
docs: debugging guide, time constants, &amp; v8.6.0 changelog (<a
href="https://redirect.github.com/express-rate-limit/express-rate-limit/issues/652">#652</a>)</li>
<li><a
href="593ddd2f4f"><code>593ddd2</code></a>
fix: make debug output easier to read (<a
href="https://redirect.github.com/express-rate-limit/express-rate-limit/issues/653">#653</a>)</li>
<li><a
href="ef8c1295ff"><code>ef8c129</code></a>
fix: Pin safe version of <code>@​asyncapi/specs</code> dev dep (<a
href="https://redirect.github.com/express-rate-limit/express-rate-limit/issues/659">#659</a>)</li>
<li><a
href="7b05e0dbd8"><code>7b05e0d</code></a>
feat: add time constants to support more readable values for windowMs
(<a
href="https://redirect.github.com/express-rate-limit/express-rate-limit/issues/655">#655</a>)</li>
<li><a
href="863e730c2f"><code>863e730</code></a>
chore(deps-dev): bump the development-dependencies group with 3 updates
(<a
href="https://redirect.github.com/express-rate-limit/express-rate-limit/issues/657">#657</a>)</li>
<li><a
href="e0e711e4c0"><code>e0e711e</code></a>
fix: correct wording in usage documentation for express-rate-limit (<a
href="https://redirect.github.com/express-rate-limit/express-rate-limit/issues/656">#656</a>)</li>
<li><a
href="fcd3aa7d5e"><code>fcd3aa7</code></a>
chore(deps-dev): bump the development-dependencies group with 3 updates
(<a
href="https://redirect.github.com/express-rate-limit/express-rate-limit/issues/651">#651</a>)</li>
<li><a
href="99d4298bda"><code>99d4298</code></a>
feat: use <code>debug</code> for debug logging (<a
href="https://redirect.github.com/express-rate-limit/express-rate-limit/issues/641">#641</a>)</li>
<li><a
href="23e4ddef55"><code>23e4dde</code></a>
feat: Run validations once each (<a
href="https://redirect.github.com/express-rate-limit/express-rate-limit/issues/650">#650</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/express-rate-limit/express-rate-limit/compare/v8.5.2...v8.6.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `obug` from 2.1.3 to 2.1.4
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/sxzz/obug/releases">obug's
releases</a>.</em></p>
<blockquote>
<h2>v2.1.4</h2>
<h3>   🐞 Bug Fixes</h3>
<ul>
<li>Fallbacks for when env variables are disallowed  -  by <a
href="https://github.com/vrugtehagel"><code>@​vrugtehagel</code></a> and
<a href="https://github.com/sxzz"><code>@​sxzz</code></a> in <a
href="https://redirect.github.com/sxzz/obug/issues/5">sxzz/obug#5</a> <a
href="https://github.com/sxzz/obug/commit/faf1f47"><!-- raw HTML omitted
-->(faf1f)<!-- raw HTML omitted --></a></li>
</ul>
<h5>    <a
href="https://github.com/sxzz/obug/compare/v2.1.3...v2.1.4">View changes
on GitHub</a></h5>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="e50bedcb5f"><code>e50bedc</code></a>
chore: release v2.1.4</li>
<li><a
href="12086c0ec0"><code>12086c0</code></a>
chore: upgrade deps</li>
<li><a
href="faf1f47e4f"><code>faf1f47</code></a>
fix: fallbacks for when env variables are disallowed (<a
href="https://redirect.github.com/sxzz/obug/issues/5">#5</a>)</li>
<li>See full diff in <a
href="https://github.com/sxzz/obug/compare/v2.1.3...v2.1.4">compare
view</a></li>
</ul>
</details>
<br />

Updates `vite` from 8.1.4 to 8.1.5
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/vitejs/vite/releases">vite's
releases</a>.</em></p>
<blockquote>
<h2>v8.1.5</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v8.1.5/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md">vite's
changelog</a>.</em></p>
<blockquote>
<h2><!-- raw HTML omitted --><a
href="https://github.com/vitejs/vite/compare/v8.1.4...v8.1.5">8.1.5</a>
(2026-07-16)<!-- raw HTML omitted --></h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>bundled-dev:</strong> avoid duplicated <code>buildEnd</code>
(<a
href="https://redirect.github.com/vitejs/vite/issues/22931">#22931</a>)
(<a
href="8100320970">8100320</a>)</li>
<li><strong>client:</strong> overlay error message format align rolldown
(<a
href="https://redirect.github.com/vitejs/vite/issues/22869">#22869</a>)
(<a
href="5a72b87807">5a72b87</a>)</li>
<li><strong>deps:</strong> update all non-major dependencies (<a
href="https://redirect.github.com/vitejs/vite/issues/22921">#22921</a>)
(<a
href="fef682d3f0">fef682d</a>)</li>
<li><strong>deps:</strong> update rolldown-related dependencies (<a
href="https://redirect.github.com/vitejs/vite/issues/22922">#22922</a>)
(<a
href="3c345e475a">3c345e4</a>)</li>
<li><strong>module-runner:</strong> don't crash stack-trace source
mapping when globalThis.Buffer is absent (<a
href="https://redirect.github.com/vitejs/vite/issues/22945">#22945</a>)
(<a
href="f8b38e316b">f8b38e3</a>)</li>
<li><strong>optimizer:</strong> respect importer module format for
dynamic import interop with CJS deps (<a
href="https://redirect.github.com/vitejs/vite/issues/22951">#22951</a>)
(<a
href="6c08c39ac4">6c08c39</a>)</li>
<li><strong>ssr:</strong> scope switch-case declarations to the switch,
not the function (<a
href="https://redirect.github.com/vitejs/vite/issues/22893">#22893</a>)
(<a
href="b59a73f76f">b59a73f</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li><strong>build:</strong> fix incorrect <code>@default</code> for
build.cssMinify (<a
href="https://redirect.github.com/vitejs/vite/issues/22948">#22948</a>)
(<a
href="c88c236186">c88c236</a>)</li>
<li><strong>build:</strong> fix incorrect <code>@default</code> for
build.lib.formats (<a
href="https://redirect.github.com/vitejs/vite/issues/22911">#22911</a>)
(<a
href="369ed609a4">369ed60</a>)</li>
</ul>
<h3>Tests</h3>
<ul>
<li>avoid scanner scanning all files under <code>__tests__</code> (<a
href="https://redirect.github.com/vitejs/vite/issues/22912">#22912</a>)
(<a
href="c961cae286">c961cae</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="5e7fe129a4"><code>5e7fe12</code></a>
release: v8.1.5</li>
<li><a
href="6c08c39ac4"><code>6c08c39</code></a>
fix(optimizer): respect importer module format for dynamic import
interop wit...</li>
<li><a
href="5a72b87807"><code>5a72b87</code></a>
fix(client): overlay error message format align rolldown (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22869">#22869</a>)</li>
<li><a
href="f8b38e316b"><code>f8b38e3</code></a>
fix(module-runner): don't crash stack-trace source mapping when
globalThis.Bu...</li>
<li><a
href="8100320970"><code>8100320</code></a>
fix(bundled-dev): avoid duplicated <code>buildEnd</code> (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22931">#22931</a>)</li>
<li><a
href="c88c236186"><code>c88c236</code></a>
docs(build): fix incorrect <code>@default</code> for build.cssMinify (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22948">#22948</a>)</li>
<li><a
href="b59a73f76f"><code>b59a73f</code></a>
fix(ssr): scope switch-case declarations to the switch, not the function
(<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22">#22</a>...</li>
<li><a
href="fef682d3f0"><code>fef682d</code></a>
fix(deps): update all non-major dependencies (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22921">#22921</a>)</li>
<li><a
href="3c345e475a"><code>3c345e4</code></a>
fix(deps): update rolldown-related dependencies (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22922">#22922</a>)</li>
<li><a
href="369ed609a4"><code>369ed60</code></a>
docs(build): fix incorrect <code>@default</code> for build.lib.formats
(<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22911">#22911</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/vitejs/vite/commits/v8.1.5/packages/vite">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

---------

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-07-20 11:02:00 +00:00
Ilia Shulgin
cc5d5e8b80 build: Trigger registry update after publishing new version (#888) 2026-07-17 10:18:20 +02:00
Mark Tkachenko
82cd692e50 feat: implement configurable LLM providers (#853)
According to [custom endpoints
rfd](https://github.com/agentclientprotocol/agent-client-protocol/blob/main/docs/rfds/custom-llm-endpoint.mdx)
Implement the Configurable LLM Providers RFD so clients can discover and
route the agent's LLM traffic through their own proxy or gateway without
relying solely on the ad-hoc gateway auth _meta mechanism.
2026-07-16 17:37:08 +02:00
acp-release-bot[bot]
30b7c06f76 chore(main): release 0.59.0 (#860)
🤖 I have created a release *beep* *boop*
---


##
[0.59.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.58.1...v0.59.0)
(2026-07-13)


### Features

* **deps-dev:** bump nanoid from 3.3.15 to 3.3.16
([#875](https://github.com/agentclientprotocol/claude-agent-acp/issues/875))
([e67dacd](e67dacdcac))
* **deps:** bump @anthropic-ai/claude-agent-sdk to 0.3.207
([#874](https://github.com/agentclientprotocol/claude-agent-acp/issues/874))
([c7f5b8f](c7f5b8fe76))


### Bug Fixes

* Add subagent parent tool use attribution
([#859](https://github.com/agentclientprotocol/claude-agent-acp/issues/859))
([9cd48c5](9cd48c597a))
* forward result text when the turn emitted no assistant message
([#858](https://github.com/agentclientprotocol/claude-agent-acp/issues/858))
([61ae860](61ae8609d4))
* hold a turn open while its background subagents are still live
([#870](https://github.com/agentclientprotocol/claude-agent-acp/issues/870))
([7a70f82](7a70f82739))
* Refine tool calls from streamed input
([#867](https://github.com/agentclientprotocol/claude-agent-acp/issues/867))
([2c19974](2c19974d5b))
* Seed context window from SDK usage report
([#868](https://github.com/agentclientprotocol/claude-agent-acp/issues/868))
([3ba2d36](3ba2d367a2)),
closes
[#596](https://github.com/agentclientprotocol/claude-agent-acp/issues/596)
* Skip synthetic login messages on replay
([#869](https://github.com/agentclientprotocol/claude-agent-acp/issues/869))
([c7dff3c](c7dff3cf7e)),
closes
[#863](https://github.com/agentclientprotocol/claude-agent-acp/issues/863)

---
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-07-13 18:46:18 +02:00
dependabot[bot]
c7f5b8fe76 feat(deps): bump @anthropic-ai/claude-agent-sdk to 0.3.207 (#874)
Bumps the minor group with 14 updates:

| Package | From | To |
| --- | --- | --- |
|
[@anthropic-ai/claude-agent-sdk](https://github.com/anthropics/claude-agent-sdk-typescript)
| `0.3.206` | `0.3.207` |
|
[@anthropic-ai/sdk](https://github.com/anthropics/anthropic-sdk-typescript)
| `0.110.0` | `0.111.0` |
| [eslint](https://github.com/eslint/eslint) | `10.6.0` | `10.7.0` |
| @anthropic-ai/claude-agent-sdk-darwin-arm64 | `0.3.206` | `0.3.207` |
| @anthropic-ai/claude-agent-sdk-darwin-x64 | `0.3.206` | `0.3.207` |
| @anthropic-ai/claude-agent-sdk-linux-arm64 | `0.3.206` | `0.3.207` |
| @anthropic-ai/claude-agent-sdk-linux-arm64-musl | `0.3.206` |
`0.3.207` |
| @anthropic-ai/claude-agent-sdk-linux-x64 | `0.3.206` | `0.3.207` |
| @anthropic-ai/claude-agent-sdk-linux-x64-musl | `0.3.206` | `0.3.207`
|
| @anthropic-ai/claude-agent-sdk-win32-arm64 | `0.3.206` | `0.3.207` |
| @anthropic-ai/claude-agent-sdk-win32-x64 | `0.3.206` | `0.3.207` |
| [es-module-lexer](https://github.com/guybedford/es-module-lexer) |
`2.3.0` | `2.3.1` |
| [hono](https://github.com/honojs/hono) | `4.12.29` | `4.12.30` |
| [postcss](https://github.com/postcss/postcss) | `8.5.16` | `8.5.19` |

Updates `@anthropic-ai/claude-agent-sdk` from 0.3.206 to 0.3.207
<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.207</h2>
<h2>What's changed</h2>
<ul>
<li>Fixed <code>canUseTool</code> returning <code>{behavior:
'allow'}</code> without <code>updatedInput</code> being rejected as a
deny with a raw ZodError message; the tool now runs with the original
input per the documented contract</li>
<li>The Agent tool's structured result now has a published SDK type
(<code>AgentToolCompletedOutput</code>) that matches the emitted object
exactly</li>
</ul>
<h2>Update</h2>
<pre lang="sh"><code>npm install @anthropic-ai/claude-agent-sdk@0.3.207
# or
yarn add @anthropic-ai/claude-agent-sdk@0.3.207
# or
pnpm add @anthropic-ai/claude-agent-sdk@0.3.207
# or
bun add @anthropic-ai/claude-agent-sdk@0.3.207
</code></pre>
</blockquote>
</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.207</h2>
<ul>
<li>Fixed <code>canUseTool</code> returning <code>{behavior:
'allow'}</code> without <code>updatedInput</code> being rejected as a
deny with a raw ZodError message; the tool now runs with the original
input per the documented contract</li>
<li>The Agent tool's structured result now has a published SDK type
(<code>AgentToolCompletedOutput</code>) that matches the emitted object
exactly</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="79b6350e13"><code>79b6350</code></a>
chore: Update CHANGELOG.md</li>
<li>See full diff in <a
href="https://github.com/anthropics/claude-agent-sdk-typescript/compare/v0.3.206...v0.3.207">compare
view</a></li>
</ul>
</details>
<br />

Updates `@anthropic-ai/sdk` from 0.110.0 to 0.111.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.111.0</h2>
<h2>0.111.0 (2026-07-10)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.110.0...sdk-v0.111.0">sdk-v0.110.0...sdk-v0.111.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> add support for dreaming (<a
href="77b28a6472">77b28a6</a>)</li>
<li><strong>tools:</strong> gate session tool calls on
evaluated_permission; bound idle by server stop_reason (<a
href="68a6d7b92a">68a6d7b</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><strong>docs:</strong> small updates to field descriptions (<a
href="e25b885eac">e25b885</a>)</li>
<li><strong>docs:</strong> update model example (<a
href="a33f3f0b7c">a33f3f0</a>)</li>
<li><strong>docs:</strong> updates to descriptions and examples (<a
href="eac4bace32">eac4bac</a>)</li>
</ul>
</blockquote>
</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.111.0 (2026-07-10)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.110.0...sdk-v0.111.0">sdk-v0.110.0...sdk-v0.111.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> add support for dreaming (<a
href="77b28a6472">77b28a6</a>)</li>
<li><strong>tools:</strong> gate session tool calls on
evaluated_permission; bound idle by server stop_reason (<a
href="68a6d7b92a">68a6d7b</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><strong>docs:</strong> small updates to field descriptions (<a
href="e25b885eac">e25b885</a>)</li>
<li><strong>docs:</strong> update model example (<a
href="a33f3f0b7c">a33f3f0</a>)</li>
<li><strong>docs:</strong> updates to descriptions and examples (<a
href="eac4bace32">eac4bac</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="9e46760688"><code>9e46760</code></a>
chore: release main</li>
<li><a
href="8d461ea962"><code>8d461ea</code></a>
feat(api): add support for dreaming</li>
<li><a
href="9436e29159"><code>9436e29</code></a>
codegen metadata</li>
<li><a
href="0aec1e748b"><code>0aec1e7</code></a>
feat(tools): gate session tool calls on evaluated_permission; bound idle
by s...</li>
<li><a
href="ac2fc6779d"><code>ac2fc67</code></a>
chore(docs): update model example</li>
<li><a
href="c8af65d6af"><code>c8af65d</code></a>
chore(docs): updates to descriptions and examples</li>
<li><a
href="2a3a9042ab"><code>2a3a904</code></a>
codegen metadata</li>
<li><a
href="57c56c9172"><code>57c56c9</code></a>
chore(docs): small updates to field descriptions</li>
<li>See full diff in <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.110.0...sdk-v0.111.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `eslint` from 10.6.0 to 10.7.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/eslint/eslint/releases">eslint's
releases</a>.</em></p>
<blockquote>
<h2>v10.7.0</h2>
<h2>Features</h2>
<ul>
<li><a
href="cf2a9bf2d9"><code>cf2a9bf</code></a>
feat: add errorClassNames option to preserve-caught-error rule (<a
href="https://redirect.github.com/eslint/eslint/issues/21032">#21032</a>)
(sethamus)</li>
<li><a
href="f8b873aae5"><code>f8b873a</code></a>
feat: max-nested-callbacks option for constructor callbacks (<a
href="https://redirect.github.com/eslint/eslint/issues/21063">#21063</a>)
(fnx)</li>
<li><a
href="557fde8bc6"><code>557fde8</code></a>
feat: support computed <code>Number.parseInt</code> member access in
<code>radix</code> rule (<a
href="https://redirect.github.com/eslint/eslint/issues/21041">#21041</a>)
(Pixel)</li>
<li><a
href="0b4a73b658"><code>0b4a73b</code></a>
feat: add suggestions to no-compare-neg-zero (<a
href="https://redirect.github.com/eslint/eslint/issues/21034">#21034</a>)
(den$)</li>
<li><a
href="96cdd427db"><code>96cdd42</code></a>
feat: report invalid signed numeric radix values in <code>radix</code>
rule (<a
href="https://redirect.github.com/eslint/eslint/issues/21030">#21030</a>)
(Pixel)</li>
</ul>
<h2>Bug Fixes</h2>
<ul>
<li><a
href="3e7bf15e69"><code>3e7bf15</code></a>
fix: apply <code>ignoreClassesWithImplements</code> to class expressions
(<a
href="https://redirect.github.com/eslint/eslint/issues/21069">#21069</a>)
(Pixel)</li>
<li><a
href="0d7d70cf01"><code>0d7d70c</code></a>
fix: insert cause outside wrapping parens in preserve-caught-error (<a
href="https://redirect.github.com/eslint/eslint/issues/21062">#21062</a>)
(Mahin Anowar)</li>
<li><a
href="75ec753226"><code>75ec753</code></a>
fix: handle static template literals in <code>eqeqeq</code> rule (<a
href="https://redirect.github.com/eslint/eslint/issues/21058">#21058</a>)
(Pixel)</li>
<li><a
href="b717a22e24"><code>b717a22</code></a>
fix: prevent <code>eqeqeq</code> null option from reporting non-equality
operators (<a
href="https://redirect.github.com/eslint/eslint/issues/21057">#21057</a>)
(Pixel)</li>
<li><a
href="e35b05f196"><code>e35b05f</code></a>
fix: avoid <code>no-invalid-regexp</code> false positive for shadowed
RegExp (<a
href="https://redirect.github.com/eslint/eslint/issues/21051">#21051</a>)
(Pixel)</li>
<li><a
href="a3172b69c7"><code>a3172b6</code></a>
fix: avoid <code>no-control-regex</code> false positive for shadowed
RegExp (<a
href="https://redirect.github.com/eslint/eslint/issues/21050">#21050</a>)
(Pixel)</li>
<li><a
href="d1f637eca2"><code>d1f637e</code></a>
fix: parenthesize sequence expression operands in no-implicit-coercion
(<a
href="https://redirect.github.com/eslint/eslint/issues/21045">#21045</a>)
(spokodev)</li>
<li><a
href="8859bafb01"><code>8859baf</code></a>
fix: avoid prefer-numeric-literals false positive for shadowed globals
(<a
href="https://redirect.github.com/eslint/eslint/issues/21047">#21047</a>)
(한국)</li>
<li><a
href="a9e5961050"><code>a9e5961</code></a>
fix: use-isnan false positive on shadowed NaN/Number (<a
href="https://redirect.github.com/eslint/eslint/issues/20958">#20958</a>)
(sethamus)</li>
<li><a
href="8a240a7610"><code>8a240a7</code></a>
fix: avoid false positives in <code>radix</code> rule for spread
arguments (<a
href="https://redirect.github.com/eslint/eslint/issues/21044">#21044</a>)
(Pixel)</li>
</ul>
<h2>Documentation</h2>
<ul>
<li><a
href="c30d80801c"><code>c30d808</code></a>
docs: Update README (GitHub Actions Bot)</li>
<li><a
href="5139800b23"><code>5139800</code></a>
docs: document ESLint migration codemods in v9 and v10 guides (<a
href="https://redirect.github.com/eslint/eslint/issues/20980">#20980</a>)
(Alex Bit)</li>
<li><a
href="04174cbfaa"><code>04174cb</code></a>
docs: Update README (GitHub Actions Bot)</li>
<li><a
href="026e130408"><code>026e130</code></a>
docs: update semver policy for bug fixes (<a
href="https://redirect.github.com/eslint/eslint/issues/21048">#21048</a>)
(Milos Djermanovic)</li>
<li><a
href="9d42fefb0e"><code>9d42fef</code></a>
docs: Update README (GitHub Actions Bot)</li>
<li><a
href="b23015955c"><code>b230159</code></a>
docs: Update README (GitHub Actions Bot)</li>
<li><a
href="0129972ba1"><code>0129972</code></a>
docs: correct <code>**/.js</code> glob to <code>**/*.js</code> in config
files guide (<a
href="https://redirect.github.com/eslint/eslint/issues/21036">#21036</a>)
(EduardF1)</li>
</ul>
<h2>Chores</h2>
<ul>
<li><a
href="948937986f"><code>9489379</code></a>
chore: update dependency <code>@​eslint/eslintrc</code> to ^3.3.6 (<a
href="https://redirect.github.com/eslint/eslint/issues/21076">#21076</a>)
(renovate[bot])</li>
<li><a
href="81a4774a92"><code>81a4774</code></a>
chore: updates for v9.39.5 release (Jenkins)</li>
<li><a
href="9835414913"><code>9835414</code></a>
chore: enable <code>$ExpectType</code> annotations in all TypeScript
files (<a
href="https://redirect.github.com/eslint/eslint/issues/21071">#21071</a>)
(Francesco Trotta)</li>
<li><a
href="72adf6bd21"><code>72adf6b</code></a>
chore: restrict <code>markdownlint-cli2</code> updates in renovate (<a
href="https://redirect.github.com/eslint/eslint/issues/21067">#21067</a>)
(lumir)</li>
<li><a
href="833ec10fd7"><code>833ec10</code></a>
chore: update dependency prettier to v3.9.4 (<a
href="https://redirect.github.com/eslint/eslint/issues/21061">#21061</a>)
(renovate[bot])</li>
<li><a
href="7ea106ddcb"><code>7ea106d</code></a>
chore: update ecosystem plugins (<a
href="https://redirect.github.com/eslint/eslint/issues/21059">#21059</a>)
(ESLint Bot)</li>
<li><a
href="8fb550e7c2"><code>8fb550e</code></a>
chore: add prettier update commit to <code>.git-blame-ignore-revs</code>
(<a
href="https://redirect.github.com/eslint/eslint/issues/21056">#21056</a>)
(lumir)</li>
<li><a
href="e4e11668ea"><code>e4e1166</code></a>
chore: update dependency prettier to v3.9.1 (<a
href="https://redirect.github.com/eslint/eslint/issues/21055">#21055</a>)
(renovate[bot])</li>
<li><a
href="0493f53f3f"><code>0493f53</code></a>
chore: update prettier to v3.9.0 (<a
href="https://redirect.github.com/eslint/eslint/issues/21054">#21054</a>)
(Pixel)</li>
<li><a
href="1056a996c6"><code>1056a99</code></a>
chore: update dependency prettier to v3.8.5 (<a
href="https://redirect.github.com/eslint/eslint/issues/21049">#21049</a>)
(renovate[bot])</li>
<li><a
href="4d4155df4a"><code>4d4155d</code></a>
ci: run ecosystem tests on pull requests (<a
href="https://redirect.github.com/eslint/eslint/issues/21027">#21027</a>)
(sethamus)</li>
<li><a
href="993539fd45"><code>993539f</code></a>
chore: update dependency <code>@​eslint/json</code> to ^2.0.1 (<a
href="https://redirect.github.com/eslint/eslint/issues/21042">#21042</a>)
(renovate[bot])</li>
<li><a
href="53f8b69c15"><code>53f8b69</code></a>
test: add error locations to <code>no-constant-binary-expression</code>
(<a
href="https://redirect.github.com/eslint/eslint/issues/21039">#21039</a>)
(lumir)</li>
<li><a
href="5ab71d52f9"><code>5ab71d5</code></a>
refactor: clean up radix rule internals (<a
href="https://redirect.github.com/eslint/eslint/issues/21015">#21015</a>)
(Pixel)</li>
<li><a
href="a80a9a4614"><code>a80a9a4</code></a>
chore: update ecosystem plugins (<a
href="https://redirect.github.com/eslint/eslint/issues/21035">#21035</a>)
(ESLint Bot)</li>
<li><a
href="7c9a02933c"><code>7c9a029</code></a>
ci: add Node.js 26 to CI (<a
href="https://redirect.github.com/eslint/eslint/issues/20847">#20847</a>)
(lumir)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="fabd99bb74"><code>fabd99b</code></a>
10.7.0</li>
<li><a
href="37c5e75949"><code>37c5e75</code></a>
Build: changelog update for 10.7.0</li>
<li><a
href="948937986f"><code>9489379</code></a>
chore: update dependency <code>@​eslint/eslintrc</code> to ^3.3.6 (<a
href="https://redirect.github.com/eslint/eslint/issues/21076">#21076</a>)</li>
<li><a
href="81a4774a92"><code>81a4774</code></a>
chore: updates for v9.39.5 release</li>
<li><a
href="3e7bf15e69"><code>3e7bf15</code></a>
fix: apply <code>ignoreClassesWithImplements</code> to class expressions
(<a
href="https://redirect.github.com/eslint/eslint/issues/21069">#21069</a>)</li>
<li><a
href="9835414913"><code>9835414</code></a>
chore: enable <code>$ExpectType</code> annotations in all TypeScript
files (<a
href="https://redirect.github.com/eslint/eslint/issues/21071">#21071</a>)</li>
<li><a
href="cf2a9bf2d9"><code>cf2a9bf</code></a>
feat: add errorClassNames option to preserve-caught-error rule (<a
href="https://redirect.github.com/eslint/eslint/issues/21032">#21032</a>)</li>
<li><a
href="c30d80801c"><code>c30d808</code></a>
docs: Update README</li>
<li><a
href="f8b873aae5"><code>f8b873a</code></a>
feat: max-nested-callbacks option for constructor callbacks (<a
href="https://redirect.github.com/eslint/eslint/issues/21063">#21063</a>)</li>
<li><a
href="72adf6bd21"><code>72adf6b</code></a>
chore: restrict <code>markdownlint-cli2</code> updates in renovate (<a
href="https://redirect.github.com/eslint/eslint/issues/21067">#21067</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/eslint/eslint/compare/v10.6.0...v10.7.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `@anthropic-ai/claude-agent-sdk-darwin-arm64` from 0.3.206 to
0.3.207

Updates `@anthropic-ai/claude-agent-sdk-darwin-x64` from 0.3.206 to
0.3.207

Updates `@anthropic-ai/claude-agent-sdk-linux-arm64` from 0.3.206 to
0.3.207

Updates `@anthropic-ai/claude-agent-sdk-linux-arm64-musl` from 0.3.206
to 0.3.207

Updates `@anthropic-ai/claude-agent-sdk-linux-x64` from 0.3.206 to
0.3.207

Updates `@anthropic-ai/claude-agent-sdk-linux-x64-musl` from 0.3.206 to
0.3.207

Updates `@anthropic-ai/claude-agent-sdk-win32-arm64` from 0.3.206 to
0.3.207

Updates `@anthropic-ai/claude-agent-sdk-win32-x64` from 0.3.206 to
0.3.207

Updates `es-module-lexer` from 2.3.0 to 2.3.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/guybedford/es-module-lexer/releases">es-module-lexer's
releases</a>.</em></p>
<blockquote>
<h2>2.3.1</h2>
<h2>What's Changed</h2>
<ul>
<li>perf: fast native UTF-16 source copy into Wasm memory on Node by <a
href="https://github.com/guybedford"><code>@​guybedford</code></a> in <a
href="https://redirect.github.com/guybedford/es-module-lexer/pull/224">guybedford/es-module-lexer#224</a></li>
<li>perf(lexer): gate keywordStart on the next byte for i/c tokens by <a
href="https://github.com/BridgeAR"><code>@​BridgeAR</code></a> in <a
href="https://redirect.github.com/guybedford/es-module-lexer/pull/223">guybedford/es-module-lexer#223</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/guybedford/es-module-lexer/compare/2.3.0...2.3.1">https://github.com/guybedford/es-module-lexer/compare/2.3.0...2.3.1</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="2b2e6209ba"><code>2b2e620</code></a>
2.3.1</li>
<li><a
href="8c21b74b1f"><code>8c21b74</code></a>
perf(lexer): gate keywordStart on the next byte for i/c tokens (<a
href="https://redirect.github.com/guybedford/es-module-lexer/issues/223">#223</a>)</li>
<li><a
href="b8f9d89f9d"><code>b8f9d89</code></a>
perf: fast native UTF-16 source copy into Wasm memory on Node (<a
href="https://redirect.github.com/guybedford/es-module-lexer/issues/224">#224</a>)</li>
<li>See full diff in <a
href="https://github.com/guybedford/es-module-lexer/compare/2.3.0...2.3.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `hono` from 4.12.29 to 4.12.30
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/honojs/hono/releases">hono's
releases</a>.</em></p>
<blockquote>
<h2>v4.12.30</h2>
<h2>What's Changed</h2>
<ul>
<li>chore(benchmark/routers): bump deps in <a
href="https://redirect.github.com/honojs/hono/pull/5107">honojs/hono#5107</a></li>
<li>chore(benchmark): remove not used benchmarks in <a
href="https://redirect.github.com/honojs/hono/pull/5108">honojs/hono#5108</a></li>
<li>chore: update to ts6 in prep for ts7 in <a
href="https://redirect.github.com/honojs/hono/pull/5104">honojs/hono#5104</a></li>
<li>fix(cache): deduplicate Cache-Control directives case-insensitively
in <a
href="https://redirect.github.com/honojs/hono/pull/5025">honojs/hono#5025</a></li>
<li>fix(compress): do not compress 206 Partial Content responses in <a
href="https://redirect.github.com/honojs/hono/pull/5020">honojs/hono#5020</a></li>
<li>fix(client): replaceUrlParam should not match a param that prefixes
another in <a
href="https://redirect.github.com/honojs/hono/pull/5096">honojs/hono#5096</a></li>
<li>fix(method-override): set duplex when forwarding a stream body in
query mode in <a
href="https://redirect.github.com/honojs/hono/pull/5110">honojs/hono#5110</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/honojs/hono/compare/v4.12.29...v4.12.30">https://github.com/honojs/hono/compare/v4.12.29...v4.12.30</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="b2ae3a2204"><code>b2ae3a2</code></a>
4.12.30</li>
<li><a
href="e0cdeb0a89"><code>e0cdeb0</code></a>
fix(method-override): set duplex when forwarding a stream body in query
mode ...</li>
<li><a
href="653025e5c3"><code>653025e</code></a>
fix(client): replaceUrlParam should not match a param that prefixes
another (...</li>
<li><a
href="67efb27f7b"><code>67efb27</code></a>
fix(compress): do not compress 206 Partial Content responses (<a
href="https://redirect.github.com/honojs/hono/issues/5020">#5020</a>)</li>
<li><a
href="a48c8bf248"><code>a48c8bf</code></a>
fix(cache): deduplicate Cache-Control directives case-insensitively (<a
href="https://redirect.github.com/honojs/hono/issues/5025">#5025</a>)</li>
<li><a
href="3bb9a0e3a7"><code>3bb9a0e</code></a>
chore: update to ts6 in prep for ts7 (<a
href="https://redirect.github.com/honojs/hono/issues/5104">#5104</a>)</li>
<li><a
href="d3f97caa29"><code>d3f97ca</code></a>
chore(benchmark): remove not used benchmarks (<a
href="https://redirect.github.com/honojs/hono/issues/5108">#5108</a>)</li>
<li><a
href="29a3f73fd5"><code>29a3f73</code></a>
chore(benchmark/routers): bump deps (<a
href="https://redirect.github.com/honojs/hono/issues/5107">#5107</a>)</li>
<li>See full diff in <a
href="https://github.com/honojs/hono/compare/v4.12.29...v4.12.30">compare
view</a></li>
</ul>
</details>
<br />

Updates `postcss` from 8.5.16 to 8.5.19
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/postcss/postcss/releases">postcss's
releases</a>.</em></p>
<blockquote>
<h2>8.5.19</h2>
<ul>
<li>Fixed cleaning <code>before</code> for new nodes inserted to
<code>Root</code> (by <a
href="https://github.com/MahinAnowar"><code>@​MahinAnowar</code></a>).</li>
</ul>
<h2>8.5.18</h2>
<ul>
<li>Restricted loading previous source maps file to the
<code>opts.from</code> folder for security reasons (use <code>unsafeMap:
true</code> to disable the check).</li>
</ul>
<h2>8.5.17</h2>
<ul>
<li>Fixed <code>Maximum call stack size exceeded</code> error.</li>
<li>Fixed Prototype hijacking for <code>postcss.fromJSON()</code>.</li>
<li>Fixed <code>Input#origin()</code> for unmapped end position (by <a
href="https://github.com/chatman-media"><code>@​chatman-media</code></a>).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/postcss/postcss/blob/main/CHANGELOG.md">postcss's
changelog</a>.</em></p>
<blockquote>
<h2>8.5.19</h2>
<ul>
<li>Fixed cleaning <code>before</code> for new nodes inserted to
<code>Root</code> (by <a
href="https://github.com/MahinAnowar"><code>@​MahinAnowar</code></a>).</li>
</ul>
<h2>8.5.18</h2>
<ul>
<li>Restricted loading previous source maps file to the
<code>opts.from</code> folder for security reasons (use <code>unsafeMap:
true</code> to disable the check).</li>
</ul>
<h2>8.5.17</h2>
<ul>
<li>Fixed <code>Maximum call stack size exceeded</code> error.</li>
<li>Fixed Prototype hijacking for <code>postcss.fromJSON()</code>.</li>
<li>Fixed <code>Input#origin()</code> for unmapped end position (by <a
href="https://github.com/chatman-media"><code>@​chatman-media</code></a>).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="9543b22769"><code>9543b22</code></a>
Release 8.5.19 version</li>
<li><a
href="3d13bf9360"><code>3d13bf9</code></a>
Fix CI on Windows too</li>
<li><a
href="00d0dd2322"><code>00d0dd2</code></a>
Keep explicitly set raws.before when inserting nodes into root (<a
href="https://redirect.github.com/postcss/postcss/issues/2111">#2111</a>)</li>
<li><a
href="7a05b33e7a"><code>7a05b33</code></a>
Temporary fix CI</li>
<li><a
href="4c0d194c13"><code>4c0d194</code></a>
Release 8.5.18 version</li>
<li><a
href="92b4e7891e"><code>92b4e78</code></a>
Update dependencies</li>
<li><a
href="95663d3eb7"><code>95663d3</code></a>
Limit where source map can be loaded for security reasons</li>
<li><a
href="74e25ae9f4"><code>74e25ae</code></a>
Release 8.5.17 version</li>
<li><a
href="d1518afd5a"><code>d1518af</code></a>
Fix Maximum call stack size exceeded error</li>
<li><a
href="2421312ffe"><code>2421312</code></a>
Fix linter</li>
<li>Additional commits viewable in <a
href="https://github.com/postcss/postcss/compare/8.5.16...8.5.19">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

---------

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-07-13 16:44:29 +00:00
Ben Brandt
7a70f82739 fix: hold a turn open while its background subagents are still live (#870)
## Problem

`session/prompt` settled at the turn's terminal `result`, but a turn
that launched an **async subagent** is not done at that point.
Everything after settlement — the subagent's streamed tool calls, the
model's promised task-notification followup summary, and any
`session/request_permission` the subagent raises — landed **outside any
turn**. Some clients stops consuming updates at the prompt response, so
the output was silently lost and the permission request deadlocked on an
RPC nobody answers.

Reported as #864 (no signal for the post-turn activity), #865
(background tasks not trackable), and #866 (end-to-end field report
incl. the permission deadlock).

## What I verified before designing this

Driving the adapter over real ACP stdio against the bundled CLI
(2.1.206) with `emitRawSDKMessages` on showed that the SDK's trailing
`session_state_changed: idle` **does not wait for background agents** —
it follows the user result immediately, while the subagent is still
running, and the session then cycles:

```
user result → idle → (subagent works) → task_notification → followup turn → result(origin: task-notification) → idle
```

So the old #463-era "settle at idle" design would **not** fix this on
current CLIs (and plain re-adopting it would still trade away #773). The
hold has to be keyed on the live-subagent set and span those idle
cycles.

## Fix

A user turn whose terminal result arrives while subagents **it spawned**
are still live stores its outcome on `Turn.deferredSettle` instead of
settling. The prompt stays pending, so all of the subagent's output and
permission requests arrive inside a live turn. It settles with the
stored stop reason/usage:

- at the **task-notification followup's terminal result** — the promised
summary has fully streamed by then (the common case), or
- at an **idle with none of its subagents left**, when no followup
comes, or
- earlier, via `session/cancel` or the next prompt's echo hand-off — a
long-running subagent never holds the prompt hostage, and the hand-off
reports the recorded stop reason instead of rewriting it to `end_turn`.

Tracking: `Turn.spawnedTaskIds` ∩ `session.liveSubagentTasks`, fed by
`task_started` (only entries with `subagent_type`; background shells are
deliberately excluded — a dev server outlives every turn), pruned at
`task_notification`/terminal `task_updated`, and reconciled against
`background_tasks_changed`'s replace-semantics payload so a lost bookend
can't wedge the hold. A spawn during the held-open window (an agent
chain) extends the hold naturally.

Turns that spawned nothing (or whose subagents finished in-turn) still
settle at their result — the #773 responsiveness is untouched.

## Live verification (adapter on real stdio, CLI 2.1.206, temp cwd)

Prompt: *"launch a background agent to read the two files and create
summary.txt, then end your turn saying you'll report back"*:

```
[ 15.72] raw: result success            ← user result; subagent live → HELD
[ 15.72] raw: session_state_changed idle  ← immediate idle survives the hold
[ 17.50] update: tool_call Read hello.txt       ← subagent work, in-turn
[ 21.39] update: tool_call Write summary.txt    ← in-turn
[ 24.17] raw: task_notification completed
[ 34.35] update: agent_message_chunk "The background agent finished. It created summary.txt…"  ← promised summary, in-turn
[ 35.70] raw: result success (origin task-notification)
[ 35.70] PROMPT RESOLVED end_turn, usage = user turn's own tokens
```

No-regression check: a plain `"say hello"` prompt resolves the instant
its result lands (2.09s, zero added latency).

## Hardening from the adversarial review pass

A high-effort review (8 finder angles) of the change surfaced three
confirmed defects, each fixed with a regression test that fails pre-fix:

- **cancel() of a held turn stalled ~30s**: the session is already idle
during the hold, so the interrupt may never emit the trailing idle the
cancelled-settle path waited on. cancel() now settles a deferred turn
immediately (its result is already in hand); this also prevents the
force-cancel backstop from seeding a phantom orphan for a result that
already passed.
- **A followup's `is_error` / "Please run /login" could reject a held
user turn** whose own result was a success (reachable with parallel
subagents). Followup results now never touch the user-turn lifecycle.
- **The followup's own trailing idle was un-owed**: settling at the
followup result unblocks the client at exactly the moment that idle can
lag past the next prompt's echo — which would have been read as the
fresh turn ending without a result (#825 false-fail). All results now
record their trailer debt.

A second review round on the post-merge state (after resolving conflicts
with #858) found and fixed five more: an echo-less command (`/context`)
during a hold overwrote the held turn's outcome and hung
(ensureActiveTurn now hands the held turn off and promotes the head);
the refusal lane bypassed the deferral gate (result-time settles now
route through a shared `settleOrDefer`); cancel()'s inline settle
couldn't record the interrupt trailer's owed-idle debt (the counter
moved onto the Session) and skipped the backstop disarm; `failAllTurns`
rejected a held turn whose answer had already streamed (now resolves the
stored outcome, like stream-done); and a followup result inside a cancel
window skipped the trailer count. Settling at a followup result also now
closes the #453 delivery stretch so a following replayed turn's fallback
isn't suppressed.

Known residuals, deliberately accepted: a subagent whose settle bookends
are all lost parks the held turn until `session/cancel` or the next
prompt (same rescue contract as the adapter's other wedge classes); a
wake whose origin can't be attributed (the SDK's `origin` carries no
task id) can settle the turn one followup early, degrading to today's
behavior, never worse.

## Verification

Unit: 530 tests green; the deferral/cancel/false-fail tests were each
run against the pre-fix adapter and fail there. Live (real ACP stdio,
CLI 2.1.206): held turn with in-turn subagent output + followup summary,
resolution at the followup's result; plain prompts resolve instantly at
their result.

## Out of scope (follow-ups)

- A background **shell**'s exit waking the model between turns remains
an out-of-turn episode — there is no turn to hold for it without
blocking every prompt behind a dev server. Same for clients that want a
session-level active/idle or background-task level signal (#864/#865's
additive asks): both raw signals are already available today via
`_meta.claudeCode.emitRawSDKMessages` → `_claude/sdkMessage` ext
notifications.
- Flipping the spawning `Task` tool call's status at terminal
`task_updated` (#865's optional ask).

A third review round on the consolidated state fixed: the reconciliation
deleting live sync-subagent attribution entries (the level's universe is
background-only — absent subagent entries are now kept but marked
`endedPerLevel` so holds stop waiting on them);
peer/coordinator/observer-origin results being treated as the user's
turn (now gated via `AUTONOMOUS_RESULT_ORIGINS`, preserving
channel-origin behavior); `emittedAssistantText` moving onto the Session
so every held-turn settle lane closes the #453 delivery stretch (with
the snapshot taken after the held-turn hand-off); the interrupt-trailer
pre-count gated on a running cycle so idle-cancels can't mask future
#825 detections; and `turnAwaitingSubagents` now reading `isSubagent` as
defense in depth for the shells-never-defer contract. Plus
`isHeldOpen()`/`disarmForceCancel()` deduplicating the hold predicate
and backstop disarm.

(An earlier follow-up — folding the attribution map and the
live-subagent set into one task registry — is now done in this PR:
`liveBackgroundTasks` keyed by task id, so the
`background_tasks_changed` reconciliation heals both concerns and future
terminal paths can't prune one but not the other.)

Fixes #866. Addresses the settlement half of #864 / #865.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 17:16:11 +02:00
dependabot[bot]
e67dacdcac feat(deps-dev): bump nanoid from 3.3.15 to 3.3.16 (#875)
Bumps [nanoid](https://github.com/ai/nanoid) from 3.3.15 to 3.3.16.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/ai/nanoid/releases">nanoid's
releases</a>.</em></p>
<blockquote>
<h2>3.3.16</h2>
<ul>
<li>Fixed infinite loop on negative size (by <a
href="https://github.com/greymoth-jp"><code>@​greymoth-jp</code></a>).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/ai/nanoid/blob/main/CHANGELOG.md">nanoid's
changelog</a>.</em></p>
<blockquote>
<h2>3.3.16</h2>
<ul>
<li>Fixed infinite loop on negative size (by <a
href="https://github.com/greymoth-jp"><code>@​greymoth-jp</code></a>).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="9760e11275"><code>9760e11</code></a>
Release 3.3.16 version</li>
<li><a
href="e835c9b71e"><code>e835c9b</code></a>
fix(non-secure): clamp negative size to prevent infinite loop (<a
href="https://redirect.github.com/ai/nanoid/issues/601">#601</a>)</li>
<li><a
href="96dd086eb2"><code>96dd086</code></a>
Update CI action</li>
<li><a
href="ba0bc3b6b9"><code>ba0bc3b</code></a>
Do not create latest release for v3</li>
<li>See full diff in <a
href="https://github.com/ai/nanoid/compare/3.3.15...3.3.16">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=nanoid&package-manager=npm_and_yarn&previous-version=3.3.15&new-version=3.3.16)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-13 16:46:19 +02:00
Josh Kappler
61ae8609d4 fix: forward result text when the turn emitted no assistant message (#858)
Fixes #453.

On a cache-replayed turn the CLI can answer on the `result` alone:
output_tokens is 0, no `stream_event` deltas, and no consolidated
`assistant` message. The result handler never forwarded that text, so
the client rendered an empty turn.

The check suggested in the issue (output_tokens === 0 and a non-empty
result) isn't enough by itself. I tried it: it emits the answer twice
when the consolidated `assistant` message already delivered it,
including the case where the echo lands mid-message and the consolidated
message dedupes to nothing, and it forwards a background
task-notification followup's output into the user's turn.

So this tracks whether any top-level assistant text already reached the
client since the last user-turn result, and forwards `message.result`
only when none did and the turn isn't a task-notification followup. The
direct emitters in the `status` handler (compaction notices, local
command output) set the flag too, since they bypass the forwarding
loops. `compact_boundary` doesn't, because it only sends a usage update.

Tests: six added to the "assembled assistant text fallback" block.
Reverting the source change fails "forwards the result text when nothing
else carried it"; the other five pin the guards, and they catch the
naive version, which double-emits and leaks task-notification output.
Full suite: 501 passed, 2 failed, and both failures reproduce on a clean
main (Windows path separator assertions in the tool-conversion tests).
eslint, tsc --noEmit, and prettier --check are clean.

---------

Co-authored-by: Josh Kappler <boffytv@gmail.com>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 12:21:23 +00:00
nikita-ashihmin
2c19974d5b fix: Refine tool calls from streamed input (#867)
## Why

Tool-use `content_block_start` events arrive before their JSON input.
This leaves pending calls with generic labels such as `Read File` or
`Terminal`; long-running and concurrent calls can remain
indistinguishable in clients while they execute.

Anthropic specifies `input_json_delta.partial_json` as arbitrary string
fragments, so delta boundaries cannot be assumed to align with fields or
JSON values.

## What changed

- Buffer `input_json_delta` fragments per parent message and
content-block index.
- Recover completed top-level fields while the overall JSON is still
incomplete.
- Refine the existing ACP tool call immediately when distinguishing
fields such as `file_path`, `command`, `query`, `pattern`, `url`, or
`description` become available.
- Continue refining as later fields arrive, while keeping the early
pending lifecycle and cleaning incomplete buffers at block/message
boundaries.
- Avoid publishing ambiguous numeric suffixes (`10` may still become
`100` or `10.5`) until a delimiter arrives.

## Test coverage

Parameterized coverage now includes Agent/Task, Bash, Read/Write/Edit,
Glob/Grep, WebFetch/WebSearch, ReportFindings, ExitPlanMode,
AskUserQuestion, Other, custom MCP calls, TodoWrite plan updates, and
intentionally suppressed TaskCreate/Update/List/Get calls. Edge cases
cover arbitrary field splits, unfinished strings, escaped quotes and
commas, numbers, booleans, null, arrays, nested objects, and all three
tool block types (`tool_use`, `server_tool_use`, `mcp_tool_use`).

## Validation

- `npm run build`
- `npm run test:run` (535 passed, 20 skipped)
- `npm run check`

---------

Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 12:17:37 +00:00
Ben Brandt
c7dff3cf7e fix: Skip synthetic login messages on replay (#869)
Closes #863
2026-07-13 11:42:19 +00:00
Ben Brandt
3ba2d367a2 fix: Seed context window from SDK usage report (#868)
Closes #596
2026-07-13 11:35:03 +00:00
dependabot[bot]
f4e750decf feat(deps): bump the minor group with 11 updates (#862)
Bumps the minor group with 11 updates:

| Package | From | To |
| --- | --- | --- |
|
[@anthropic-ai/claude-agent-sdk](https://github.com/anthropics/claude-agent-sdk-typescript)
| `0.3.205` | `0.3.206` |
| [prettier](https://github.com/prettier/prettier) | `3.9.4` | `3.9.5` |
| @anthropic-ai/claude-agent-sdk-darwin-arm64 | `0.3.205` | `0.3.206` |
| @anthropic-ai/claude-agent-sdk-darwin-x64 | `0.3.205` | `0.3.206` |
| @anthropic-ai/claude-agent-sdk-linux-arm64 | `0.3.205` | `0.3.206` |
| @anthropic-ai/claude-agent-sdk-linux-arm64-musl | `0.3.205` |
`0.3.206` |
| @anthropic-ai/claude-agent-sdk-linux-x64 | `0.3.205` | `0.3.206` |
| @anthropic-ai/claude-agent-sdk-linux-x64-musl | `0.3.205` | `0.3.206`
|
| @anthropic-ai/claude-agent-sdk-win32-arm64 | `0.3.205` | `0.3.206` |
| @anthropic-ai/claude-agent-sdk-win32-x64 | `0.3.205` | `0.3.206` |
| [hono](https://github.com/honojs/hono) | `4.12.28` | `4.12.29` |

Updates `@anthropic-ai/claude-agent-sdk` from 0.3.205 to 0.3.206
<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.206</h2>
<h2>What's changed</h2>
<ul>
<li>Added <code>command_lifecycle</code> frames to stream-json and SDK
sessions, reporting each uuid-stamped message's terminal state
(<code>queued</code>/<code>started</code>/<code>completed</code>/<code>cancelled</code>/<code>discarded</code>);
zero-API results no longer report stale
<code>duration_api_ms</code></li>
</ul>
<h2>Update</h2>
<pre lang="sh"><code>npm install @anthropic-ai/claude-agent-sdk@0.3.206
# or
yarn add @anthropic-ai/claude-agent-sdk@0.3.206
# or
pnpm add @anthropic-ai/claude-agent-sdk@0.3.206
# or
bun add @anthropic-ai/claude-agent-sdk@0.3.206
</code></pre>
</blockquote>
</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.206</h2>
<ul>
<li>Added <code>command_lifecycle</code> frames to stream-json and SDK
sessions, reporting each uuid-stamped message's terminal state
(<code>queued</code>/<code>started</code>/<code>completed</code>/<code>cancelled</code>/<code>discarded</code>);
zero-API results no longer report stale
<code>duration_api_ms</code></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="9fc7ea5ff6"><code>9fc7ea5</code></a>
chore: Update CHANGELOG.md</li>
<li>See full diff in <a
href="https://github.com/anthropics/claude-agent-sdk-typescript/compare/v0.3.205...v0.3.206">compare
view</a></li>
</ul>
</details>
<br />

Updates `prettier` from 3.9.4 to 3.9.5
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/prettier/prettier/releases">prettier's
releases</a>.</em></p>
<blockquote>
<h2>3.9.5</h2>
<p>🔗 <a
href="https://github.com/prettier/prettier/blob/3.9.5/CHANGELOG.md#395">Changelog</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/prettier/prettier/blob/main/CHANGELOG.md">prettier's
changelog</a>.</em></p>
<blockquote>
<h1>3.9.5</h1>
<p><a
href="https://github.com/prettier/prettier/compare/3.9.4...3.9.5">diff</a></p>
<h4>Markdown: Cap ordered list mark at 999,999,999 (<a
href="https://redirect.github.com/prettier/prettier/pull/19351">#19351</a>
by <a href="https://github.com/tats-u"><code>@​tats-u</code></a>)</h4>
<p>CommonMark parsers only support ordered list item numbers up to
999,999,999.</p>
<p>With this change, Prettier now caps the ordered list item number at
999,999,999 to ensure that the output is correctly parsed as an ordered
list by CommonMark parsers. Numbers larger than 999,999,999 are not
parsed as list item numbers and are left unchanged in the output:</p>
<!-- raw HTML omitted -->
<pre lang="markdown"><code>&lt;!-- Input --&gt;
999999998. text
999999998. text
999999998. text
999999998. text
<p>1234567890123456789012) text</p>
<p>&lt;!-- Prettier 3.9.4 --&gt;
999999998. text
999999999. text
1000000000. text
1000000001. text</p>
<p>1234567890123456789012) text</p>
<p>&lt;!-- Prettier 3.9.5 --&gt;
999999998. text
999999999. text
999999999. text
999999999. text</p>
<p>1234567890123456789012) text
</code></pre></p>
<h4>Markdown: Avoid corrupting empty link with title (<a
href="https://redirect.github.com/prettier/prettier/pull/19487">#19487</a>
by <a href="https://github.com/andersk"><code>@​andersk</code></a>)</h4>
<p>Do not remove <code>&lt;&gt;</code> from an inline link or image with
an empty URL and a title, as this removal would change its
interpretation.</p>
<!-- raw HTML omitted -->
<pre lang="md"><code>&lt;!-- Input --&gt;
[link](https://github.com/prettier/prettier/blob/main/&lt;&gt;
&quot;title&quot;)
<p>&lt;!-- Prettier 3.9.4 --&gt;
[link](<a
href="https://github.com/prettier/prettier/blob/main/">https://github.com/prettier/prettier/blob/main/</a>
&quot;title&quot;)</p>
<p>&lt;!-- Prettier 3.9.5 --&gt;
&lt;/tr&gt;&lt;/table&gt;
</code></pre></p>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="b6c7d18068"><code>b6c7d18</code></a>
Release 3.9.5</li>
<li><a
href="cd54ccc728"><code>cd54ccc</code></a>
Avoid corrupting empty Markdown link with title (<a
href="https://redirect.github.com/prettier/prettier/issues/19487">#19487</a>)</li>
<li><a
href="2bb67ce8cd"><code>2bb67ce</code></a>
Preserving comments' <code>placement</code> property (<a
href="https://redirect.github.com/prettier/prettier/issues/19567">#19567</a>)</li>
<li><a
href="91bcac88ec"><code>91bcac8</code></a>
Add more tests for comment-only object type (<a
href="https://redirect.github.com/prettier/prettier/issues/19587">#19587</a>)</li>
<li><a
href="cbee7377a1"><code>cbee737</code></a>
Remove space in empty object type (<a
href="https://redirect.github.com/prettier/prettier/issues/19583">#19583</a>)</li>
<li><a
href="6394c73209"><code>6394c73</code></a>
Align empty module declaration with TS (<a
href="https://redirect.github.com/prettier/prettier/issues/19568">#19568</a>)</li>
<li><a
href="a4e6f7a026"><code>a4e6f7a</code></a>
Prevent the addition of space in <code>type()</code> with <code>+</code>
(<a
href="https://redirect.github.com/prettier/prettier/issues/19516">#19516</a>)</li>
<li><a
href="3d063b57b1"><code>3d063b5</code></a>
Ignore dangling comments when checking type parameter comments (<a
href="https://redirect.github.com/prettier/prettier/issues/19572">#19572</a>)</li>
<li><a
href="908503e9ee"><code>908503e</code></a>
Handle dangling comments in <code>SwitchStatement</code> (<a
href="https://redirect.github.com/prettier/prettier/issues/19581">#19581</a>)</li>
<li><a
href="943a475c35"><code>943a475</code></a>
Angular: Support expression for exhaustive typechecking (<a
href="https://redirect.github.com/prettier/prettier/issues/19571">#19571</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/prettier/prettier/compare/3.9.4...3.9.5">compare
view</a></li>
</ul>
</details>
<br />

Updates `@anthropic-ai/claude-agent-sdk-darwin-arm64` from 0.3.205 to
0.3.206

Updates `@anthropic-ai/claude-agent-sdk-darwin-x64` from 0.3.205 to
0.3.206

Updates `@anthropic-ai/claude-agent-sdk-linux-arm64` from 0.3.205 to
0.3.206

Updates `@anthropic-ai/claude-agent-sdk-linux-arm64-musl` from 0.3.205
to 0.3.206

Updates `@anthropic-ai/claude-agent-sdk-linux-x64` from 0.3.205 to
0.3.206

Updates `@anthropic-ai/claude-agent-sdk-linux-x64-musl` from 0.3.205 to
0.3.206

Updates `@anthropic-ai/claude-agent-sdk-win32-arm64` from 0.3.205 to
0.3.206

Updates `@anthropic-ai/claude-agent-sdk-win32-x64` from 0.3.205 to
0.3.206

Updates `hono` from 4.12.28 to 4.12.29
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/honojs/hono/releases">hono's
releases</a>.</em></p>
<blockquote>
<h2>v4.12.29</h2>
<h2>What's Changed</h2>
<ul>
<li>fix(client): merge function headers with per-request headers by <a
href="https://github.com/yusukebe"><code>@​yusukebe</code></a> in <a
href="https://redirect.github.com/honojs/hono/pull/5092">honojs/hono#5092</a></li>
<li>chore: fix no-op tsc in test script by <a
href="https://github.com/yusukebe"><code>@​yusukebe</code></a> in <a
href="https://redirect.github.com/honojs/hono/pull/5093">honojs/hono#5093</a></li>
<li>fix(lambda-edge): resolve the handler with the value passed to the
callback by <a
href="https://github.com/yusukebe"><code>@​yusukebe</code></a> in <a
href="https://redirect.github.com/honojs/hono/pull/5094">honojs/hono#5094</a></li>
<li>docs(language): add JSDoc <a
href="https://github.com/example"><code>@​example</code></a> to
languageDetector by <a
href="https://github.com/codebybilal18"><code>@​codebybilal18</code></a>
in <a
href="https://redirect.github.com/honojs/hono/pull/5081">honojs/hono#5081</a></li>
<li>test(workerd): add <code>compatibilityDate</code> by <a
href="https://github.com/yusukebe"><code>@​yusukebe</code></a> in <a
href="https://redirect.github.com/honojs/hono/pull/5100">honojs/hono#5100</a></li>
<li>fix(lambda-edge): base64 encode content-encoded response bodies by
<a href="https://github.com/yusukebe"><code>@​yusukebe</code></a> in <a
href="https://redirect.github.com/honojs/hono/pull/5099">honojs/hono#5099</a></li>
<li>fix(aws-lambda): treat any non-identity content-encoding as binary
by <a href="https://github.com/yusukebe"><code>@​yusukebe</code></a> in
<a
href="https://redirect.github.com/honojs/hono/pull/5101">honojs/hono#5101</a></li>
<li>fix(types): strip extra properties from array types in JSONParsed by
<a
href="https://github.com/Arman-Luthra"><code>@​Arman-Luthra</code></a>
in <a
href="https://redirect.github.com/honojs/hono/pull/5103">honojs/hono#5103</a></li>
<li>fix(trie-router): match empty wildcard remainder after regexp param
by <a href="https://github.com/usualoma"><code>@​usualoma</code></a> in
<a
href="https://redirect.github.com/honojs/hono/pull/5102">honojs/hono#5102</a></li>
<li>fix(etag): treat If-None-Match: <code>*</code> as a match by <a
href="https://github.com/yusukebe"><code>@​yusukebe</code></a> in <a
href="https://redirect.github.com/honojs/hono/pull/5084">honojs/hono#5084</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/codebybilal18"><code>@​codebybilal18</code></a>
made their first contribution in <a
href="https://redirect.github.com/honojs/hono/pull/5081">honojs/hono#5081</a></li>
<li><a
href="https://github.com/Arman-Luthra"><code>@​Arman-Luthra</code></a>
made their first contribution in <a
href="https://redirect.github.com/honojs/hono/pull/5103">honojs/hono#5103</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/honojs/hono/compare/v4.12.28...v4.12.29">https://github.com/honojs/hono/compare/v4.12.28...v4.12.29</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="cda1af2002"><code>cda1af2</code></a>
4.12.29</li>
<li><a
href="2126289c15"><code>2126289</code></a>
fix(etag): treat If-None-Match: <code>*</code> as a match (<a
href="https://redirect.github.com/honojs/hono/issues/5084">#5084</a>)</li>
<li><a
href="0fc7ffc949"><code>0fc7ffc</code></a>
fix(trie-router): match empty wildcard remainder after regexp param (<a
href="https://redirect.github.com/honojs/hono/issues/5102">#5102</a>)</li>
<li><a
href="ab590e4ec8"><code>ab590e4</code></a>
fix(types): strip extra properties from array types in JSONParsed (<a
href="https://redirect.github.com/honojs/hono/issues/5103">#5103</a>)</li>
<li><a
href="b93a2547a3"><code>b93a254</code></a>
fix(aws-lambda): treat any non-identity content-encoding as binary (<a
href="https://redirect.github.com/honojs/hono/issues/5101">#5101</a>)</li>
<li><a
href="6217f4b74c"><code>6217f4b</code></a>
fix(lambda-edge): base64 encode content-encoded response bodies (<a
href="https://redirect.github.com/honojs/hono/issues/5099">#5099</a>)</li>
<li><a
href="ba89e973c7"><code>ba89e97</code></a>
test(workerd): add <code>compatibilityDate</code> (<a
href="https://redirect.github.com/honojs/hono/issues/5100">#5100</a>)</li>
<li><a
href="46557ef820"><code>46557ef</code></a>
docs(language): add JSDoc <a
href="https://github.com/example"><code>@​example</code></a> to
languageDetector (<a
href="https://redirect.github.com/honojs/hono/issues/5081">#5081</a>)</li>
<li><a
href="24547a6ed5"><code>24547a6</code></a>
fix(lambda-edge): resolve the handler with the value passed to the
callback (...</li>
<li><a
href="9c6ccd76f1"><code>9c6ccd7</code></a>
chore: fix no-op tsc in test script (<a
href="https://redirect.github.com/honojs/hono/issues/5093">#5093</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/honojs/hono/compare/v4.12.28...v4.12.29">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

---------

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-07-13 13:00:40 +02:00
Ben Brandt
9cd48c597a fix: Add subagent parent tool use attribution (#859)
Hopefully help address #851
2026-07-10 09:10:55 +00:00
acp-release-bot[bot]
3500ef736f chore(main): release 0.58.1 (#856)
🤖 I have created a release *beep* *boop*
---


##
[0.58.1](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.58.0...v0.58.1)
(2026-07-09)


### Bug Fixes

* Use valid npm version for publish
([#855](https://github.com/agentclientprotocol/claude-agent-acp/issues/855))
([8b366d8](8b366d8e9a))

---
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-07-09 14:19:30 +02:00
Ben Brandt
8b366d8e9a fix: Use valid npm version for publish (#855) 2026-07-09 11:57:22 +00:00
23 changed files with 9776 additions and 784 deletions

View File

@@ -21,10 +21,10 @@ jobs:
timeout-minutes: 5
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
- name: Use Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020
with:
node-version: "lts/*"
cache: "npm"

View File

@@ -44,14 +44,37 @@ jobs:
contents: read
id-token: write
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
# Setup .npmrc file to publish to npm
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020
with:
node-version: "lts/*"
registry-url: "https://registry.npmjs.org"
- name: Update npm
run: npm install -g npm@latest
- run: npm ci
- run: npm run build
- run: npm publish
trigger-registry-update:
needs: publish-npm
runs-on: ubuntu-latest
environment: release
permissions: {}
steps:
- name: Generate token scoped to the registry repo
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1
id: registry-token
with:
app-id: ${{ secrets.REGISTRY_UPDATER_APP_ID }}
private-key: ${{ secrets.REGISTRY_UPDATER_APP_PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
repositories: registry
- name: Dispatch registry version update
env:
GH_TOKEN: ${{ steps.registry-token.outputs.token }}
run: |
gh workflow run update-versions.yml \
--repo ${{ github.repository_owner }}/registry \
--ref main \
-f apply=true \
-f agents=claude-acp

1
.gitignore vendored
View File

@@ -3,3 +3,4 @@ dist
.DS_Store
.claude/*.local.*
CLAUDE.local.md
.idea/

View File

@@ -1,3 +1,3 @@
{
".": "0.58.0"
".": "0.64.0"
}

View File

@@ -1,5 +1,91 @@
# Changelog
## [0.64.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.63.0...v0.64.0) (2026-07-30)
### Features
* **deps:** Bump actions/checkout from 7.0.0 to 7.0.1 ([#925](https://github.com/agentclientprotocol/claude-agent-acp/issues/925)) ([8e099e8](https://github.com/agentclientprotocol/claude-agent-acp/commit/8e099e844254c3e91508c79a02e3e7dc2239fcbb))
* **deps:** Bump the minor group with 7 updates ([#928](https://github.com/agentclientprotocol/claude-agent-acp/issues/928)) ([3f60921](https://github.com/agentclientprotocol/claude-agent-acp/commit/3f609219592e63b947539f79c696b3cedb421060))
### Bug Fixes
* **steering:** add opt-in host-owned fallback ([#919](https://github.com/agentclientprotocol/claude-agent-acp/issues/919)) ([43af4ec](https://github.com/agentclientprotocol/claude-agent-acp/commit/43af4ec29ea5396c2614813af05967bfb0b1bac8)), closes [#903](https://github.com/agentclientprotocol/claude-agent-acp/issues/903)
## [0.63.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.62.0...v0.63.0) (2026-07-27)
### Features
* Update to claude agent sdk v0.3.220 ([#921](https://github.com/agentclientprotocol/claude-agent-acp/issues/921)) ([4c7b897](https://github.com/agentclientprotocol/claude-agent-acp/commit/4c7b89718306229254879e5045a183233b5ed073))
### Bug Fixes
* Only resolve a denied tool call the client was told about ([#923](https://github.com/agentclientprotocol/claude-agent-acp/issues/923)) ([8f67b6a](https://github.com/agentclientprotocol/claude-agent-acp/commit/8f67b6a92bec24ae43b3dfbd087fe35df0531857)), closes [#918](https://github.com/agentclientprotocol/claude-agent-acp/issues/918)
* Report tool_progress heartbeats against the tool call they describe ([#916](https://github.com/agentclientprotocol/claude-agent-acp/issues/916)) ([5559ba8](https://github.com/agentclientprotocol/claude-agent-acp/commit/5559ba890ca614cdaa189500aba65d81cc4cd51a))
* **tools:** key Bash terminal metas off the announced tool_use id ([#917](https://github.com/agentclientprotocol/claude-agent-acp/issues/917)) ([d060414](https://github.com/agentclientprotocol/claude-agent-acp/commit/d0604140f907adbf9747f26a070690926e1de82d))
## [0.62.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.61.0...v0.62.0) (2026-07-24)
### Features
* **deps:** Bump @hono/node-server from 1.19.14 to 1.19.15 ([#908](https://github.com/agentclientprotocol/claude-agent-acp/issues/908)) ([5193604](https://github.com/agentclientprotocol/claude-agent-acp/commit/51936049e175274de8e0fd90bc1be2af988c3ca6))
* **deps:** Bump media-typer from 1.1.0 to 1.1.1 ([#909](https://github.com/agentclientprotocol/claude-agent-acp/issues/909)) ([5d35001](https://github.com/agentclientprotocol/claude-agent-acp/commit/5d35001563ffd702a29c3bac3b7ca134e40c77f8))
* **deps:** Bump the minor group with 2 updates ([#900](https://github.com/agentclientprotocol/claude-agent-acp/issues/900)) ([809d41c](https://github.com/agentclientprotocol/claude-agent-acp/commit/809d41c6b7c9e7ba3cb5b206d00793a70edba64a))
* **deps:** Bump the minor group with 2 updates ([#907](https://github.com/agentclientprotocol/claude-agent-acp/issues/907)) ([14d0627](https://github.com/agentclientprotocol/claude-agent-acp/commit/14d06273c01ad4ae944912e36700f6b5599c4c4d))
* Update to claude-agent-sdk 0.3.218 ([#904](https://github.com/agentclientprotocol/claude-agent-acp/issues/904)) ([8cbaf97](https://github.com/agentclientprotocol/claude-agent-acp/commit/8cbaf97254576089a3b5ee6ae222fb763003c01d))
## [0.61.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.60.0...v0.61.0) (2026-07-22)
### Features
* **deps:** Bump actions/setup-node from 6.4.0 to 7.0.0 ([#897](https://github.com/agentclientprotocol/claude-agent-acp/issues/897)) ([d9bd36d](https://github.com/agentclientprotocol/claude-agent-acp/commit/d9bd36d8b06764d63656d0387ad9430ec9fcb27d))
* **deps:** Update to @anthropic-ai/claude-agent-sdk 0.3.217 ([#899](https://github.com/agentclientprotocol/claude-agent-acp/issues/899)) ([edf3af0](https://github.com/agentclientprotocol/claude-agent-acp/commit/edf3af043b6d00e5caca2cc81a2285c477c8b2ab))
## [0.60.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.59.0...v0.60.0) (2026-07-20)
### Features
* **deps:** Update to claude-agent-sdk 0.3.215 ([#890](https://github.com/agentclientprotocol/claude-agent-acp/issues/890)) ([92548f0](https://github.com/agentclientprotocol/claude-agent-acp/commit/92548f043547b0ddac95921ece020e69f7c12c5f))
* implement configurable LLM providers ([#853](https://github.com/agentclientprotocol/claude-agent-acp/issues/853)) ([82cd692](https://github.com/agentclientprotocol/claude-agent-acp/commit/82cd692e500eedec182f817b18cebb005c4b98ce))
### Bug Fixes
* parse Agent/Task trailers without regex ([#879](https://github.com/agentclientprotocol/claude-agent-acp/issues/879)) ([06c3d7b](https://github.com/agentclientprotocol/claude-agent-acp/commit/06c3d7bdbd8cc9415c8cabac060a50e0951c758b))
* remove ~15s stall on session/new and model switch by seeding the context window synchronously ([#894](https://github.com/agentclientprotocol/claude-agent-acp/issues/894)) ([ff9b96d](https://github.com/agentclientprotocol/claude-agent-acp/commit/ff9b96d462831b1c3b96722ea20215ff6e529cb1))
* Silence missing PostToolUse callbacks ([#895](https://github.com/agentclientprotocol/claude-agent-acp/issues/895)) ([1887ada](https://github.com/agentclientprotocol/claude-agent-acp/commit/1887ada215b27bb1025d9b7696a46ae7a4ac0f7a)), closes [#889](https://github.com/agentclientprotocol/claude-agent-acp/issues/889)
## [0.59.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.58.1...v0.59.0) (2026-07-13)
### Features
* **deps-dev:** bump nanoid from 3.3.15 to 3.3.16 ([#875](https://github.com/agentclientprotocol/claude-agent-acp/issues/875)) ([e67dacd](https://github.com/agentclientprotocol/claude-agent-acp/commit/e67dacdcac629b00e5bdcda94b3de57459696b1e))
* **deps:** bump @anthropic-ai/claude-agent-sdk to 0.3.207 ([#874](https://github.com/agentclientprotocol/claude-agent-acp/issues/874)) ([c7f5b8f](https://github.com/agentclientprotocol/claude-agent-acp/commit/c7f5b8fe768507afa28cdffe5cc3c054c56306ad))
### Bug Fixes
* Add subagent parent tool use attribution ([#859](https://github.com/agentclientprotocol/claude-agent-acp/issues/859)) ([9cd48c5](https://github.com/agentclientprotocol/claude-agent-acp/commit/9cd48c597af68f630774ec00bc3adc85f6b0fd4b))
* forward result text when the turn emitted no assistant message ([#858](https://github.com/agentclientprotocol/claude-agent-acp/issues/858)) ([61ae860](https://github.com/agentclientprotocol/claude-agent-acp/commit/61ae8609d4343da6758c2376c6cf684c7fb0956a))
* hold a turn open while its background subagents are still live ([#870](https://github.com/agentclientprotocol/claude-agent-acp/issues/870)) ([7a70f82](https://github.com/agentclientprotocol/claude-agent-acp/commit/7a70f82739e085014cad878f08513cdef7b7fe16))
* Refine tool calls from streamed input ([#867](https://github.com/agentclientprotocol/claude-agent-acp/issues/867)) ([2c19974](https://github.com/agentclientprotocol/claude-agent-acp/commit/2c19974d5b80d3178b35e80a4bb0a1050588aab9))
* Seed context window from SDK usage report ([#868](https://github.com/agentclientprotocol/claude-agent-acp/issues/868)) ([3ba2d36](https://github.com/agentclientprotocol/claude-agent-acp/commit/3ba2d367a282fe3053a73804a416a435cb01ee08)), closes [#596](https://github.com/agentclientprotocol/claude-agent-acp/issues/596)
* Skip synthetic login messages on replay ([#869](https://github.com/agentclientprotocol/claude-agent-acp/issues/869)) ([c7dff3c](https://github.com/agentclientprotocol/claude-agent-acp/commit/c7dff3cf7eeb03cb993493efafa96344f18de81b)), closes [#863](https://github.com/agentclientprotocol/claude-agent-acp/issues/863)
## [0.58.1](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.58.0...v0.58.1) (2026-07-09)
### Bug Fixes
* Use valid npm version for publish ([#855](https://github.com/agentclientprotocol/claude-agent-acp/issues/855)) ([8b366d8](https://github.com/agentclientprotocol/claude-agent-acp/commit/8b366d8e9ad55e9237bf2d618cfc99b57fac570d))
## [0.58.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.57.0...v0.58.0) (2026-07-09)

View File

@@ -12,12 +12,24 @@ This tool implements an ACP agent by using the official [Claude Agent SDK](https
- Following
- Edit review
- TODO lists
- Nested subagent transcripts
- Interactive (and background) terminals
- Custom [Slash commands](https://docs.anthropic.com/en/docs/claude-code/slash-commands)
- Client MCP servers
Learn more about the [Agent Client Protocol](https://agentclientprotocol.com/).
### Nested subagent transcripts
ACP 1.2 has no standard subagent tool kind or nested-message relationship. Clients that can render
nested transcripts can opt in with `clientCapabilities._meta["subagent-transcript"] = true`.
The agent then forwards subagent text, thinking, and tool calls, relating nested updates to the
launching Agent/Task call through `_meta.claudeCode.parentToolUseId`. Agent/Task calls are marked
with `_meta.claudeCode.subagent = true`.
Clients that do not advertise the capability retain the legacy flattened behavior. In both modes,
the normal Agent/Task tool result is preserved as the protocol-compatible fallback.
## Contribution Policy
This project does not require a Contributor License Agreement (CLA). Instead, contributions are accepted under the following terms:

221
examples/steering.ts Normal file
View File

@@ -0,0 +1,221 @@
/**
* Example: mid-turn steering over ACP with the `_session/steering` extension.
*
* "Steering" lets a client deliver a follow-up message to a turn that is still
* running, instead of waiting for it to finish and sending a fresh
* `session/prompt`. This is what powers "the user typed something while the
* agent was still working" — the new message joins the in-flight turn so the
* agent can adapt immediately (it shines in multi-step / tool-using turns,
* where the message slots in between tool calls).
*
* The wire protocol has three moving parts:
*
* 1. The agent advertises support in its `initialize` response, at the
* top-level `_meta.steering` (a sibling of `agentCapabilities`).
* 2. The client calls the `_session/steering` request with `{ sessionId,
* prompt }` while a turn is running.
* 3. The agent replies with an `outcome`:
* - "injected" the message joined the running turn;
* - "startedNewTurn" the turn had already finished and the legacy
* detached fallback was used;
* - "promptRequired" the turn had already finished, but this request
* explicitly opted into Host-owned prompt delivery.
*
* This example launches the agent as a subprocess, starts a deliberately
* long-running prompt, and — as soon as the agent begins streaming — injects a
* steering message and prints the outcome. All agent output is streamed to
* stdout so you can watch the turn change course.
*
* Run (build the agent first so `dist/index.js` exists):
*
* npm run build
* node examples/steering.ts
*
* (Node < 22.18 needs `node --experimental-strip-types examples/steering.ts`.)
*
* Override the prompts with the PROMPT / STEER env vars. Requires the agent to
* be authenticated, since it talks to the real model.
*/
import { spawn } from "node:child_process";
import { Readable, Writable } from "node:stream";
import { fileURLToPath } from "node:url";
import path from "node:path";
import {
client as acpClient,
methods,
ndJsonStream,
type PromptRequest,
type PromptResponse,
} from "@agentclientprotocol/sdk";
/** The steering extension method, per the ACP steering wire protocol. */
const STEERING_METHOD = "_session/steering";
/** Params for a `_session/steering` request — the same shape as the relevant
* subset of a `session/prompt`. */
type SteeringRequest = {
sessionId: string;
prompt: Array<{ type: "text"; text: string }>;
_meta?: { steering?: { idleBehavior?: "promptRequired" } };
};
/** Result of a `_session/steering` request. `injected` means the message joined
* the running turn; `promptRequired` means the turn had already settled, so the
* message was NOT consumed and must be (re)sent through a normal
* `session/prompt`. Both are successes. */
type SteeringResponse =
| { outcome: "injected" }
| { outcome: "startedNewTurn" }
| { outcome: "promptRequired"; reason: "noRunningTurn" };
/** The existing steering capability advertised at the top-level `_meta.steering`
* of the `initialize` result. The idle behavior is selected per request. */
type SteeringCapability = {
supported?: boolean;
};
// The built agent entry. Run `npm run build` first so this exists.
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const AGENT_ENTRY = process.env.AGENT_ENTRY ?? path.join(repoRoot, "dist", "index.js");
const CWD = process.env.CWD ?? process.cwd();
// A deliberately long-running first prompt, and the follow-up injected while it
// is still streaming. Override either via env vars to experiment.
const PROMPT =
process.env.PROMPT ??
"Count slowly from 1 to 30, one number per line, with a short sentence of " +
"commentary after each. Do not stop early.";
const STEER =
process.env.STEER ?? "Actually stop counting and instead reply with exactly one line: STEERED-OK";
function log(msg: string) {
process.stderr.write(`\x1b[2m[client]\x1b[0m ${msg}\n`);
}
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
async function main() {
// An ACP client launches the agent as a subprocess and speaks JSON-RPC over
// its stdin/stdout. stderr is inherited so the agent's own logs stay visible.
const child = spawn(process.execPath, [AGENT_ENTRY], {
stdio: ["pipe", "pipe", "inherit"],
env: process.env,
});
child.on("error", (err) => {
log(`failed to spawn agent (${AGENT_ENTRY}): ${err}`);
});
try {
const stream = ndJsonStream(
Writable.toWeb(child.stdin!) as WritableStream<Uint8Array>,
Readable.toWeb(child.stdout!) as unknown as ReadableStream<Uint8Array>,
);
// Resolves the first time the agent streams assistant text — our signal that
// the turn is genuinely underway and therefore steerable.
let signalFirstOutput = () => {};
const firstOutput = new Promise<void>((resolve) => (signalFirstOutput = resolve));
const connection = acpClient({ name: "steering-example" })
.onNotification(methods.client.session.update, (ctx) => {
const update = ctx.params.update;
if (update.sessionUpdate === "agent_message_chunk" && update.content.type === "text") {
process.stdout.write(update.content.text);
signalFirstOutput();
}
})
// Auto-approve permission prompts so the turn is never blocked on us.
.onRequest(methods.client.session.requestPermission, (ctx) => {
const options = ctx.params.options;
const option = options.find((o) => o.kind === "allow_once") ?? options[0];
return { outcome: { outcome: "selected", optionId: option.optionId } };
})
// Minimal file-system stubs; the example prompts don't touch files.
.onRequest(methods.client.fs.readTextFile, () => ({ content: "" }))
.onRequest(methods.client.fs.writeTextFile, () => ({}))
.connect(stream);
try {
const agent = connection.agent;
// 1. Initialize and confirm the agent advertises steering. Per the wire
// protocol the capability lives at the TOP-LEVEL `_meta` of the initialize
// result — a sibling of `agentCapabilities`, not nested inside it.
const init = await agent.request(methods.agent.initialize, {
protocolVersion: 1,
clientCapabilities: { fs: { readTextFile: true, writeTextFile: true } },
});
const steering = (init._meta as { steering?: SteeringCapability } | null | undefined)
?.steering;
if (steering?.supported !== true) {
throw new Error("agent does not advertise steering support");
}
log("agent advertises steering support");
// 2. Open a session.
const { sessionId } = await agent.request(methods.agent.session.new, {
cwd: CWD,
mcpServers: [],
});
log(`session: ${sessionId}`);
// 3. Start a long turn, but DON'T await it yet — we need it in flight so we
// can steer it. Its output streams through the notification handler above.
log(`prompt: ${PROMPT}`);
process.stdout.write("\n----- agent output -----\n");
const turn = agent.request(methods.agent.session.prompt, {
sessionId,
prompt: [{ type: "text", text: PROMPT }],
});
// 4. Once the turn is producing output, inject the follow-up. Wait for the
// first streamed chunk (with a fallback) plus a beat, so the steer clearly
// lands mid-turn.
await Promise.race([firstOutput, delay(5000)]);
await delay(1000);
process.stdout.write("\n");
log(`steer: ${STEER}`);
const steerRequest: SteeringRequest = {
sessionId,
prompt: [{ type: "text", text: STEER }],
// Opt into the host-owned idle fallback. Without this request metadata,
// the Adapter preserves its legacy `startedNewTurn` behavior.
_meta: { steering: { idleBehavior: "promptRequired" } },
};
const result = await agent.request<SteeringResponse>(STEERING_METHOD, steerRequest);
log(`steer outcome: ${result.outcome}`);
if (result.outcome === "promptRequired") {
// The target turn already settled, so steering did not consume the message.
// Start a normal session/prompt on the same session to deliver it — that
// request owns the continuation's updates and terminal response.
log(`steer fallback: ${result.reason}; starting a normal session/prompt`);
const continuationRequest: PromptRequest = {
sessionId: steerRequest.sessionId,
prompt: steerRequest.prompt,
};
const continuation = await agent.request<PromptResponse, PromptRequest>(
methods.agent.session.prompt,
continuationRequest,
);
log(`continuation stopReason: ${continuation.stopReason}`);
}
// 5. Await the original turn. With outcome "injected" the steer already
// reshaped the output above; the promptRequired branch owns its own turn.
const response = await turn;
log(`original turn stopReason: ${response.stopReason}`);
process.stdout.write("\n----- end of agent output -----\n");
} finally {
connection.close();
}
} finally {
child.kill();
}
}
main().catch((err) => {
log(`fatal: ${err?.stack ?? err}`);
process.exitCode = 1;
});

547
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@
"publishConfig": {
"access": "public"
},
"version": "0.58.0",
"version": "0.64.0",
"description": "An ACP-compatible coding agent powered by the Claude Agent SDK (TypeScript)",
"main": "dist/lib.js",
"types": "dist/lib.d.ts",
@@ -63,21 +63,21 @@
"node": ">=22"
},
"dependencies": {
"@agentclientprotocol/sdk": "1.2.1",
"@anthropic-ai/claude-agent-sdk": "0.3.205",
"@agentclientprotocol/sdk": "1.3.0",
"@anthropic-ai/claude-agent-sdk": "0.3.220",
"zod": "^3.25.0 || ^4.0.0"
},
"devDependencies": {
"@anthropic-ai/sdk": "0.110.0",
"@anthropic-ai/sdk": "0.115.0",
"@eslint/js": "10.0.1",
"@tsconfig/node22": "22.0.5",
"@types/node": "26.1.1",
"@typescript-eslint/eslint-plugin": "8.63.0",
"@typescript-eslint/parser": "8.63.0",
"eslint": "10.6.0",
"@types/node": "26.1.2",
"@typescript-eslint/eslint-plugin": "8.65.0",
"@typescript-eslint/parser": "8.65.0",
"eslint": "10.8.0",
"eslint-config-prettier": "10.1.8",
"globals": "17.7.0",
"prettier": "3.9.4",
"globals": "17.8.0",
"prettier": "3.9.6",
"ts-node": "10.9.2",
"typescript": "6.0.3",
"vitest": "4.1.10"

File diff suppressed because it is too large Load Diff

View File

@@ -148,6 +148,14 @@ function questionCustomFieldKey(index: number): string {
*/
const OPTION_META_KEY = "_claude/askUserQuestionOption";
/**
* Shared `_meta` key for marking a per-question free-text field as the custom
* answer companion for a select question. This intentionally has no
* agent-specific namespace so ACP clients can recognize the same marker across
* Codex, Claude, and other AskUserQuestion bridges.
*/
const CUSTOM_ANSWER_META_KEY = "_askUserQuestionCustomAnswer";
/**
* Render the AskUserQuestion tool's questions as an ACP form elicitation.
*
@@ -204,6 +212,12 @@ export function askUserQuestionsToCreateRequest(
type: "string",
title: "Other",
description: "Type your own answer instead of choosing an option above (optional).",
_meta: {
[CUSTOM_ANSWER_META_KEY]: {
questionId: questionFieldKey(index),
isCustomAnswer: true,
},
},
};
});

View File

@@ -3,6 +3,7 @@ import * as fs from "node:fs";
import * as path from "node:path";
import * as os from "node:os";
import type { AcpClient, ClaudeAcpAgent as ClaudeAcpAgentType } from "../acp-agent.js";
import { makeMockQuery } from "./helpers.js";
const { querySpy } = vi.hoisted(() => ({
querySpy: vi.fn(),
@@ -34,7 +35,7 @@ describe("ClaudeAcpAgent settings", () => {
const setModelSpy = vi.fn();
querySpy.mockImplementation(({ options }: any) => {
capturedOptions = options;
return {
return makeMockQuery({
initializationResult: async () => ({
models: [
{
@@ -45,8 +46,7 @@ describe("ClaudeAcpAgent settings", () => {
],
}),
setModel: setModelSpy,
supportedCommands: async () => [],
} as any;
}) as any;
});
return { getCapturedOptions: () => capturedOptions, setModelSpy };
}
@@ -240,12 +240,11 @@ describe("ClaudeAcpAgent settings", () => {
const setPermissionModeSpy = vi.fn();
querySpy.mockImplementation(({ options }: any) => {
capturedOptions = options;
return {
return makeMockQuery({
initializationResult: async () => ({ models }),
setModel: setModelSpy,
setPermissionMode: setPermissionModeSpy,
supportedCommands: async () => [],
} as any;
}) as any;
});
return {
getCapturedOptions: () => capturedOptions,
@@ -396,11 +395,10 @@ describe("ClaudeAcpAgent settings", () => {
} {
const setModelSpy = vi.fn();
querySpy.mockImplementation(() => {
return {
return makeMockQuery({
initializationResult: async () => ({ models }),
setModel: setModelSpy,
supportedCommands: async () => [],
} as any;
}) as any;
});
return { setModelSpy };
}
@@ -875,7 +873,7 @@ describe("ClaudeAcpAgent settings", () => {
const setModelSpy = vi.fn();
querySpy.mockImplementation(({ options: _options }: any) => {
return {
return makeMockQuery({
initializationResult: async () => ({
models: [
{
@@ -891,8 +889,7 @@ describe("ClaudeAcpAgent settings", () => {
],
}),
setModel: setModelSpy,
supportedCommands: async () => [],
} as any;
}) as any;
});
const { ClaudeAcpAgent } = await import("../acp-agent.js");
@@ -923,7 +920,7 @@ describe("ClaudeAcpAgent settings", () => {
const setModelSpy = vi.fn();
querySpy.mockImplementation(() => {
return {
return makeMockQuery({
initializationResult: async () => ({
models: [
{ value: "default", displayName: "Default", description: "" },
@@ -932,8 +929,7 @@ describe("ClaudeAcpAgent settings", () => {
],
}),
setModel: setModelSpy,
supportedCommands: async () => [],
} as any;
}) as any;
});
try {
@@ -977,7 +973,7 @@ describe("ClaudeAcpAgent settings", () => {
const setModelSpy = vi.fn();
querySpy.mockImplementation(() => {
return {
return makeMockQuery({
initializationResult: async () => ({
models: [
{ value: "default", displayName: "Default", description: "" },
@@ -985,8 +981,7 @@ describe("ClaudeAcpAgent settings", () => {
],
}),
setModel: setModelSpy,
supportedCommands: async () => [],
} as any;
}) as any;
});
const { ClaudeAcpAgent } = await import("../acp-agent.js");

File diff suppressed because it is too large Load Diff

View File

@@ -7,30 +7,30 @@ import type { AcpClient, ClaudeAcpAgent as ClaudeAcpAgentType } from "../acp-age
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
let capturedOptions: Options | undefined;
vi.mock("@anthropic-ai/claude-agent-sdk", async () => ({
...(await vi.importActual<typeof import("@anthropic-ai/claude-agent-sdk")>(
vi.mock("@anthropic-ai/claude-agent-sdk", async () => {
const actual = await vi.importActual<typeof import("@anthropic-ai/claude-agent-sdk")>(
"@anthropic-ai/claude-agent-sdk",
)),
query: ({ options }: { options: Options }) => {
capturedOptions = options;
return {
initializationResult: async () => ({
models: [
{
value: "claude-sonnet-4-6",
displayName: "Claude Sonnet",
description: "Fast",
supportsAutoMode: true,
},
],
}),
setModel: async () => {},
setPermissionMode: async () => {},
supportedCommands: async () => [],
[Symbol.asyncIterator]: async function* () {},
};
},
}));
);
const { makeMockQuery } = await import("./helpers.js");
return {
...actual,
query: ({ options }: { options: Options }) => {
capturedOptions = options;
return makeMockQuery({
initializationResult: async () => ({
models: [
{
value: "claude-sonnet-4-6",
displayName: "Claude Sonnet",
description: "Fast",
supportsAutoMode: true,
},
],
}),
});
},
};
});
vi.mock("../tools.js", async () => ({
...(await vi.importActual<typeof import("../tools.js")>("../tools.js")),
registerHookCallback: vi.fn(),

View File

@@ -1,18 +1,8 @@
import { describe, expect, it, Mock, vi, afterEach, beforeEach } from "vitest";
import { AcpClient, ClaudeAcpAgent } from "../acp-agent.js";
import { makeMockQuery } from "./helpers.js";
const mockQuery = vi.hoisted(() =>
vi.fn(() => ({
initializationResult: vi.fn().mockResolvedValue({
models: [
{ value: "id", displayName: "name", description: "description", supportsAutoMode: true },
],
}),
setModel: vi.fn(),
setPermissionMode: vi.fn(),
supportedCommands: vi.fn().mockResolvedValue([]),
})),
);
const mockQuery = vi.hoisted(() => vi.fn());
vi.mock("@anthropic-ai/claude-agent-sdk", async () => ({
...(await vi.importActual<typeof import("@anthropic-ai/claude-agent-sdk")>(
@@ -24,6 +14,22 @@ vi.mock("@anthropic-ai/claude-agent-sdk", async () => ({
describe("authorization", () => {
beforeEach(() => {
vi.useFakeTimers();
// Set here (not in vi.fn(impl) at hoist time) so the helper import is
// available; afterEach's resetAllMocks clears it, beforeEach re-sets it.
mockQuery.mockImplementation(() =>
makeMockQuery({
initializationResult: async () => ({
models: [
{
value: "id",
displayName: "name",
description: "description",
supportsAutoMode: true,
},
],
}),
}),
);
});
afterEach(() => {

View File

@@ -7,15 +7,17 @@ import * as os from "node:os";
import * as path from "node:path";
let capturedOptions: Options | undefined;
let contextUsageResult: (() => Promise<{ rawMaxTokens: number; model?: string }>) | undefined;
vi.mock("@anthropic-ai/claude-agent-sdk", async () => {
const actual = await vi.importActual<typeof import("@anthropic-ai/claude-agent-sdk")>(
"@anthropic-ai/claude-agent-sdk",
);
const { makeMockQuery, DEFAULT_CONTEXT_USAGE } = await import("./helpers.js");
return {
...actual,
query: (args: { prompt: unknown; options: Options }) => {
capturedOptions = args.options;
return {
return makeMockQuery({
initializationResult: async () => ({
models: [
{
@@ -26,11 +28,9 @@ vi.mock("@anthropic-ai/claude-agent-sdk", async () => {
},
],
}),
setModel: async () => {},
setPermissionMode: async () => {},
supportedCommands: async () => [],
[Symbol.asyncIterator]: async function* () {},
};
getContextUsage: () =>
contextUsageResult ? contextUsageResult() : Promise.resolve(DEFAULT_CONTEXT_USAGE),
});
},
};
});
@@ -58,6 +58,7 @@ describe("createSession options merging", () => {
beforeEach(async () => {
capturedOptions = undefined;
contextUsageResult = undefined;
vi.resetModules();
const acpAgent = await import("../acp-agent.js");
@@ -262,6 +263,38 @@ describe("createSession options merging", () => {
expect(capturedOptions!.tools).toEqual([]);
});
describe("subagent transcript forwarding", () => {
it("keeps the legacy default when neither the client nor caller opts in", async () => {
await agent.newSession({ cwd: process.cwd(), mcpServers: [] });
expect(capturedOptions!.forwardSubagentText).toBe(false);
});
it("preserves the pre-existing caller-provided SDK option", async () => {
await agent.newSession({
cwd: process.cwd(),
mcpServers: [],
_meta: { claudeCode: { options: { forwardSubagentText: true } } },
});
expect(capturedOptions!.forwardSubagentText).toBe(true);
});
it("enables SDK forwarding when the ACP client advertises support", async () => {
await agent.initialize({
protocolVersion: 1,
clientCapabilities: { _meta: { "subagent-transcript": true } },
});
await agent.newSession({
cwd: process.cwd(),
mcpServers: [],
_meta: { claudeCode: { options: { forwardSubagentText: false } } },
});
expect(capturedOptions!.forwardSubagentText).toBe(true);
});
});
describe("systemPrompt via _meta", () => {
it("defaults to the claude_code preset when not provided", async () => {
await agent.newSession({ cwd: process.cwd(), mcpServers: [] });
@@ -514,8 +547,13 @@ describe("createSession options merging", () => {
it("ignores a non-numeric value", async () => {
process.env.MAX_THINKING_TOKENS = "lots";
// The bad value is deliberate — capture the agent's warning instead of
// letting it hit the console.
const errorSpy = vi.fn();
(agent as any).logger = { log: () => {}, error: errorSpy };
await agent.newSession({ cwd: process.cwd(), mcpServers: [] });
expect(capturedOptions!.thinking).toBeUndefined();
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining("MAX_THINKING_TOKENS"));
});
it("lets a user-provided thinking option override the env default", async () => {
@@ -614,6 +652,109 @@ describe("createSession options merging", () => {
});
});
describe("context window seeding", () => {
function sessionFor(sessionId: string) {
return (agent as unknown as { sessions: Record<string, any> }).sessions[sessionId];
}
it("does not call getContextUsage during session creation", async () => {
// getContextUsage stalls until the session's first prompt turn has run
// (it is not serviced pre-turn), so session/new must never call it —
// awaiting it inline is what regressed session/new latency in 0.59.0.
const ctxSpy = vi.fn(async () => ({ rawMaxTokens: 967000 }));
contextUsageResult = ctxSpy;
await agent.newSession({ cwd: process.cwd(), mcpServers: [] });
expect(ctxSpy).not.toHaveBeenCalled();
});
it("seeds contextWindowSize from text inference, falling back to the default when it misses", async () => {
// The mock model ("claude-sonnet-4-6" / "Claude Sonnet" / "Fast") carries
// no "1m" token anywhere, so inference misses and the window falls back to
// the default; the authoritative value arrives later via result.modelUsage.
contextUsageResult = async () => ({ rawMaxTokens: 967000 });
const response = await agent.newSession({ cwd: process.cwd(), mcpServers: [] });
expect(sessionFor(response.sessionId).contextWindowSize).toBe(200000);
expect(sessionFor(response.sessionId).contextWindowAuthoritative).toBe(false);
});
it("session/load seeds the window from the resumed session's getContextUsage report", async () => {
// Resumed sessions get getContextUsage serviced pre-turn (issue #845 uses
// it to restore the live model), and the same response carries the
// authoritative window (`rawMaxTokens`). After a process restart the
// module cache is empty and text inference misses natively-1M aliases, so
// discarding this in-hand value would replay the issue-#596 flicker on
// every reload — the flagship scenario. 888_000 can only come from the
// report: inference on the mock model yields null → 200_000 default.
contextUsageResult = async () => ({ rawMaxTokens: 888_000, model: "claude-sonnet-4-6" });
await (
agent as unknown as {
createSession: (params: object, opts: { resume?: string }) => Promise<unknown>;
}
).createSession({ cwd: process.cwd(), mcpServers: [] }, { resume: "resumed-window-probe" });
const session = sessionFor("resumed-window-probe");
expect(session.contextWindowSize).toBe(888_000);
expect(session.contextWindowAuthoritative).toBe(true);
});
it("scopes providerCacheKey by per-session env routing", async () => {
// The context-window cache key must distinguish backends exactly as the
// CLI will see them: a session routed to a proxy via _meta env shares a
// model id spelling with default-routed sessions but not a context lane,
// so it must land in its own cache bucket (and two default-routed
// sessions must share one).
const r1 = await agent.newSession({ cwd: process.cwd(), mcpServers: [] });
const r2 = await agent.newSession({
cwd: process.cwd(),
mcpServers: [],
_meta: {
claudeCode: {
options: { env: { ANTHROPIC_BASE_URL: "https://window-probe-proxy.example" } },
},
},
});
const r3 = await agent.newSession({ cwd: process.cwd(), mcpServers: [] });
expect(sessionFor(r2.sessionId).providerCacheKey).not.toBe(
sessionFor(r1.sessionId).providerCacheKey,
);
expect(sessionFor(r3.sessionId).providerCacheKey).toBe(
sessionFor(r1.sessionId).providerCacheKey,
);
});
it("scopes providerCacheKey by provider headers: same endpoint, different headers → different buckets", async () => {
// Two providers/set configs sharing apiType+baseUrl but differing in
// headers (e.g. an `anthropic-beta: context-1m-…` routing header) can
// serve different context lanes for the same model id, so they must not
// share a window-cache bucket.
await agent.unstable_setProvider({
providerId: "main",
apiType: "anthropic",
baseUrl: "https://gw.example",
headers: {},
});
const plain = await agent.newSession({ cwd: process.cwd(), mcpServers: [] });
await agent.unstable_setProvider({
providerId: "main",
apiType: "anthropic",
baseUrl: "https://gw.example",
headers: { "anthropic-beta": "context-1m-2025-08-07" },
});
const beta = await agent.newSession({ cwd: process.cwd(), mcpServers: [] });
expect(sessionFor(beta.sessionId).providerCacheKey).not.toBe(
sessionFor(plain.sessionId).providerCacheKey,
);
});
});
describe("refusal fallback dialog", () => {
it("declares the dialog kind and callback when the client supports form elicitation", async () => {
await agent.initialize({
@@ -711,6 +852,10 @@ describe("createSession options merging", () => {
it("cancels on a malformed payload without presenting anything", async () => {
const { onUserDialog, createElicitation } = await setupDialog();
// The malformed payload is deliberate — capture the agent's warning
// instead of letting it hit the console.
const errorSpy = vi.fn();
(agent as any).logger = { log: () => {}, error: errorSpy };
const result = await onUserDialog(
{ dialogKind: "refusal_fallback_prompt", payload: { fallbackModel: 42 } },
@@ -719,11 +864,16 @@ describe("createSession options merging", () => {
expect(result).toEqual({ behavior: "cancelled" });
expect(createElicitation).not.toHaveBeenCalled();
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining("unexpected shape"));
});
it("cancels when the elicitation request fails", async () => {
const { onUserDialog, createElicitation } = await setupDialog();
createElicitation.mockRejectedValue(new Error("client exploded"));
// The client failure is deliberate — capture the agent's warning
// instead of letting it hit the console.
const errorSpy = vi.fn();
(agent as any).logger = { log: () => {}, error: errorSpy };
const result = await onUserDialog(
{
@@ -734,6 +884,7 @@ describe("createSession options merging", () => {
);
expect(result).toEqual({ behavior: "cancelled" });
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining("client exploded"));
});
});
});

View File

@@ -264,10 +264,22 @@ describe("askUserQuestionsToCreateRequest", () => {
expect(schema.properties?.["question_0_custom"]).toMatchObject({
type: "string",
title: "Other",
_meta: {
_askUserQuestionCustomAnswer: {
questionId: "question_0",
isCustomAnswer: true,
},
},
});
expect(schema.properties?.["question_1_custom"]).toMatchObject({
type: "string",
title: "Other",
_meta: {
_askUserQuestionCustomAnswer: {
questionId: "question_1",
isCustomAnswer: true,
},
},
});
});

View File

@@ -1,11 +1,12 @@
import { describe, it, expect, vi } from "vitest";
import type { ClientCapabilities, SessionNotification } from "@agentclientprotocol/sdk";
import type { ModelInfo } from "@anthropic-ai/claude-agent-sdk";
import type { FastModeDisabledReason, ModelInfo } from "@anthropic-ai/claude-agent-sdk";
import {
buildConfigOptions,
clientSupportsBooleanConfigOptions,
createFastModeConfigOption,
fastModeStateEnabled,
normalizeFastModeDisabledReason,
resolveFastModeEnabled,
FAST_MODE_CONFIG_ID,
FAST_MODE_ON,
@@ -42,6 +43,28 @@ describe("createFastModeConfigOption", () => {
});
});
it("explains a blocking disabled reason while the toggle reads off", () => {
const option = createFastModeConfigOption(false, true, "free");
expect(option.description).toBe(
"Faster responses on supported models — not available on the free plan",
);
});
it("ignores routine and on-state reasons", () => {
// Every SDK session starts at `sdk_opt_in_required` — the toggle IS the
// opt-in, so it must not read as a blocker.
expect(createFastModeConfigOption(false, true, "sdk_opt_in_required").description).toBe(
"Faster responses on supported models",
);
expect(createFastModeConfigOption(false, true, "preference").description).toBe(
"Faster responses on supported models",
);
// A reason riding an `on` state isn't blocking anything right now.
expect(createFastModeConfigOption(true, true, "free").description).toBe(
"Faster responses on supported models",
);
});
it("falls back to an on/off select when the client did not opt in", () => {
const option = createFastModeConfigOption(false, false);
expect(option).toMatchObject({
@@ -104,6 +127,28 @@ describe("fastModeStateEnabled", () => {
});
});
describe("normalizeFastModeDisabledReason", () => {
it("retains only reasons we have an explanation for", () => {
expect(normalizeFastModeDisabledReason("free")).toBe("free");
expect(normalizeFastModeDisabledReason("extra_usage_disabled")).toBe("extra_usage_disabled");
expect(normalizeFastModeDisabledReason("model_not_allowed")).toBe("model_not_allowed");
expect(normalizeFastModeDisabledReason("not_first_party")).toBe("not_first_party");
expect(normalizeFastModeDisabledReason("disabled_by_env")).toBe("disabled_by_env");
expect(normalizeFastModeDisabledReason("network_error")).toBe("network_error");
});
it("drops routine states and unknown values", () => {
expect(normalizeFastModeDisabledReason("sdk_opt_in_required")).toBeUndefined();
expect(normalizeFastModeDisabledReason("preference")).toBeUndefined();
expect(normalizeFastModeDisabledReason("pending")).toBeUndefined();
expect(normalizeFastModeDisabledReason("unknown")).toBeUndefined();
expect(normalizeFastModeDisabledReason(undefined)).toBeUndefined();
expect(
normalizeFastModeDisabledReason("some_future_reason" as FastModeDisabledReason),
).toBeUndefined();
});
});
describe("buildConfigOptions Fast mode", () => {
it("omits the Fast mode option when the model does not support it", () => {
const options = buildConfigOptions(MODES, MODELS, MODEL_INFOS, undefined, [], "default", {
@@ -249,6 +294,7 @@ describe("syncFastModeState (SDK-driven state changes)", () => {
const session = {
query: {},
fastModeEnabled: opts.fastModeEnabled,
fastModeDisabledReason: undefined as FastModeDisabledReason | undefined,
configOptions: opts.withOption
? [createFastModeConfigOption(opts.fastModeEnabled, true)]
: [],
@@ -261,6 +307,7 @@ describe("syncFastModeState (SDK-driven state changes)", () => {
sessionId: string,
session: unknown,
state: string | undefined,
reason?: FastModeDisabledReason,
) => Promise<void>;
}
).syncFastModeState.bind(agent);
@@ -319,10 +366,65 @@ describe("syncFastModeState (SDK-driven state changes)", () => {
await sync(SESSION_ID, session, undefined);
await sync(SESSION_ID, session, "off");
// A routine reason normalizes away, so it's still "unchanged" — an
// `sdk_opt_in_required` on every turn's result must not churn the option.
await sync(SESSION_ID, session, "off", "sdk_opt_in_required");
expect(sessionUpdates).toHaveLength(0);
});
it("explains a blocking reason once when the SDK refuses a toggle the user turned on", async () => {
const { sync, session, sessionUpdates } = setup({ fastModeEnabled: true, withOption: true });
await sync(SESSION_ID, session, "off", "extra_usage_disabled");
expect(session.fastModeEnabled).toBe(false);
expect(session.fastModeDisabledReason).toBe("extra_usage_disabled");
expect(sessionUpdates).toHaveLength(2);
expect(sessionUpdates[0].update).toEqual({
sessionUpdate: "agent_message_chunk",
content: {
type: "text",
text: "**Fast mode turned off:** requires extra usage to be enabled for this account.",
},
});
expect(sessionUpdates[1].update).toMatchObject({ sessionUpdate: "config_option_update" });
expect(session.configOptions).toContainEqual(
createFastModeConfigOption(false, true, "extra_usage_disabled"),
);
// The same report again changes nothing the user can see: no repeat notice.
await sync(SESSION_ID, session, "off", "extra_usage_disabled");
expect(sessionUpdates).toHaveLength(2);
});
it("updates the description when only the reason changes", async () => {
const { sync, session, sessionUpdates } = setup({ fastModeEnabled: false, withOption: true });
await sync(SESSION_ID, session, "off", "not_first_party");
expect(session.fastModeEnabled).toBe(false);
// The toggle was already off, so the flip-time notice doesn't fire — only
// the description carries the explanation.
expect(sessionUpdates).toHaveLength(1);
expect(sessionUpdates[0].update).toMatchObject({ sessionUpdate: "config_option_update" });
expect(session.configOptions).toContainEqual(
createFastModeConfigOption(false, true, "not_first_party"),
);
});
it("drops a retained reason once fast mode comes back on", async () => {
const { sync, session } = setup({ fastModeEnabled: false, withOption: true });
await sync(SESSION_ID, session, "off", "network_error");
expect(session.fastModeDisabledReason).toBe("network_error");
await sync(SESSION_ID, session, "on");
expect(session.fastModeEnabled).toBe(true);
expect(session.fastModeDisabledReason).toBeUndefined();
expect(session.configOptions).toContainEqual(createFastModeConfigOption(true, true));
});
it("preserves the retained setting (no clobber) when the model has no Fast mode option", async () => {
// Model without fast support: the SDK reports a capability-driven state, not
// the user's intent. We must leave session.fastModeEnabled untouched so it's

34
src/tests/helpers.ts Normal file
View File

@@ -0,0 +1,34 @@
/**
* Shared test doubles. Deliberately vitest-free so `vi.mock` async factories
* can `await import("./helpers.js")` without ordering hazards; tests supply
* their own vi.fn spies via `overrides`.
*/
/** The context-usage report the base mock query returns. `rawMaxTokens`
* matches the agent's DEFAULT_CONTEXT_WINDOW so window-related assertions
* don't shift in tests that don't care about context usage. */
export const DEFAULT_CONTEXT_USAGE = { totalTokens: 0, rawMaxTokens: 200000 };
/**
* Base stub for the SDK `query()` return object, covering the surface
* ClaudeAcpAgent touches unconditionally at session creation. Tests pass
* `overrides` for the parts they assert on (spies, custom models, rejecting
* getContextUsage, …).
*
* When the agent starts calling a new SDK method on every session, add it
* here once — the getContextUsage adoption required hand-editing ~10 inline
* mocks across five files, and any missed copy didn't fail: it silently
* rerouted that test through the error-fallback branch and re-polluted test
* output.
*/
export function makeMockQuery(overrides: Record<string, unknown> = {}) {
return {
initializationResult: async () => ({ models: [] }),
setModel: async () => {},
setPermissionMode: async () => {},
supportedCommands: async () => [],
getContextUsage: async () => DEFAULT_CONTEXT_USAGE,
[Symbol.asyncIterator]: async function* () {},
...overrides,
};
}

303
src/tests/providers.test.ts Normal file
View File

@@ -0,0 +1,303 @@
import { describe, expect, it, Mock, vi, afterEach, beforeEach } from "vitest";
import { AcpClient, ClaudeAcpAgent } from "../acp-agent.js";
const mockQuery = vi.hoisted(() =>
vi.fn(() => ({
initializationResult: vi.fn().mockResolvedValue({
models: [
{ value: "id", displayName: "name", description: "description", supportsAutoMode: true },
],
}),
setModel: vi.fn(),
setPermissionMode: vi.fn(),
supportedCommands: vi.fn().mockResolvedValue([]),
})),
);
vi.mock("@anthropic-ai/claude-agent-sdk", async () => ({
...(await vi.importActual<typeof import("@anthropic-ai/claude-agent-sdk")>(
"@anthropic-ai/claude-agent-sdk",
)),
query: mockQuery,
}));
// `logout` shells out to `claude auth logout`; make execFile succeed so the
// real logout body runs (and clears provider config) without a live CLI.
vi.mock("node:child_process", async () => {
const actual = await vi.importActual<typeof import("node:child_process")>("node:child_process");
return {
...actual,
execFile: vi.fn((_file: string, _args: string[], cb: (...a: unknown[]) => void) =>
cb(null, { stdout: "", stderr: "" }),
),
};
});
describe("providers", () => {
beforeEach(() => {
vi.useFakeTimers();
});
afterEach(() => {
vi.runAllTimers();
vi.useRealTimers();
vi.unstubAllGlobals();
vi.resetAllMocks();
});
async function createAgentMock(): Promise<[ClaudeAcpAgent, Mock]> {
const connectionMock = {
sessionUpdate: async (_: any) => {},
} as AcpClient;
const agent = new ClaudeAcpAgent(connectionMock);
return [agent, mockQuery];
}
it("advertises the providers capability in initialize", async () => {
const [agent] = await createAgentMock();
const response = await agent.initialize({ protocolVersion: 1, clientCapabilities: {} });
expect(response.agentCapabilities?.providers).toEqual({});
});
it("lists a single optional 'main' provider, unconfigured by default", async () => {
const [agent] = await createAgentMock();
const response = await agent.unstable_listProviders({});
expect(response.providers).toEqual([
{
providerId: "main",
supported: ["anthropic", "bedrock", "vertex"],
required: false,
current: null,
},
]);
});
it("reflects apiType/baseUrl after set, and never echoes headers", async () => {
const [agent] = await createAgentMock();
await agent.unstable_setProvider({
providerId: "main",
apiType: "anthropic",
baseUrl: "https://gateway.example/v1",
headers: { authorization: "Bearer secret" },
});
const response = await agent.unstable_listProviders({});
expect(response.providers[0].current).toEqual({
apiType: "anthropic",
baseUrl: "https://gateway.example/v1",
});
// headers must not leak through list
expect(JSON.stringify(response.providers)).not.toContain("secret");
});
it("rejects set for an unknown providerId", async () => {
const [agent] = await createAgentMock();
await expect(
agent.unstable_setProvider({
providerId: "openai",
apiType: "anthropic",
baseUrl: "https://gateway.example",
}),
).rejects.toMatchObject({ code: -32602 });
});
it("rejects set for an unsupported apiType", async () => {
const [agent] = await createAgentMock();
await expect(
agent.unstable_setProvider({
providerId: "main",
apiType: "openai",
baseUrl: "https://gateway.example",
}),
).rejects.toMatchObject({ code: -32602 });
});
it("rejects set for an empty or non-http baseUrl", async () => {
const [agent] = await createAgentMock();
await expect(
agent.unstable_setProvider({ providerId: "main", apiType: "anthropic", baseUrl: "" }),
).rejects.toMatchObject({ code: -32602 });
await expect(
agent.unstable_setProvider({
providerId: "main",
apiType: "anthropic",
baseUrl: "ftp://nope.example",
}),
).rejects.toMatchObject({ code: -32602 });
});
it("disables the 'main' provider by clearing config and reporting current: null", async () => {
const [agent] = await createAgentMock();
await agent.unstable_setProvider({
providerId: "main",
apiType: "anthropic",
baseUrl: "https://gateway.example/v1",
});
expect((await agent.unstable_listProviders({})).providers[0].current).not.toBeNull();
await expect(agent.unstable_disableProvider({ providerId: "main" })).resolves.toEqual({});
expect((await agent.unstable_listProviders({})).providers[0].current).toBeNull();
});
it("treats disabling an unknown provider as an idempotent no-op", async () => {
const [agent] = await createAgentMock();
await expect(agent.unstable_disableProvider({ providerId: "openai" })).resolves.toEqual({});
});
it("routes anthropic provider config into session env", async () => {
const [agent, mockQuery] = await createAgentMock();
await agent.initialize({ protocolVersion: 1, clientCapabilities: {} });
await agent.unstable_setProvider({
providerId: "main",
apiType: "anthropic",
baseUrl: "https://gateway.example",
headers: { "x-api-key": "test" },
});
await agent.newSession({ cwd: process.cwd(), mcpServers: [] });
expect(mockQuery).toHaveBeenCalledWith(
expect.objectContaining({
options: expect.objectContaining({
env: expect.objectContaining({
ANTHROPIC_AUTH_TOKEN: " ",
ANTHROPIC_BASE_URL: "https://gateway.example",
ANTHROPIC_CUSTOM_HEADERS: "x-api-key: test",
}),
}),
}),
);
});
it("routes bedrock provider config into session env", async () => {
const [agent, mockQuery] = await createAgentMock();
await agent.initialize({ protocolVersion: 1, clientCapabilities: {} });
await agent.unstable_setProvider({
providerId: "main",
apiType: "bedrock",
baseUrl: "https://gateway.example",
headers: { "custom-header": "test" },
});
await agent.newSession({ cwd: process.cwd(), mcpServers: [] });
expect(mockQuery).toHaveBeenCalledWith(
expect.objectContaining({
options: expect.objectContaining({
env: expect.objectContaining({
CLAUDE_CODE_USE_BEDROCK: "1",
AWS_BEARER_TOKEN_BEDROCK: " ",
ANTHROPIC_BEDROCK_BASE_URL: "https://gateway.example",
ANTHROPIC_CUSTOM_HEADERS: "custom-header: test",
}),
}),
}),
);
});
it("accepts vertex config via _meta and routes it into session env", async () => {
const [agent, mockQuery] = await createAgentMock();
await agent.initialize({ protocolVersion: 1, clientCapabilities: {} });
await agent.unstable_setProvider({
providerId: "main",
apiType: "vertex",
baseUrl: "https://vertex.example",
headers: { "custom-header": "test" },
_meta: { claudeCode: { vertex: { projectId: "my-project", region: "us-east5" } } },
});
// list surfaces apiType/baseUrl but not the _meta extras
const listed = await agent.unstable_listProviders({});
expect(listed.providers[0].current).toEqual({
apiType: "vertex",
baseUrl: "https://vertex.example",
});
await agent.newSession({ cwd: process.cwd(), mcpServers: [] });
expect(mockQuery).toHaveBeenCalledWith(
expect.objectContaining({
options: expect.objectContaining({
env: expect.objectContaining({
CLAUDE_CODE_USE_VERTEX: "1",
ANTHROPIC_VERTEX_BASE_URL: "https://vertex.example",
ANTHROPIC_VERTEX_PROJECT_ID: "my-project",
CLOUD_ML_REGION: "us-east5",
ANTHROPIC_CUSTOM_HEADERS: "custom-header: test",
}),
}),
}),
);
});
it("rejects vertex set without _meta project/region", async () => {
const [agent] = await createAgentMock();
await expect(
agent.unstable_setProvider({
providerId: "main",
apiType: "vertex",
baseUrl: "https://vertex.example",
}),
).rejects.toMatchObject({ code: -32602 });
// partial _meta (missing region) is also rejected
await expect(
agent.unstable_setProvider({
providerId: "main",
apiType: "vertex",
baseUrl: "https://vertex.example",
_meta: { claudeCode: { vertex: { projectId: "my-project" } } } as any,
}),
).rejects.toMatchObject({ code: -32602 });
});
it("providers/set takes precedence over gateway auth", async () => {
const [agent, mockQuery] = await createAgentMock();
await agent.initialize({
protocolVersion: 1,
clientCapabilities: { auth: { terminal: true, _meta: { gateway: true } } } as any,
});
// Gateway auth first...
await agent.authenticate({
methodId: "gateway",
_meta: { gateway: { baseUrl: "https://gateway.example", headers: {} } },
});
// ...then providers/set wins.
await agent.unstable_setProvider({
providerId: "main",
apiType: "anthropic",
baseUrl: "https://provider.example",
});
await agent.newSession({ cwd: process.cwd(), mcpServers: [] });
expect(mockQuery).toHaveBeenCalledWith(
expect.objectContaining({
options: expect.objectContaining({
env: expect.objectContaining({
ANTHROPIC_BASE_URL: "https://provider.example",
}),
}),
}),
);
});
it("clears provider config on logout", async () => {
const [agent] = await createAgentMock();
// Avoids resolving the native CLI binary in claudeCliPath().
vi.stubEnv("CLAUDE_CODE_EXECUTABLE", "/bin/true");
await agent.unstable_setProvider({
providerId: "main",
apiType: "anthropic",
baseUrl: "https://provider.example",
});
expect((await agent.unstable_listProviders({})).providers[0].current).not.toBeNull();
await agent.logout({});
expect((await agent.unstable_listProviders({})).providers[0].current).toBeNull();
});
});

View File

@@ -2,6 +2,7 @@ import { describe, it, expect, beforeEach, vi } from "vitest";
import { SessionNotification } from "@agentclientprotocol/sdk";
import type { ModelInfo } from "@anthropic-ai/claude-agent-sdk";
import type { AcpClient, ClaudeAcpAgent as ClaudeAcpAgentType } from "../acp-agent.js";
import { makeMockQuery } from "./helpers.js";
const { registerHookCallbackSpy } = vi.hoisted(() => ({
registerHookCallbackSpy: vi.fn(),
@@ -101,12 +102,11 @@ describe("session config options", () => {
applyFlagSettingsSpy = vi.fn();
(agent as unknown as { sessions: Record<string, unknown> }).sessions[SESSION_ID] = {
query: {
query: makeMockQuery({
setPermissionMode: setPermissionModeSpy,
setModel: setModelSpy,
applyFlagSettings: applyFlagSettingsSpy,
supportedCommands: async () => [],
},
}),
input: null,
cancelled: false,
permissionMode: "default",
@@ -927,6 +927,84 @@ describe("session config options", () => {
});
});
describe("context window on model change", () => {
beforeEach(() => {
populateSession();
});
function getSession() {
return (agent as unknown as { sessions: Record<string, any> }).sessions[SESSION_ID];
}
it("sets the window from text inference on model switch, without any getContextUsage IPC", async () => {
// getContextUsage stalls until the session's first prompt turn, so the
// switch path must never call it; the window is seeded from the text
// heuristic (here via the new model's resolvedModel) and later confirmed
// by result.modelUsage.
const session = getSession();
session.query.getContextUsage = vi.fn(async () => ({ rawMaxTokens: 967000 }));
session.modelInfos = session.modelInfos.map((m: ModelInfo) =>
m.value === "claude-sonnet-4-6" ? { ...m, resolvedModel: "claude-sonnet-5[1m]" } : m,
);
await agent.setSessionConfigOption({
sessionId: SESSION_ID,
configId: "model",
value: "claude-sonnet-4-6",
});
expect(session.query.getContextUsage).not.toHaveBeenCalled();
expect(session.contextWindowSize).toBe(1_000_000);
});
it("falls back to the default window when inference misses, without any getContextUsage IPC", async () => {
const session = getSession();
session.contextWindowSize = 1_000_000;
// Present but must NOT be called; the switch never consults it.
session.query.getContextUsage = vi.fn(async () => ({ rawMaxTokens: 967000 }));
// claude-sonnet-4-6 carries no "1m" token in its id, resolvedModel,
// displayName, or description, so inference misses → default window.
await agent.setSessionConfigOption({
sessionId: SESSION_ID,
configId: "model",
value: "claude-sonnet-4-6",
});
expect(session.query.getContextUsage).not.toHaveBeenCalled();
expect(session.contextWindowSize).toBe(200000);
});
it("does not call getContextUsage even when switching to a fresh model", async () => {
const session = getSession();
const spy = vi.fn(async () => ({ rawMaxTokens: 967000 }));
session.query.getContextUsage = spy;
await agent.setSessionConfigOption({
sessionId: SESSION_ID,
configId: "model",
value: "claude-sonnet-4-6",
});
expect(spy).not.toHaveBeenCalled();
});
it("keeps the learned window when re-asserting the current model", async () => {
const session = getSession();
session.contextWindowSize = 1_000_000;
session.query.getContextUsage = vi.fn(async () => ({ rawMaxTokens: 200000 }));
await agent.setSessionConfigOption({
sessionId: SESSION_ID,
configId: "model",
value: "claude-opus-4-5",
});
expect(session.query.getContextUsage).not.toHaveBeenCalled();
expect(session.contextWindowSize).toBe(1_000_000);
});
});
describe("auto mode availability per model", () => {
/**
* Augment the session populated by `populateSession()` with a Haiku entry

View File

@@ -1,4 +1,4 @@
import { describe, it, expect, beforeEach } from "vitest";
import { describe, it, expect, beforeEach, vi } from "vitest";
import { ClientCapabilities } from "@agentclientprotocol/sdk";
import { ImageBlockParam, ToolResultBlockParam } from "@anthropic-ai/sdk/resources";
import {
@@ -491,6 +491,94 @@ describe("Bash terminal output", () => {
});
});
it("keys the terminal metas off the tool_use id, which is what was announced", () => {
// `toolInfoFromToolUse` announces the terminal as `toolUse.id`, so the
// result's metas have to use the same value for the client to match them
// up. A result block that disagrees (or omits `tool_use_id`) must not be
// allowed to retarget them.
const toolResult = {
...makeBashResult("out", "", 0),
tool_use_id: "toolu_something_else",
};
const update = toolUpdateFromToolResult(toolResult, bashToolUse, true);
expect(update.content).toEqual([{ type: "terminal", terminalId: "toolu_bash" }]);
expect(update._meta).toEqual({
terminal_info: { terminal_id: "toolu_bash" },
terminal_output: { terminal_id: "toolu_bash", data: "out" },
terminal_exit: { terminal_id: "toolu_bash", exit_code: 0, signal: null },
});
});
it("falls back to the result's tool_use_id when the tool_use is unavailable", () => {
const toolResult = makeBashResult("out", "", 0);
const update = toolUpdateFromToolResult(toolResult, { name: "Bash" }, true);
expect(update.content).toEqual([{ type: "terminal", terminalId: "toolu_bash" }]);
expect(update._meta).toEqual({
terminal_info: { terminal_id: "toolu_bash" },
terminal_output: { terminal_id: "toolu_bash", data: "out" },
terminal_exit: { terminal_id: "toolu_bash", exit_code: 0, signal: null },
});
});
it("renders a code block instead of a dangling terminal when no id is available", () => {
// Previously this emitted `terminal_id: ""` for all three metas. Nothing
// on the client has a terminal under that id, so the output was stranded
// (Zed buffers output/exit for unknown terminals indefinitely) and the
// user saw an empty terminal. Degrade to the non-terminal rendering.
// `tool_use_id` is required on every result-block type, so a block without
// it can only arrive at runtime (an older or non-conforming emitter). The
// source guards for it with `"tool_use_id" in toolResult`, so exercise that
// path with a cast rather than pretending the type allows it.
const { content, type } = makeBashResult("out", "", 0);
const update = toolUpdateFromToolResult(
{ content, type } as unknown as Parameters<typeof toolUpdateFromToolResult>[0],
{ name: "Bash" },
true,
);
expect(update._meta).toBeUndefined();
expect(update.content).toEqual([
{
type: "content",
content: { type: "text", text: "```console\nout\n```" },
},
]);
});
it("treats an empty tool_use id as no id and falls back to the result block", () => {
const toolResult = makeBashResult("out", "", 0);
const update = toolUpdateFromToolResult(toolResult, { id: "", name: "Bash" }, true);
expect(update.content).toEqual([{ type: "terminal", terminalId: "toolu_bash" }]);
expect(update._meta).toEqual({
terminal_info: { terminal_id: "toolu_bash" },
terminal_output: { terminal_id: "toolu_bash", data: "out" },
terminal_exit: { terminal_id: "toolu_bash", exit_code: 0, signal: null },
});
});
it("renders a code block when neither id is a usable string", () => {
// A present-but-undefined `tool_use_id` still satisfies an `in` check, and
// stringifying it would key all three metas on the literal "undefined" — an
// id no client ever created a terminal for, so the output strands exactly as
// it did under the old empty-string id.
const toolResult = {
...makeBashResult("out", "", 0),
tool_use_id: undefined,
} as unknown as Parameters<typeof toolUpdateFromToolResult>[0];
const update = toolUpdateFromToolResult(toolResult, { id: "", name: "Bash" }, true);
expect(update._meta).toBeUndefined();
expect(update.content).toEqual([
{
type: "content",
content: { type: "text", text: "```console\nout\n```" },
},
]);
});
it("should include exit_code from return_code in terminal_exit", () => {
const toolResult = makeBashResult("", "command not found", 127);
const update = toolUpdateFromToolResult(toolResult, bashToolUse, true);
@@ -1024,7 +1112,7 @@ describe("Bash terminal output", () => {
);
// Fire PostToolUse hook with a structuredPatch in tool_response
const hook = createPostToolUseHook(mockLogger);
const hook = createPostToolUseHook();
await hook(
{
hook_event_name: "PostToolUse",
@@ -1102,7 +1190,7 @@ describe("Bash terminal output", () => {
mockLogger,
);
const hook = createPostToolUseHook(mockLogger);
const hook = createPostToolUseHook();
await hook(
{
hook_event_name: "PostToolUse",
@@ -1182,7 +1270,7 @@ describe("Bash terminal output", () => {
mockLogger,
);
const hook = createPostToolUseHook(mockLogger);
const hook = createPostToolUseHook();
await hook(
{
hook_event_name: "PostToolUse",
@@ -1203,6 +1291,36 @@ describe("Bash terminal output", () => {
expect(hookUpdate.content).toBeUndefined();
expect(hookUpdate.locations).toBeUndefined();
});
// Regression for issue #889: tool uses that never register a callback
// (TodoWrite/Task* are rendered as plan updates, not tool_calls) fire the
// PostToolUse hook too. That's expected — the hook must stay silent
// instead of spamming "No onPostToolUseHook found" to stderr.
it("should not log an error when no callback is registered for the tool use", async () => {
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
try {
const hook = createPostToolUseHook();
const result = await hook(
{
hook_event_name: "PostToolUse",
tool_name: "TodoWrite",
tool_input: { todos: [] },
tool_response: { success: true },
tool_use_id: "toolu_todo_no_callback",
session_id: "test-session",
transcript_path: "/tmp/test",
cwd: "/tmp",
},
"toolu_todo_no_callback",
{ signal: AbortSignal.abort() },
);
expect(result).toEqual({ continue: true });
expect(errorSpy).not.toHaveBeenCalled();
} finally {
errorSpy.mockRestore();
}
});
});
describe("post-tool-use hook sends diff content for Write tool", () => {
@@ -1239,7 +1357,7 @@ describe("Bash terminal output", () => {
mockLogger,
);
const hook = createPostToolUseHook(mockLogger);
const hook = createPostToolUseHook();
await hook(
{
hook_event_name: "PostToolUse",
@@ -1315,7 +1433,7 @@ describe("Bash terminal output", () => {
mockLogger,
);
const hook = createPostToolUseHook(mockLogger);
const hook = createPostToolUseHook();
await hook(
{
hook_event_name: "PostToolUse",
@@ -1438,7 +1556,7 @@ describe("Bash terminal output", () => {
expect((resultNotifications[1].update as any).status).toBe("completed");
// Step 3: Fire the PostToolUse hook (simulates what Claude Code SDK does)
const hook = createPostToolUseHook(mockLogger);
const hook = createPostToolUseHook();
await hook(
{
hook_event_name: "PostToolUse",
@@ -1519,7 +1637,7 @@ describe("Bash terminal output", () => {
);
// Fire hook
const hook = createPostToolUseHook(mockLogger);
const hook = createPostToolUseHook();
await hook(
{
hook_event_name: "PostToolUse",
@@ -2205,3 +2323,664 @@ describe("empty message content is not emitted", () => {
});
});
});
describe("Agent/Task tool_result rendering from tool_use_result", () => {
const mockClient = {} as AcpClient;
const mockLogger: Logger = { log: () => {}, error: () => {} };
const TRAILER =
"\nagentId: a0e1eff08fcb6e2e8 (use SendMessage with to: 'a0e1eff08fcb6e2e8', summary: '<5-10 word recap>' to continue this agent)\n<usage>subagent_tokens: 11735\ntool_uses: 2\nduration_ms: 21237</usage>";
const agentToolUse = {
type: "tool_use" as const,
id: "toolu_agent",
name: "Task",
input: { description: "Explore", prompt: "look around" },
};
const rawResult: ToolResultBlockParam = {
type: "tool_result",
tool_use_id: "toolu_agent",
content: [{ type: "text", text: `The report.${TRAILER}` }],
};
const structured = {
status: "completed",
agentId: "a0e1eff08fcb6e2e8",
content: [{ type: "text", text: "The structured report." }],
totalTokens: 11735,
totalToolUseCount: 2,
totalDurationMs: 21237,
};
it("renders the structured subagent report instead of the raw trailer text", () => {
const update = toolUpdateFromToolResult(rawResult, agentToolUse, false, structured);
expect(update).toEqual({
content: [{ type: "content", content: { type: "text", text: "The structured report." } }],
});
});
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.
const update = toolUpdateFromToolResult(rawResult, agentToolUse, false);
expect(update).toEqual({
content: [{ type: "content", content: { type: "text", text: "The report." } }],
});
});
it("strips only matching trailer parts and leaves unrecognized text alone", () => {
const oddResult: ToolResultBlockParam = {
type: "tool_result",
tool_use_id: "toolu_agent",
content: [
{ type: "text", text: "Report A.\n<usage>subagent_tokens: 5</usage>" },
{ type: "text", text: "Report B.\nagentId: abc-123 (for resuming)" },
{ type: "text", text: "agentId mentioned mid-text (not a trailer) stays.\nDone." },
],
};
const update = toolUpdateFromToolResult(oddResult, agentToolUse, false);
expect(update.content).toEqual([
{ type: "content", content: { type: "text", text: "Report A." } },
{ type: "content", content: { type: "text", text: "Report B." } },
{
type: "content",
content: { type: "text", text: "agentId mentioned mid-text (not a trailer) stays.\nDone." },
},
]);
});
it("leaves malformed trailers alone", () => {
// Incomplete trailers are ordinary report text, not metadata to strip.
const malformedResult: ToolResultBlockParam = {
type: "tool_result",
tool_use_id: "toolu_agent",
content: [
{ type: "text", text: "Report.\n<usage>missing closing tag" },
{ type: "text", text: "Report.\nagentId: abc-123 (missing closing paren" },
],
};
const update = toolUpdateFromToolResult(malformedResult, agentToolUse, false);
expect(update.content).toEqual([
{ type: "content", content: { type: "text", text: "Report.\n<usage>missing closing tag" } },
{
type: "content",
content: { type: "text", text: "Report.\nagentId: abc-123 (missing closing paren" },
},
]);
});
it("strips only the trailer when the report itself mentions <usage>", () => {
const result: ToolResultBlockParam = {
type: "tool_result",
tool_use_id: "toolu_agent",
content: "Grep for <usage> found 3 hits.\n<usage>subagent_tokens: 5</usage>",
};
const update = toolUpdateFromToolResult(result, agentToolUse, false);
expect(update.content).toEqual([
{ type: "content", content: { type: "text", text: "Grep for <usage> found 3 hits." } },
]);
});
it("handles adversarial trailer-shaped input in linear time", () => {
// Regression: the old regex-based strip backtracked quadratically on
// these shapes (CodeQL js/polynomial-redos) — at this size it would
// blow the test timeout rather than merely run slow.
const result: ToolResultBlockParam = {
type: "tool_result",
tool_use_id: "toolu_agent",
content: [
{ type: "text", text: "agentId: - (".repeat(20000) },
{ type: "text", text: "<usage>".repeat(30000) },
],
};
const update = toolUpdateFromToolResult(result, agentToolUse, false);
// Neither is a real trailer, so both come through unchanged.
expect(update.content).toEqual([
{ type: "content", content: { type: "text", text: "agentId: - (".repeat(20000) } },
{ type: "content", content: { type: "text", text: "<usage>".repeat(30000) } },
]);
});
it("falls back (trailer-stripped) when tool_use_result is the async_launched variant", () => {
const update = toolUpdateFromToolResult(rawResult, agentToolUse, false, {
status: "async_launched",
agentId: "a0e1eff08fcb6e2e8",
description: "Explore",
});
expect(update.content).toEqual([
{ type: "content", content: { type: "text", text: "The report." } },
]);
});
it("falls back (trailer-stripped) when the structured content array is empty", () => {
// A completed subagent can end with zero text blocks — an empty
// structured render must not beat the raw fallback.
const update = toolUpdateFromToolResult(rawResult, agentToolUse, false, {
...structured,
content: [],
});
expect(update.content).toEqual([
{ type: "content", content: { type: "text", text: "The report." } },
]);
});
it("threads options.toolUseResult through toAcpNotifications for a lone tool_result", () => {
const toolUseCache: ToolUseCache = { toolu_agent: agentToolUse };
const notifications = toAcpNotifications(
[rawResult] as any,
"user",
"test-session",
toolUseCache,
mockClient,
mockLogger,
{ toolUseResult: structured },
);
expect(notifications).toHaveLength(1);
expect(notifications[0].update).toMatchObject({
sessionUpdate: "tool_call_update",
toolCallId: "toolu_agent",
status: "completed",
content: [{ type: "content", content: { type: "text", text: "The structured report." } }],
});
});
it("ignores options.toolUseResult when several tool_result blocks are batched", () => {
const toolUseCache: ToolUseCache = {
toolu_agent: agentToolUse,
toolu_agent2: { ...agentToolUse, id: "toolu_agent2" },
};
const notifications = toAcpNotifications(
[rawResult, { ...rawResult, tool_use_id: "toolu_agent2" }] as any,
"user",
"test-session",
toolUseCache,
mockClient,
mockLogger,
{ toolUseResult: structured },
);
expect(notifications).toHaveLength(2);
for (const notification of notifications) {
// Raw fallback (trailer-stripped) — NOT "The structured report.", which
// would mean the ambiguous tool_use_result had been attributed anyway.
expect(notification.update).toMatchObject({
content: [{ type: "content", content: { type: "text", text: "The report." } }],
});
}
});
});
describe("tool_result_meta non-execution stamping", () => {
const mockClient = {} as AcpClient;
const mockLogger: Logger = { log: () => {}, error: () => {} };
const bashToolUse = {
type: "tool_use" as const,
id: "toolu_bash",
name: "Bash",
input: { command: "rm -rf build" },
};
const deniedResult: ToolResultBlockParam = {
type: "tool_result",
tool_use_id: "toolu_bash",
is_error: true,
content: "The user doesn't want to proceed with this tool use.",
};
it("stamps nonExecutionKind and userFeedback on the failed tool_call_update", () => {
const toolUseCache: ToolUseCache = { toolu_bash: bashToolUse };
const notifications = toAcpNotifications(
[deniedResult] as any,
"user",
"test-session",
toolUseCache,
mockClient,
mockLogger,
{
toolResultMeta: [
{ id: "toolu_bash", non_execution_kind: "user-rejected", user_feedback: "use npm" },
],
},
);
expect(notifications).toHaveLength(1);
expect(notifications[0].update).toMatchObject({
sessionUpdate: "tool_call_update",
toolCallId: "toolu_bash",
status: "failed",
_meta: {
claudeCode: {
toolName: "Bash",
nonExecutionKind: "user-rejected",
userFeedback: "use npm",
},
},
});
});
it("attributes entries by tool_use_id, so only the flagged result in a batch is stamped", () => {
const toolUseCache: ToolUseCache = {
toolu_bash: bashToolUse,
toolu_bash2: { ...bashToolUse, id: "toolu_bash2" },
};
const notifications = toAcpNotifications(
[
deniedResult,
{
type: "tool_result",
tool_use_id: "toolu_bash2",
content: "ok",
},
] as any,
"user",
"test-session",
toolUseCache,
mockClient,
mockLogger,
{ toolResultMeta: [{ id: "toolu_bash", non_execution_kind: "user-rejected" }] },
);
expect(notifications).toHaveLength(2);
const [denied, ran] = notifications.map((n) => n.update) as any[];
expect(denied._meta.claudeCode).toMatchObject({ nonExecutionKind: "user-rejected" });
// No user_feedback on the wire entry → no userFeedback key at all.
expect(denied._meta.claudeCode).not.toHaveProperty("userFeedback");
expect(ran._meta.claudeCode).not.toHaveProperty("nonExecutionKind");
});
it("ignores a malformed sidecar and malformed entries", () => {
for (const malformed of [
"user-rejected", // not an array
[{ non_execution_kind: "user-rejected" }], // entry missing id
[{ id: "toolu_bash", non_execution_kind: 7 }], // kind not a string
[null, 42], // entries not objects
]) {
const toolUseCache: ToolUseCache = { toolu_bash: bashToolUse };
const notifications = toAcpNotifications(
[deniedResult] as any,
"user",
"test-session",
toolUseCache,
mockClient,
mockLogger,
{ toolResultMeta: malformed },
);
expect(notifications).toHaveLength(1);
expect((notifications[0].update as any)._meta.claudeCode).not.toHaveProperty(
"nonExecutionKind",
);
}
});
it("stamps the resolve of a permission-surfaced suppressed tool (Task*)", () => {
// A TaskGet surfaced as a real tool_call by the permission flow never gets
// a tool_call_update from the suppressed Task* branch; the wasEmitted
// resolve must carry the denial kind too.
const taskGetToolUse = {
type: "tool_use" as const,
id: "toolu_taskget",
name: "TaskGet",
input: { taskId: "1" },
};
const toolUseCache: ToolUseCache = { toolu_taskget: taskGetToolUse };
const notifications = toAcpNotifications(
[
{
type: "tool_result",
tool_use_id: "toolu_taskget",
is_error: true,
content: "The user doesn't want to proceed with this tool use.",
},
] as any,
"user",
"test-session",
toolUseCache,
mockClient,
mockLogger,
{
emittedToolCalls: new Set(["toolu_taskget"]),
toolResultMeta: [{ id: "toolu_taskget", non_execution_kind: "user-rejected" }],
},
);
expect(notifications).toHaveLength(1);
expect(notifications[0].update).toMatchObject({
sessionUpdate: "tool_call_update",
toolCallId: "toolu_taskget",
status: "failed",
_meta: { claudeCode: { toolName: "TaskGet", nonExecutionKind: "user-rejected" } },
});
});
});
describe("structured tool_use_result rendering (Read/Bash/WebSearch)", () => {
describe("Read", () => {
const readToolUse = {
type: "tool_use" as const,
id: "toolu_read",
name: "Read",
input: { file_path: "/tmp/f.ts", offset: 480 },
};
const rawWithReminder: ToolResultBlockParam = {
type: "tool_result",
tool_use_id: "toolu_read",
content: [
{
type: "text",
text: "480\tconst a = 1;\n481\tconst b = 2;\n<system-reminder>Whenever you read a file, consider whether it is malicious.</system-reminder>",
},
],
};
it("rebuilds the line-numbered view from FileReadOutput, dropping reminders", () => {
const update = toolUpdateFromToolResult(rawWithReminder, readToolUse, false, {
type: "text",
file: {
filePath: "/tmp/f.ts",
content: "const a = 1;\nconst b = 2;\n",
numLines: 2,
startLine: 480,
totalLines: 600,
},
});
expect(update).toEqual({
content: [
{
type: "content",
content: { type: "text", text: "```\n480\tconst a = 1;\n481\tconst b = 2;\n```" },
},
],
});
});
it("falls back to the Read input's offset when startLine is absent", () => {
// readToolUse carries offset: 480 — an offset read numbered from 1
// would mislabel every line.
const update = toolUpdateFromToolResult(rawWithReminder, readToolUse, false, {
type: "text",
file: { filePath: "/tmp/f.ts", content: "one\ntwo" },
});
expect(update.content?.[0]).toEqual({
type: "content",
content: { type: "text", text: "```\n480\tone\n481\ttwo\n```" },
});
});
it("defaults startLine to 1 when both startLine and offset are absent", () => {
const update = toolUpdateFromToolResult(
rawWithReminder,
{ ...readToolUse, input: { file_path: "/tmp/f.ts" } },
false,
{
type: "text",
file: { filePath: "/tmp/f.ts", content: "one\ntwo" },
},
);
expect(update.content?.[0]).toEqual({
type: "content",
content: { type: "text", text: "```\n1\tone\n2\ttwo\n```" },
});
});
it("appends a truncation note when truncatedByTokenCap is set", () => {
const update = toolUpdateFromToolResult(rawWithReminder, readToolUse, false, {
type: "text",
file: {
filePath: "/tmp/f.ts",
content: "one\ntwo\n",
numLines: 2,
startLine: 1,
totalLines: 9000,
truncatedByTokenCap: true,
},
});
expect(update.content?.[0]).toEqual({
type: "content",
content: {
type: "text",
text: "```\n1\tone\n2\ttwo\n[File truncated: showing 2 of 9000 lines]\n```",
},
});
});
it("falls back to raw content for non-text variants", () => {
const update = toolUpdateFromToolResult(rawWithReminder, readToolUse, false, {
type: "image",
file: { base64: "aGk=", type: "image/png", originalSize: 3 },
});
// Raw path: markdown-escaped raw text (reminder included — image reads
// don't carry reminders in practice).
expect(update.content).toHaveLength(1);
expect((update.content?.[0] as any).content.text).toContain("const a = 1;");
});
});
describe("Bash", () => {
const bashToolUse = {
type: "tool_use" as const,
id: "toolu_bash",
name: "Bash",
input: { command: "git push" },
};
const HINT =
"\n[This command modified 1 file you've previously read: src/foo.ts. Call Read before editing.]";
const rawWithHint: ToolResultBlockParam = {
type: "tool_result",
tool_use_id: "toolu_bash",
content: `pushed ok${HINT}`,
};
const structured = {
stdout: "pushed ok",
stderr: "",
interrupted: false,
isImage: false,
};
it("prefers structured stdout/stderr over raw text with model-directed hints", () => {
const update = toolUpdateFromToolResult(rawWithHint, bashToolUse, true, structured);
expect(update._meta?.terminal_output).toEqual({
terminal_id: "toolu_bash",
data: "pushed ok",
});
expect(update._meta?.terminal_exit).toEqual({
terminal_id: "toolu_bash",
exit_code: 0,
signal: null,
});
});
it("joins stderr after stdout like the code-execution path", () => {
const update = toolUpdateFromToolResult(rawWithHint, bashToolUse, false, {
...structured,
stderr: "warning: something",
});
expect(update.content).toEqual([
{
type: "content",
content: { type: "text", text: "```console\npushed ok\nwarning: something\n```" },
},
]);
});
it("falls back to raw text for backgrounded commands", () => {
const update = toolUpdateFromToolResult(rawWithHint, bashToolUse, true, {
...structured,
stdout: "",
backgroundTaskId: "bash_1",
});
expect(update._meta?.terminal_output?.data).toBe(`pushed ok${HINT}`);
});
it("falls back to the raw content array for image output", () => {
const imageResult: ToolResultBlockParam = {
type: "tool_result",
tool_use_id: "toolu_bash",
content: [
{ type: "image", source: { type: "base64", data: "aGk=", media_type: "image/png" } },
],
};
const update = toolUpdateFromToolResult(imageResult, bashToolUse, true, {
...structured,
isImage: true,
});
expect(update.content).toEqual([
{
type: "content",
content: { type: "image", data: "aGk=", mimeType: "image/png" },
},
]);
});
it("re-establishes the abort notice and a failing exit code for interrupted commands", () => {
const update = toolUpdateFromToolResult(rawWithHint, bashToolUse, true, {
...structured,
stdout: "partial output",
interrupted: true,
});
expect(update._meta?.terminal_output).toEqual({
terminal_id: "toolu_bash",
data: "partial output\n[Command was aborted before completion]",
});
expect(update._meta?.terminal_exit).toEqual({
terminal_id: "toolu_bash",
exit_code: 1,
signal: null,
});
});
it("re-establishes the truncation note and persisted path for too-large outputs", () => {
const update = toolUpdateFromToolResult(rawWithHint, bashToolUse, true, {
...structured,
stdout: "clipped stdout",
persistedOutputPath: "/tmp/tool-results/abc.txt",
persistedOutputSize: 38100,
});
expect(update._meta?.terminal_output).toEqual({
terminal_id: "toolu_bash",
data: "clipped stdout\n[Output truncated (38100 bytes total): full output saved to /tmp/tool-results/abc.txt]",
});
});
});
describe("WebSearch", () => {
const searchToolUse = {
type: "tool_use" as const,
id: "toolu_search",
name: "WebSearch",
input: { query: "npm sigstore bug" },
};
const rawDump: ToolResultBlockParam = {
type: "tool_result",
tool_use_id: "toolu_search",
content:
'Web search results for query: "npm sigstore bug"\n\nLinks: [{"title":"Issue #9722","url":"https://github.com/npm/cli/issues/9722"}]',
};
it("renders hits as Title (url) lines from WebSearchOutput", () => {
const update = toolUpdateFromToolResult(rawDump, searchToolUse, false, {
query: "npm sigstore bug",
durationSeconds: 5.5,
results: [
"I found one relevant issue:",
{
tool_use_id: "srvtoolu_1",
content: [
{ title: "Issue #9722", url: "https://github.com/npm/cli/issues/9722" },
{ title: "sigstore-js", url: "https://github.com/sigstore/sigstore-js" },
],
},
],
});
expect(update).toEqual({
content: [
{
type: "content",
content: {
type: "text",
text: "I found one relevant issue:\nIssue #9722 (https://github.com/npm/cli/issues/9722)\nsigstore-js (https://github.com/sigstore/sigstore-js)",
},
},
],
});
});
it("falls back to the raw dump when tool_use_result is absent", () => {
const update = toolUpdateFromToolResult(rawDump, searchToolUse, false);
expect((update.content?.[0] as any).content.text).toContain("Web search results for query");
});
it("skips off-spec hits instead of rendering undefined fields", () => {
const update = toolUpdateFromToolResult(rawDump, searchToolUse, false, {
query: "npm sigstore bug",
durationSeconds: 5.5,
results: [
{
tool_use_id: "srvtoolu_1",
content: [
{ error_code: "provider_error" },
{ title: "Issue #9722", url: "https://github.com/npm/cli/issues/9722" },
],
},
],
});
expect(update).toEqual({
content: [
{
type: "content",
content: {
type: "text",
text: "Issue #9722 (https://github.com/npm/cli/issues/9722)",
},
},
],
});
});
it("falls back to the raw dump when every hit is off-spec", () => {
const update = toolUpdateFromToolResult(rawDump, searchToolUse, false, {
query: "npm sigstore bug",
durationSeconds: 5.5,
results: [{ tool_use_id: "srvtoolu_1", content: [{ error_code: "provider_error" }] }],
});
expect((update.content?.[0] as any).content.text).toContain("Web search results for query");
});
});
});

View File

@@ -8,10 +8,13 @@ import {
import { HookCallback } from "@anthropic-ai/claude-agent-sdk";
import {
AgentInput,
AgentOutput,
AskUserQuestionInput,
BashInput,
BashOutput,
FileEditInput,
FileReadInput,
FileReadOutput,
FileWriteInput,
GlobInput,
GrepInput,
@@ -22,6 +25,7 @@ import {
TodoWriteInput,
WebFetchInput,
WebSearchInput,
WebSearchOutput,
} from "@anthropic-ai/claude-agent-sdk/sdk-tools.js";
import {
ImageBlockParam,
@@ -56,7 +60,6 @@ import {
BetaWebSearchToolResultBlockParam,
} from "@anthropic-ai/sdk/resources/beta.mjs";
import path from "node:path";
import { Logger } from "./acp-agent.js";
/**
* Union of all possible content types that can appear in tool results from the Anthropic SDK.
@@ -486,6 +489,88 @@ export function toolInfoFromToolUse(
}
}
/**
* Narrow the untyped message-level `tool_use_result` toward a per-tool Output
* shape: rejects everything but a plain non-null object (arrays pass a bare
* `typeof === "object"` check, so they're excluded here). The returned value
* is only *nominally* typed — it arrives over the wire from arbitrary CLI
* versions, so each caller must still guard the specific fields it reads
* before trusting them.
*/
function structuredResult<T extends object>(toolUseResult: unknown): T | undefined {
return toolUseResult !== null &&
typeof toolUseResult === "object" &&
!Array.isArray(toolUseResult)
? (toolUseResult as T)
: undefined;
}
/**
* Strip the model-directed trailer from a raw Agent/Task tool_result text:
* a `<usage>…</usage>` totals block and/or an
* `agentId: <id> (use SendMessage …)` continuation line at the end of the
* text. Both patterns are tail-anchored and independent (older CLIs emit
* variants with only one of them), so a format change makes them stop
* matching rather than mangle the report.
*/
function stripAgentTrailer(text: string): string {
return stripAgentIdLine(stripUsageBlock(text));
}
const USAGE_OPEN = "<usage>";
const USAGE_CLOSE = "</usage>";
/** Remove a trailing `<usage>…</usage>` block, plus trailing whitespace and
* one preceding newline. Matches from the *last* `<usage>` so a report that
* merely mentions the marker earlier isn't truncated at the mention. */
function stripUsageBlock(text: string): string {
const body = text.trimEnd();
if (!body.endsWith(USAGE_CLOSE)) {
return text;
}
const open = body.lastIndexOf(USAGE_OPEN, body.length - USAGE_CLOSE.length - USAGE_OPEN.length);
if (open === -1) {
return text;
}
return body.slice(0, open > 0 && body[open - 1] === "\n" ? open - 1 : open);
}
/** The continuation line, anchored to a whole line so the regex has a single
* start position and no ambiguous repetition (`[\w-]+` can't consume the
* following space, `[^)]*` can't consume the closing paren) — it runs in
* linear time on any input. */
const AGENT_ID_LINE = /^agentId: [\w-]+ \([^)]*\)$/;
/** Remove a final `agentId: <id> (…)` line, plus trailing whitespace and the
* newline that preceded the line. */
function stripAgentIdLine(text: string): string {
const body = text.trimEnd();
const lineStart = body.lastIndexOf("\n") + 1;
if (!AGENT_ID_LINE.test(body.slice(lineStart))) {
return text;
}
return body.slice(0, Math.max(lineStart - 1, 0));
}
/** Apply {@link stripAgentTrailer} across a raw tool_result `content` (plain
* string or block array), leaving non-text blocks untouched. */
function stripAgentTrailerFromContent(content: unknown): unknown {
if (typeof content === "string") {
return stripAgentTrailer(content);
}
if (Array.isArray(content)) {
return content.map((block) =>
block !== null &&
typeof block === "object" &&
block.type === "text" &&
typeof block.text === "string"
? { ...block, text: stripAgentTrailer(block.text) }
: block,
);
}
return content;
}
export function toolUpdateFromToolResult(
toolResult:
| ToolResultBlockParam
@@ -500,6 +585,7 @@ export function toolUpdateFromToolResult(
| BetaToolSearchToolResultBlockParam,
toolUse: any | undefined,
supportsTerminalOutput: boolean = false,
toolUseResult?: unknown,
): ToolUpdate {
if (
"is_error" in toolResult &&
@@ -512,8 +598,63 @@ export function toolUpdateFromToolResult(
return toAcpContentUpdate(toolResult.content, true);
}
// Shared raw-text fallback: renders the tool_result content the model saw.
// The structured cases below fall back to this when `tool_use_result` is
// absent or fails its shape guard (older CLIs, replayed sessions).
const rawContentUpdate = () =>
toAcpContentUpdate(toolResult.content, "is_error" in toolResult ? toolResult.is_error : false);
switch (toolUse?.name) {
case "Read":
case "Read": {
// The raw tool_result text is the model-facing view: line-numbered
// content plus any appended <system-reminder> blocks (malicious-code
// checks, memory staleness notes, …) that clients shouldn't see. The
// structured FileReadOutput carries the clean content — rebuild the
// line-numbered view from it. Non-text variants (image/notebook/pdf)
// fall back to the raw content blocks, which already render fine.
const structuredRead = structuredResult<FileReadOutput>(toolUseResult);
if (
structuredRead?.type === "text" &&
typeof structuredRead.file?.content === "string" &&
// An empty file has nothing to line-number; keep the raw view (the
// model-facing "file is empty" note) rather than a phantom blank line.
structuredRead.file.content.length > 0
) {
// startLine is typed non-optional but defended anyway; a Read's
// `offset` input is the same 1-based starting line, so it beats a
// blind 1 when an emitter omits the field.
const startLine =
structuredRead.file.startLine ??
(toolUse.input as FileReadInput | undefined)?.offset ??
1;
// A trailing newline is a line terminator, not an extra line — don't
// number a phantom empty line after it.
let numbered = structuredRead.file.content
.replace(/\n$/, "")
.split("\n")
.map((line, i) => `${startLine + i}\t${line}`)
.join("\n");
// The model-facing truncation banner doesn't survive reconstruction
// from file.content (the SDK flag exists for exactly this case) —
// re-establish it so a partial first page doesn't read as the whole
// file.
if (structuredRead.file.truncatedByTokenCap) {
const { numLines, totalLines } = structuredRead.file;
const detail =
typeof numLines === "number" && typeof totalLines === "number"
? `: showing ${numLines} of ${totalLines} lines`
: "";
numbered += `\n[File truncated${detail}]`;
}
return {
content: [
{
type: "content",
content: { type: "text", text: markdownEscape(numbered) },
},
],
};
}
if (Array.isArray(toolResult.content) && toolResult.content.length > 0) {
return {
content: toolResult.content.map((content: any) => ({
@@ -541,22 +682,77 @@ export function toolUpdateFromToolResult(
};
}
return {};
}
case "Bash": {
const result = toolResult.content;
const terminalId = "tool_use_id" in toolResult ? String(toolResult.tool_use_id) : "";
// The terminal was announced under the tool_use's own id (see
// `toolInfoFromToolUse`), so key the output/exit metas off that: it is the
// id the client actually created a terminal for. `toolResult.tool_use_id`
// is the same value whenever present — the caller looks the tool_use up by
// it — so preferring `toolUse.id` only adds a source for the case where the
// result block carries no id at all. Anything that isn't a non-empty
// string is no id at all: `""` matches no terminal, and stringifying a
// present-but-undefined field would invent the literal `"undefined"`.
const terminalIdOf = (id: unknown): string | undefined =>
typeof id === "string" && id.length > 0 ? id : undefined;
const terminalId: string | undefined =
terminalIdOf(toolUse?.id) ??
terminalIdOf("tool_use_id" in toolResult ? toolResult.tool_use_id : undefined);
const isError = "is_error" in toolResult && toolResult.is_error;
// Extract output and exit code from either format:
// 1. BetaBashCodeExecutionResultBlock: { type: "bash_code_execution_result", stdout, stderr, return_code }
// 2. Plain string content from a regular tool_result
// 3. Array content (e.g. [{ type: "text", text: "..." }] for stdout,
// 1. The structured BashOutput (message-level tool_use_result): its
// stdout/stderr exclude the model-directed suffixes the raw text
// carries (stale-read hints, gh rate-limit hints, the
// persisted-output wrapper for too-large outputs — the interruption
// and truncation facts those carried are re-established from the
// structured flags below). Skipped for image output (the raw content
// array carries the actual image blocks) and backgrounded commands
// (the raw text carries the background-task notice; structured
// stdout may be empty).
// 2. BetaBashCodeExecutionResultBlock: { type: "bash_code_execution_result", stdout, stderr, return_code }
// 3. Plain string content from a regular tool_result
// 4. Array content (e.g. [{ type: "text", text: "..." }] for stdout,
// or [{ type: "image", source: {...} }] when the local Bash tool
// produces an image, e.g. piping a base64 data URI)
let output = "";
let exitCode = isError ? 1 : 0;
const structuredBash = structuredResult<BashOutput>(toolUseResult);
if (
structuredBash &&
typeof structuredBash.stdout === "string" &&
typeof structuredBash.stderr === "string" &&
!structuredBash.isImage &&
structuredBash.backgroundTaskId === undefined
) {
output = [structuredBash.stdout, structuredBash.stderr].filter(Boolean).join("\n");
// Two raw-text notices don't survive the structured stdout/stderr —
// re-establish them so the client isn't shown a clean-looking result:
// the CLI appends its abort marker only to the model-facing text, and
// an aborted command isn't a success, so synthesize a failing exit
// code when the result wasn't already an error.
if (structuredBash.interrupted) {
output = [output, "[Command was aborted before completion]"].filter(Boolean).join("\n");
exitCode = 1;
}
// Structured stdout is clipped (~30k chars) when the full output was
// persisted to disk; without this note the clip is silent and the
// path to the full output is lost.
if (typeof structuredBash.persistedOutputPath === "string") {
const size =
typeof structuredBash.persistedOutputSize === "number"
? ` (${structuredBash.persistedOutputSize} bytes total)`
: "";
output = [
output,
`[Output truncated${size}: full output saved to ${structuredBash.persistedOutputPath}]`,
]
.filter(Boolean)
.join("\n");
}
} else if (
result &&
typeof result === "object" &&
"type" in result &&
@@ -583,7 +779,14 @@ export function toolUpdateFromToolResult(
}
}
if (supportsTerminalOutput) {
// Without a terminal id there is nothing the client can reconcile these
// metas against, and emitting them anyway strands the output: a client that
// buffers output/exit for terminals it has not been told about (Zed keeps
// them in `pending_terminal_output`/`pending_terminal_exit`, drained only on
// a matching create) would hold them forever behind an id that never
// arrives, showing an empty terminal. Fall through to the code-block
// rendering below instead.
if (supportsTerminalOutput && terminalId !== undefined) {
return {
content: [{ type: "terminal" as const, terminalId }],
_meta: {
@@ -619,6 +822,45 @@ export function toolUpdateFromToolResult(
return {};
}
case "Agent":
case "Task": {
// The raw tool_result text ends with a model-directed trailer (an
// `agentId: … (use SendMessage …)` line plus a `<usage>` totals block)
// that ACP clients shouldn't see. The message-level `tool_use_result`
// carries the structured AgentOutput whose `content` is the subagent's
// report without the trailer — render from it when present (per the SDK
// 0.3.207 guidance) and fall back to the raw text otherwise (older CLIs,
// replayed sessions).
// Narrowed to the full union, not the completed variant — the status
// check below is what discriminates it, and pre-narrowing would let
// future field reads typecheck against a variant the runtime value may
// not be.
const structured = structuredResult<AgentOutput>(toolUseResult);
if (
structured?.status === "completed" &&
Array.isArray(structured.content) &&
// A completed subagent can end with zero text blocks; an empty
// structured render would beat the raw fallback for no benefit.
structured.content.length > 0
) {
return toAcpContentUpdate(
structured.content,
"is_error" in toolResult ? toolResult.is_error : false,
);
}
// No structured report to render from (replayed sessions —
// getSessionMessages doesn't expose the transcript's toolUseResult —
// and older CLIs). The SDK advises rendering from tool_use_result
// instead of parsing the text, but with no structured value the
// tail-anchored strip is the only cleanup available; if the trailer
// format changes it simply stops matching and the full raw text
// renders, no worse than before.
return toAcpContentUpdate(
stripAgentTrailerFromContent(toolResult.content),
"is_error" in toolResult ? toolResult.is_error : false,
);
}
case "Edit": // Edit is handled in hooks
case "Write": {
return {};
@@ -628,15 +870,54 @@ export function toolUpdateFromToolResult(
return { title: "Exited Plan Mode" };
}
case "WebSearch": {
// The raw tool_result text is a model-directed dump ("Web search
// results for query: …\n\nLinks: [{…json…}]"). The structured
// WebSearchOutput carries the hits — render them the way server-side
// web_search_result blocks render ("Title (url)").
const structuredSearch = structuredResult<WebSearchOutput>(toolUseResult);
if (structuredSearch && Array.isArray(structuredSearch.results)) {
const lines = structuredSearch.results.flatMap((entry) =>
typeof entry === "string"
? [entry]
: Array.isArray(entry?.content)
? // tool_use_result arrives untyped across CLI version skew —
// skip off-spec hits rather than rendering
// "undefined (undefined)" lines.
entry.content.flatMap((hit) =>
typeof hit?.title === "string" && typeof hit?.url === "string"
? [formatWebSearchHit(hit)]
: [],
)
: [],
);
if (lines.length > 0) {
return {
content: [
{
type: "content",
content: { type: "text", text: lines.join("\n") },
},
],
};
}
}
return rawContentUpdate();
}
default: {
return toAcpContentUpdate(
toolResult.content,
"is_error" in toolResult ? toolResult.is_error : false,
);
return rawContentUpdate();
}
}
}
/** One display format for a web-search hit, shared by the structured
* WebSearchOutput render and the server-side `web_search_result` block so
* the two paths can't drift. */
function formatWebSearchHit(hit: { title: string; url: string }): string {
return `${hit.title} (${hit.url})`;
}
function toAcpContentUpdate(
content: any,
isError: boolean = false,
@@ -711,7 +992,7 @@ function toAcpContentBlock(content: ToolResultContent, isError: boolean): Conten
`Error: ${content.error_code}${content.error_message ? ` - ${content.error_message}` : ""}`,
);
case "web_search_result":
return wrapText(`${content.title} (${content.url})`);
return wrapText(formatWebSearchHit(content));
case "web_search_tool_result_error":
return wrapText(`Error: ${content.error_code}`);
case "web_fetch_result":
@@ -954,12 +1235,7 @@ export const registerHookCallback = (
/* A callback for Claude Code that is called when receiving a PostToolUse hook */
export const createPostToolUseHook =
(
logger: Logger = console,
options?: {
onEnterPlanMode?: () => Promise<void>;
},
): HookCallback =>
(options?: { onEnterPlanMode?: () => Promise<void> }): HookCallback =>
async (input: any, toolUseID: string | undefined): Promise<{ continue: boolean }> => {
if (input.hook_event_name === "PostToolUse") {
// Handle EnterPlanMode tool - notify client of mode change after successful execution
@@ -971,11 +1247,8 @@ export const createPostToolUseHook =
const onPostToolUseHook = toolUseCallbacks[toolUseID]?.onPostToolUseHook;
if (onPostToolUseHook) {
await onPostToolUseHook(toolUseID, input.tool_input, input.tool_response);
delete toolUseCallbacks[toolUseID]; // Cleanup after execution
} else {
logger.error(`No onPostToolUseHook found for tool use ID: ${toolUseID}`);
delete toolUseCallbacks[toolUseID];
}
delete toolUseCallbacks[toolUseID]; // Cleanup after execution
}
}
return { continue: true };