Compare commits

..

17 Commits

Author SHA1 Message Date
acp-release-bot[bot]
253ddeb6d4 chore(main): release 0.32.0 (#615)
🤖 I have created a release *beep* *boop*
---


##
[0.32.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.31.4...v0.32.0)
(2026-05-03)


### Features

* **deps-dev:** Bump nanoid from 3.3.11 to 3.3.12
([#622](https://github.com/agentclientprotocol/claude-agent-acp/issues/622))
([c78ac62](c78ac62d05))
* **deps:** Bump the minor group with 2 updates
([#619](https://github.com/agentclientprotocol/claude-agent-acp/issues/619))
([6ccd37c](6ccd37ce94))
* **deps:** update to @anthropic-ai/claude-agent-sdk 0.2.126
([#621](https://github.com/agentclientprotocol/claude-agent-acp/issues/621))
([becc3b8](becc3b86e6))
* **deps:** Update to @anthropic-ai/claude-agent-sdk@0.2.123
([#614](https://github.com/agentclientprotocol/claude-agent-acp/issues/614))
([5b93119](5b9311938d))


### Bug Fixes

* emit a real diff when Write overwrites an existing file
([#618](https://github.com/agentclientprotocol/claude-agent-acp/issues/618))
([8d7e220](8d7e22026e))

---
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-05-04 00:02:01 +02:00
dependabot[bot]
becc3b86e6 feat(deps): update to @anthropic-ai/claude-agent-sdk 0.2.126 (#621)
Bumps the minor group with 12 updates:

| Package | From | To |
| --- | --- | --- |
|
[@anthropic-ai/claude-agent-sdk](https://github.com/anthropics/claude-agent-sdk-typescript)
| `0.2.123` | `0.2.126` |
| [eslint](https://github.com/eslint/eslint) | `10.2.1` | `10.3.0` |
| [globals](https://github.com/sindresorhus/globals) | `17.5.0` |
`17.6.0` |
| @anthropic-ai/claude-agent-sdk-darwin-arm64 | `0.2.123` | `0.2.126` |
| @anthropic-ai/claude-agent-sdk-darwin-x64 | `0.2.123` | `0.2.126` |
| @anthropic-ai/claude-agent-sdk-linux-arm64 | `0.2.123` | `0.2.126` |
| @anthropic-ai/claude-agent-sdk-linux-arm64-musl | `0.2.123` |
`0.2.126` |
| @anthropic-ai/claude-agent-sdk-linux-x64 | `0.2.123` | `0.2.126` |
| @anthropic-ai/claude-agent-sdk-linux-x64-musl | `0.2.123` | `0.2.126`
|
| @anthropic-ai/claude-agent-sdk-win32-arm64 | `0.2.123` | `0.2.126` |
| @anthropic-ai/claude-agent-sdk-win32-x64 | `0.2.123` | `0.2.126` |
| [postcss](https://github.com/postcss/postcss) | `8.5.12` | `8.5.13` |

Updates `@anthropic-ai/claude-agent-sdk` from 0.2.123 to 0.2.126
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/anthropics/claude-agent-sdk-typescript/releases"><code>@​anthropic-ai/claude-agent-sdk</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v0.2.126</h2>
<h2>What's changed</h2>
<ul>
<li>Added <code>origin</code> to result messages
(<code>SDKResultSuccess</code> / <code>SDKResultError</code>) — forwards
the triggering message's <code>SDKMessageOrigin</code> so consumers can
distinguish user-prompted results from <code>task-notification</code>
followups</li>
</ul>
<h2>Update</h2>
<pre lang="sh"><code>npm install @anthropic-ai/claude-agent-sdk@0.2.126
# or
yarn add @anthropic-ai/claude-agent-sdk@0.2.126
# or
pnpm add @anthropic-ai/claude-agent-sdk@0.2.126
# or
bun add @anthropic-ai/claude-agent-sdk@0.2.126
</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"><code>@​anthropic-ai/claude-agent-sdk</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>0.2.126</h2>
<ul>
<li>Added <code>origin</code> to result messages
(<code>SDKResultSuccess</code> / <code>SDKResultError</code>) — forwards
the triggering message's <code>SDKMessageOrigin</code> so consumers can
distinguish user-prompted results from <code>task-notification</code>
followups</li>
</ul>
<h2>0.2.125</h2>
<ul>
<li>Updated to parity with Claude Code v2.1.125</li>
</ul>
<h2>0.2.124</h2>
<ul>
<li>Updated to parity with Claude Code v2.1.124</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="36c350af3e"><code>36c350a</code></a>
chore: Update CHANGELOG.md</li>
<li>See full diff in <a
href="https://github.com/anthropics/claude-agent-sdk-typescript/compare/v0.2.123...v0.2.126">compare
view</a></li>
</ul>
</details>
<br />

Updates `eslint` from 10.2.1 to 10.3.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.3.0</h2>
<h2>Features</h2>
<ul>
<li><a
href="379571a975"><code>379571a</code></a>
feat: add suggestions for no-unused-private-class-members (<a
href="https://redirect.github.com/eslint/eslint/issues/20773">#20773</a>)
(sethamus)</li>
</ul>
<h2>Bug Fixes</h2>
<ul>
<li><a
href="b6ae5cf07b"><code>b6ae5cf</code></a>
fix: handle unavailable require cache (<a
href="https://redirect.github.com/eslint/eslint/issues/20812">#20812</a>)
(Simon Podlipsky)</li>
<li><a
href="6fb3685bcb"><code>6fb3685</code></a>
fix: rule suggestions cause continuation in class body (<a
href="https://redirect.github.com/eslint/eslint/issues/20787">#20787</a>)
(Milos Djermanovic)</li>
</ul>
<h2>Documentation</h2>
<ul>
<li><a
href="32cc7ab4ec"><code>32cc7ab</code></a>
docs: fix typos in docs and comments (<a
href="https://redirect.github.com/eslint/eslint/issues/20809">#20809</a>)
(Tanuj Kanti)</li>
<li><a
href="7f479376a2"><code>7f47937</code></a>
docs: Update README (GitHub Actions Bot)</li>
</ul>
<h2>Chores</h2>
<ul>
<li><a
href="d32235ec19"><code>d32235e</code></a>
ci: use pnpm in <code>eslint-flat-config-utils</code> type integration
test (<a
href="https://redirect.github.com/eslint/eslint/issues/20826">#20826</a>)
(Francesco Trotta)</li>
<li><a
href="3ffb14ea51"><code>3ffb14e</code></a>
chore: clean up typos in comments and JSDoc (<a
href="https://redirect.github.com/eslint/eslint/issues/20821">#20821</a>)
(Pixel998)</li>
<li><a
href="22eb58a21c"><code>22eb58a</code></a>
chore: add missing continue-on-error to ecosystem-tests.yml (<a
href="https://redirect.github.com/eslint/eslint/issues/20818">#20818</a>)
(Josh Goldberg ✨)</li>
<li><a
href="88bf0024cb"><code>88bf002</code></a>
ci: bump pnpm/action-setup from 6.0.1 to 6.0.3 (<a
href="https://redirect.github.com/eslint/eslint/issues/20815">#20815</a>)
(dependabot[bot])</li>
<li><a
href="97c8c330be"><code>97c8c33</code></a>
chore: update ilshidur/action-discord action to v0.4.0 (<a
href="https://redirect.github.com/eslint/eslint/issues/20811">#20811</a>)
(renovate[bot])</li>
<li><a
href="2f58136dd4"><code>2f58136</code></a>
chore: pin peter-evans/create-pull-request action to 5f6978f (<a
href="https://redirect.github.com/eslint/eslint/issues/20810">#20810</a>)
(renovate[bot])</li>
<li><a
href="77add7f1bc"><code>77add7f</code></a>
chore: add initial ecosystem plugin tests workflow (<a
href="https://redirect.github.com/eslint/eslint/issues/19643">#19643</a>)
(Josh Goldberg ✨)</li>
<li><a
href="4023b55490"><code>4023b55</code></a>
test: Add unit tests for SuppressionsService.prune() (<a
href="https://redirect.github.com/eslint/eslint/issues/20797">#20797</a>)
(kuldeep kumar)</li>
<li><a
href="54080dad4f"><code>54080da</code></a>
test: add unit tests for ForkContext (<a
href="https://redirect.github.com/eslint/eslint/issues/20778">#20778</a>)
(kuldeep kumar)</li>
<li><a
href="f0e2bcc4bf"><code>f0e2bcc</code></a>
test: add unit tests for SuppressionsService.suppress() method (<a
href="https://redirect.github.com/eslint/eslint/issues/20765">#20765</a>)
(kuldeep kumar)</li>
<li><a
href="a7f0b94743"><code>a7f0b94</code></a>
chore: update dependency prettier to v3.8.3 (<a
href="https://redirect.github.com/eslint/eslint/issues/20782">#20782</a>)
(renovate[bot])</li>
<li><a
href="7bf93d9e79"><code>7bf93d9</code></a>
chore: update TypeScript to v6 (<a
href="https://redirect.github.com/eslint/eslint/issues/20677">#20677</a>)
(sethamus)</li>
<li><a
href="b42dd72e76"><code>b42dd72</code></a>
ci: bump pnpm/action-setup from 6.0.0 to 6.0.1 (<a
href="https://redirect.github.com/eslint/eslint/issues/20781">#20781</a>)
(dependabot[bot])</li>
<li><a
href="2b252be80f"><code>2b252be</code></a>
test: add unit tests for IdGenerator (<a
href="https://redirect.github.com/eslint/eslint/issues/20775">#20775</a>)
(kuldeep kumar)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="78892043a3"><code>7889204</code></a>
10.3.0</li>
<li><a
href="5b69b4fce4"><code>5b69b4f</code></a>
Build: changelog update for 10.3.0</li>
<li><a
href="d32235ec19"><code>d32235e</code></a>
ci: use pnpm in <code>eslint-flat-config-utils</code> type integration
test (<a
href="https://redirect.github.com/eslint/eslint/issues/20826">#20826</a>)</li>
<li><a
href="b6ae5cf07b"><code>b6ae5cf</code></a>
fix: handle unavailable require cache (<a
href="https://redirect.github.com/eslint/eslint/issues/20812">#20812</a>)</li>
<li><a
href="3ffb14ea51"><code>3ffb14e</code></a>
chore: clean up typos in comments and JSDoc (<a
href="https://redirect.github.com/eslint/eslint/issues/20821">#20821</a>)</li>
<li><a
href="6fb3685bcb"><code>6fb3685</code></a>
fix: rule suggestions cause continuation in class body (<a
href="https://redirect.github.com/eslint/eslint/issues/20787">#20787</a>)</li>
<li><a
href="22eb58a21c"><code>22eb58a</code></a>
chore: add missing continue-on-error to ecosystem-tests.yml (<a
href="https://redirect.github.com/eslint/eslint/issues/20818">#20818</a>)</li>
<li><a
href="88bf0024cb"><code>88bf002</code></a>
ci: bump pnpm/action-setup from 6.0.1 to 6.0.3 (<a
href="https://redirect.github.com/eslint/eslint/issues/20815">#20815</a>)</li>
<li><a
href="379571a975"><code>379571a</code></a>
feat: add suggestions for no-unused-private-class-members (<a
href="https://redirect.github.com/eslint/eslint/issues/20773">#20773</a>)</li>
<li><a
href="97c8c330be"><code>97c8c33</code></a>
chore: update ilshidur/action-discord action to v0.4.0 (<a
href="https://redirect.github.com/eslint/eslint/issues/20811">#20811</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/eslint/eslint/compare/v10.2.1...v10.3.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `globals` from 17.5.0 to 17.6.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/sindresorhus/globals/releases">globals's
releases</a>.</em></p>
<blockquote>
<h2>v17.6.0</h2>
<ul>
<li>Update globals (2026-05-01) (<a
href="https://redirect.github.com/sindresorhus/globals/issues/343">#343</a>)
00a4dd9</li>
</ul>
<hr />
<p><a
href="https://github.com/sindresorhus/globals/compare/v17.5.0...v17.6.0">https://github.com/sindresorhus/globals/compare/v17.5.0...v17.6.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="6b15870f1c"><code>6b15870</code></a>
17.6.0</li>
<li><a
href="00a4dd9821"><code>00a4dd9</code></a>
Update globals (2026-05-01) (<a
href="https://redirect.github.com/sindresorhus/globals/issues/343">#343</a>)</li>
<li>See full diff in <a
href="https://github.com/sindresorhus/globals/compare/v17.5.0...v17.6.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `@anthropic-ai/claude-agent-sdk-darwin-arm64` from 0.2.123 to
0.2.126

Updates `@anthropic-ai/claude-agent-sdk-darwin-x64` from 0.2.123 to
0.2.126

Updates `@anthropic-ai/claude-agent-sdk-linux-arm64` from 0.2.123 to
0.2.126

Updates `@anthropic-ai/claude-agent-sdk-linux-arm64-musl` from 0.2.123
to 0.2.126

Updates `@anthropic-ai/claude-agent-sdk-linux-x64` from 0.2.123 to
0.2.126

Updates `@anthropic-ai/claude-agent-sdk-linux-x64-musl` from 0.2.123 to
0.2.126

Updates `@anthropic-ai/claude-agent-sdk-win32-arm64` from 0.2.123 to
0.2.126

Updates `@anthropic-ai/claude-agent-sdk-win32-x64` from 0.2.123 to
0.2.126

Updates `postcss` from 8.5.12 to 8.5.13
<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.13</h2>
<ul>
<li>Fixed <code>postcss-scss</code> commend regression.</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.13</h2>
<ul>
<li>Fixed <code>postcss-scss</code> commend regression.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="af58cf1b7a"><code>af58cf1</code></a>
Release 8.5.13 version</li>
<li><a
href="f227dbd0e9"><code>f227dbd</code></a>
Temporary ignore pnpm 11 config</li>
<li><a
href="d3abd40d72"><code>d3abd40</code></a>
Update dependencies</li>
<li><a
href="dd06c3e113"><code>dd06c3e</code></a>
Revert stringifier changes because of the conflict with
postcss-scss</li>
<li><a
href="ae889c815f"><code>ae889c8</code></a>
Try to fix CI</li>
<li><a
href="e0093e49bc"><code>e0093e4</code></a>
Move to pnpm 11</li>
<li>See full diff in <a
href="https://github.com/postcss/postcss/compare/8.5.12...8.5.13">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-05-03 23:55:27 +02:00
Ben Brandt
1f4d6c2900 test: Fix noisy tests (#626) 2026-05-03 21:52:06 +00:00
davethedev3
8d7e22026e fix: emit a real diff when Write overwrites an existing file (#618)
## What's broken

The PostToolUse hook fan-out (`src/acp-agent.ts`) only invokes
`toolUpdateFromEditToolResponse` for tools named `Edit`. Any `Write`
that overwrites an existing file (`FileWriteOutput.type === "update"`)
keeps the optimistic content built at tool-use time by
`toolInfoFromToolUse` — `oldText: null, newText: input.content` —
permanently. The user sees a "creation" diff regardless of whether the
file already existed, so overwrites visually look indistinguishable from
brand-new files.

This affects every Write that touches an existing file, which Claude
does routinely (rewriting configs, replacing small files, etc.).

## Fix

The SDK's `FileWriteOutput`
(`@anthropic-ai/claude-agent-sdk/sdk-tools.d.ts`) ships the same
hunk-shaped `structuredPatch` for `Write` that we already render for
`Edit`. Extending the dispatch to cover both tools replays the hook's
payload through `toolUpdateFromEditToolResponse` so update mode shows
the actual diff.

Create mode is unchanged in practice because the structuredPatch hunk's
lines are all additions, which collapse to the same `oldText: null,
newText: <content>` shape the optimistic path produced.

## Tests

Two regression tests added in `src/tests/tools.test.ts` under a new
`describe("post-tool-use hook sends diff content for Write tool")`:

- `Write` with `type: "update"` asserts the corrected diff (real
`oldText` / `newText` derived from `structuredPatch`)
- `Write` with `type: "create"` asserts the existing optimistic-style
diff is preserved

All existing tests pass. Total: 240 → 242.

## Validation

- `npm run check` ✅
- `npm run build` ✅
- `npm run test:run` ✅ (242 passed, 13 integration skipped)

---------

Co-authored-by: dave <dave@mint.lan>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
2026-05-03 23:44:47 +02:00
dependabot[bot]
c78ac62d05 feat(deps-dev): Bump nanoid from 3.3.11 to 3.3.12 (#622)
Bumps [nanoid](https://github.com/ai/nanoid) from 3.3.11 to 3.3.12.
<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.12</h2>
<ul>
<li>Fixed breaking Nano ID by requesting big ID.</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.12</h2>
<ul>
<li>Fixed breaking Nano ID by requesting big ID.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="aa9c399f69"><code>aa9c399</code></a>
Release 3.3.12 version</li>
<li><a
href="b0036ed60d"><code>b0036ed</code></a>
Backport fix to CommonJS version too</li>
<li><a
href="08a19a4c82"><code>08a19a4</code></a>
Update test</li>
<li><a
href="821dfed7b5"><code>821dfed</code></a>
Backport pool breaking fix</li>
<li><a
href="609646c644"><code>609646c</code></a>
Fix CI</li>
<li>See full diff in <a
href="https://github.com/ai/nanoid/compare/3.3.11...3.3.12">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.11&new-version=3.3.12)](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-05-01 19:44:59 +02:00
dependabot[bot]
6ccd37ce94 feat(deps): Bump the minor group with 2 updates (#619)
Bumps the minor group with 2 updates:
[zod](https://github.com/colinhacks/zod) and
[hono](https://github.com/honojs/hono).

Updates `zod` from 4.3.6 to 4.4.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/colinhacks/zod/releases">zod's
releases</a>.</em></p>
<blockquote>
<h2>v4.4.1</h2>
<h2>Commits:</h2>
<ul>
<li>481f7be4238c83ed58183f921b2646f340a91c6a ci: gate release publishing
on full test workflow</li>
<li>95ccab423aec720b2523c3a64cdc7e3204537cc7 test(v3): restore optional
undefined expectations</li>
<li>cede2c63739a5823d6aa5093d291e9a111da943d fix(v4): reject tuple holes
before required defaults (<a
href="https://redirect.github.com/colinhacks/zod/issues/5900">#5900</a>)</li>
<li>edd0bf0f5ada4a8dc581c259407d7bbad0a71ea7 release: 4.4.1</li>
<li>180d83d1dbe6a59260710cc8637a3dea2281ee56 docs: remove Jazz featured
sponsor</li>
</ul>
<h2>v4.4.0</h2>
<h2>4.4.0</h2>
<p>This is a minor release with a wide set of correctness and soundness
fixes. Some fixes intentionally make Zod stricter, so code that depended
on previously accepted invalid or ambiguous inputs may need small
updates.</p>
<h2>Potentially breaking bug fixes</h2>
<h3>Tuple defaults now materialize output values correctly</h3>
<p>Fixed in <a
href="https://redirect.github.com/colinhacks/zod/pull/5661">#5661</a>.
Tuple parsing now more accurately reflects defaults, optional tails,
explicit <code>undefined</code>, and under-filled inputs. The headline
behavior is that defaults in tuple positions now properly appear in
parsed output.</p>
<pre lang="ts"><code>const schema = z.tuple([
  z.string(),
  z.string().default(&quot;fallback&quot;),
]);
<p>schema.parse([&quot;a&quot;]);
// [&quot;a&quot;, &quot;fallback&quot;]
</code></pre></p>
<p>Trailing optional elements that are absent still stay absent; they
are not filled with <code>undefined</code>.</p>
<pre lang="ts"><code>const schema = z.tuple([
  z.string(),
  z.string().optional(),
]);
<p>schema.parse([&quot;a&quot;]);
// [&quot;a&quot;]
</code></pre></p>
<p>But explicit <code>undefined</code> values supplied by the caller are
preserved.</p>
<pre lang="ts"><code>schema.parse([&quot;a&quot;, undefined]);
// [&quot;a&quot;, undefined]
</code></pre>
<p>When optional elements appear before later defaults, the parsed tuple
is now dense so array operations behave predictably.</p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="180d83d1db"><code>180d83d</code></a>
docs: remove Jazz featured sponsor</li>
<li><a
href="edd0bf0f5a"><code>edd0bf0</code></a>
release: 4.4.1</li>
<li><a
href="cede2c6373"><code>cede2c6</code></a>
fix(v4): reject tuple holes before required defaults (<a
href="https://redirect.github.com/colinhacks/zod/issues/5900">#5900</a>)</li>
<li><a
href="95ccab423a"><code>95ccab4</code></a>
test(v3): restore optional undefined expectations</li>
<li><a
href="481f7be423"><code>481f7be</code></a>
ci: gate release publishing on full test workflow</li>
<li><a
href="d05f026e9e"><code>d05f026</code></a>
release: 4.4.0</li>
<li><a
href="f778e02a81"><code>f778e02</code></a>
build: bump zshy for JSR wildcard exports</li>
<li><a
href="6db607be3c"><code>6db607b</code></a>
fix(release): keep JSR manifest publishable</li>
<li><a
href="ad0b82713e"><code>ad0b827</code></a>
ci: update release workflow for trusted publishing</li>
<li><a
href="b6066b3e47"><code>b6066b3</code></a>
fix(v4): align object and tuple optionality handling (<a
href="https://redirect.github.com/colinhacks/zod/issues/5661">#5661</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/colinhacks/zod/compare/v4.3.6...v4.4.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 zod since your current version.</p>
</details>
<br />

Updates `hono` from 4.12.15 to 4.12.16
<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.16</h2>
<h2>Security fixes</h2>
<p>This release includes fixes for the following security issues:</p>
<h3>Unvalidated JSX Tag Names in hono/jsx May Allow HTML Injection</h3>
<p>Affects: hono/jsx. Fixes missing validation of JSX tag names when
using <code>jsx()</code> or <code>createElement()</code>, which could
allow HTML injection if untrusted input is used as the tag name.
GHSA-69xw-7hcm-h432</p>
<h3>bodyLimit() can be bypassed for chunked / unknown-length
requests</h3>
<p>Affects: Body Limit Middleware. Fixes late enforcement for request
bodies without a reliable Content-Length (e.g. chunked requests), where
oversized requests could reach handlers and return successful responses
before being rejected. GHSA-9vqf-7f2p-gf9v</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="90d4182aab"><code>90d4182</code></a>
4.12.16</li>
<li><a
href="db05b96d7a"><code>db05b96</code></a>
Merge commit from fork</li>
<li><a
href="614b834551"><code>614b834</code></a>
Merge commit from fork</li>
<li><a
href="027e3dfca9"><code>027e3df</code></a>
fix(method-override): handle Content-Type with charset parameter (<a
href="https://redirect.github.com/honojs/hono/issues/4894">#4894</a>)</li>
<li>See full diff in <a
href="https://github.com/honojs/hono/compare/v4.12.15...v4.12.16">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-05-01 02:44:51 +02:00
dependabot[bot]
5b9311938d feat(deps): Update to @anthropic-ai/claude-agent-sdk@0.2.123 (#614)
Bumps the minor group with 11 updates:

| Package | From | To |
| --- | --- | --- |
|
[@anthropic-ai/claude-agent-sdk](https://github.com/anthropics/claude-agent-sdk-typescript)
| `0.2.121` | `0.2.123` |
|
[@anthropic-ai/sdk](https://github.com/anthropics/anthropic-sdk-typescript)
| `0.89.0` | `0.91.1` |
| @anthropic-ai/claude-agent-sdk-darwin-arm64 | `0.2.121` | `0.2.123` |
| @anthropic-ai/claude-agent-sdk-darwin-x64 | `0.2.121` | `0.2.123` |
| @anthropic-ai/claude-agent-sdk-linux-arm64 | `0.2.121` | `0.2.123` |
| @anthropic-ai/claude-agent-sdk-linux-arm64-musl | `0.2.121` |
`0.2.123` |
| @anthropic-ai/claude-agent-sdk-linux-x64 | `0.2.121` | `0.2.123` |
| @anthropic-ai/claude-agent-sdk-linux-x64-musl | `0.2.121` | `0.2.123`
|
| @anthropic-ai/claude-agent-sdk-win32-arm64 | `0.2.121` | `0.2.123` |
| @anthropic-ai/claude-agent-sdk-win32-x64 | `0.2.121` | `0.2.123` |
| [tinyexec](https://github.com/tinylibs/tinyexec) | `1.1.1` | `1.1.2` |

Updates `@anthropic-ai/claude-agent-sdk` from 0.2.121 to 0.2.123
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/anthropics/claude-agent-sdk-typescript/releases"><code>@​anthropic-ai/claude-agent-sdk</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v0.2.123</h2>
<h2>What's changed</h2>
<ul>
<li>Updated to parity with Claude Code v2.1.123</li>
</ul>
<h2>Update</h2>
<pre lang="sh"><code>npm install @anthropic-ai/claude-agent-sdk@0.2.123
# or
yarn add @anthropic-ai/claude-agent-sdk@0.2.123
# or
pnpm add @anthropic-ai/claude-agent-sdk@0.2.123
# or
bun add @anthropic-ai/claude-agent-sdk@0.2.123
</code></pre>
<h2>v0.2.122</h2>
<h2>What's changed</h2>
<ul>
<li>Updated to parity with Claude Code v2.1.122</li>
</ul>
<h2>Update</h2>
<pre lang="sh"><code>npm install @anthropic-ai/claude-agent-sdk@0.2.122
# or
yarn add @anthropic-ai/claude-agent-sdk@0.2.122
# or
pnpm add @anthropic-ai/claude-agent-sdk@0.2.122
# or
bun add @anthropic-ai/claude-agent-sdk@0.2.122
</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"><code>@​anthropic-ai/claude-agent-sdk</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>0.2.123</h2>
<ul>
<li>Updated to parity with Claude Code v2.1.123</li>
</ul>
<h2>0.2.122</h2>
<ul>
<li>Updated to parity with Claude Code v2.1.122</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="38cd292318"><code>38cd292</code></a>
chore: Update CHANGELOG.md</li>
<li><a
href="5f07f15d5d"><code>5f07f15</code></a>
chore: Update CHANGELOG.md</li>
<li>See full diff in <a
href="https://github.com/anthropics/claude-agent-sdk-typescript/compare/v0.2.121...v0.2.123">compare
view</a></li>
</ul>
</details>
<br />

Updates `@anthropic-ai/sdk` from 0.89.0 to 0.91.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/anthropics/anthropic-sdk-typescript/releases"><code>@​anthropic-ai/sdk</code>'s
releases</a>.</em></p>
<blockquote>
<h2>sdk: v0.91.1</h2>
<h2>0.91.1 (2026-04-24)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.91.0...sdk-v0.91.1">sdk-v0.91.0...sdk-v0.91.1</a></p>
<h3>Bug Fixes</h3>
<ul>
<li><strong>memory:</strong> use restrictive file mode for memory files
(<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/901">#901</a>)
(<a
href="6db3b7e221">6db3b7e</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><strong>formatter:</strong> run prettier and eslint separately (<a
href="974d22fcf6">974d22f</a>)</li>
</ul>
<h2>sdk: v0.91.0</h2>
<h2>0.91.0 (2026-04-23)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.90.0...sdk-v0.91.0">sdk-v0.90.0...sdk-v0.91.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> CMA Memory public beta (<a
href="ddf732f5f3">ddf732f</a>)</li>
<li><strong>bedrock:</strong> use auth header for mantle client (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/866">#866</a>)
(<a
href="aec801afc7">aec801a</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>api:</strong> fix errors in api spec (<a
href="ae10768485">ae10768</a>)</li>
<li><strong>api:</strong> restore missing features (<a
href="1a5b47b689">1a5b47b</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><strong>internal:</strong> more robust bootstrap script (<a
href="7716e19f7f">7716e19</a>)</li>
<li><strong>tests:</strong> bump steady to v0.22.1 (<a
href="219a971743">219a971</a>)</li>
</ul>
<h2>sdk: v0.90.0</h2>
<h2>0.90.0 (2026-04-16)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.89.0...sdk-v0.90.0">sdk-v0.89.0...sdk-v0.90.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> add claude-opus-4-7, token budgets and
user_profiles (<a
href="b26134bb17">b26134b</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li>actually delete release-doctor.yml (<a
href="0fe984d1f6">0fe984d</a>)</li>
<li><strong>ci:</strong> remove release-doctor workflow (<a
href="08e58bd0bb">08e58bd</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"><code>@​anthropic-ai/sdk</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>0.91.1 (2026-04-24)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.91.0...sdk-v0.91.1">sdk-v0.91.0...sdk-v0.91.1</a></p>
<h3>Bug Fixes</h3>
<ul>
<li><strong>memory:</strong> use restrictive file mode for memory files
(<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/901">#901</a>)
(<a
href="6db3b7e221">6db3b7e</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><strong>formatter:</strong> run prettier and eslint separately (<a
href="974d22fcf6">974d22f</a>)</li>
</ul>
<h2>0.91.0 (2026-04-23)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.90.0...sdk-v0.91.0">sdk-v0.90.0...sdk-v0.91.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> CMA Memory public beta (<a
href="ddf732f5f3">ddf732f</a>)</li>
<li><strong>bedrock:</strong> use auth header for mantle client (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/866">#866</a>)
(<a
href="aec801afc7">aec801a</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>api:</strong> fix errors in api spec (<a
href="ae10768485">ae10768</a>)</li>
<li><strong>api:</strong> restore missing features (<a
href="1a5b47b689">1a5b47b</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><strong>internal:</strong> more robust bootstrap script (<a
href="7716e19f7f">7716e19</a>)</li>
<li><strong>tests:</strong> bump steady to v0.22.1 (<a
href="219a971743">219a971</a>)</li>
</ul>
<h2>0.90.0 (2026-04-16)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.89.0...sdk-v0.90.0">sdk-v0.89.0...sdk-v0.90.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> add claude-opus-4-7, token budgets and
user_profiles (<a
href="b26134bb17">b26134b</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li>actually delete release-doctor.yml (<a
href="0fe984d1f6">0fe984d</a>)</li>
<li><strong>ci:</strong> remove release-doctor workflow (<a
href="08e58bd0bb">08e58bd</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="74ac150346"><code>74ac150</code></a>
chore: release main (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/1014">#1014</a>)</li>
<li><a
href="22cb810364"><code>22cb810</code></a>
chore: release main (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/1008">#1008</a>)</li>
<li><a
href="93ac7c7e05"><code>93ac7c7</code></a>
chore: release main (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/1003">#1003</a>)</li>
<li>See full diff in <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.89.0...sdk-v0.91.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `@anthropic-ai/claude-agent-sdk-darwin-arm64` from 0.2.121 to
0.2.123

Updates `@anthropic-ai/claude-agent-sdk-darwin-x64` from 0.2.121 to
0.2.123

Updates `@anthropic-ai/claude-agent-sdk-linux-arm64` from 0.2.121 to
0.2.123

Updates `@anthropic-ai/claude-agent-sdk-linux-arm64-musl` from 0.2.121
to 0.2.123

Updates `@anthropic-ai/claude-agent-sdk-linux-x64` from 0.2.121 to
0.2.123

Updates `@anthropic-ai/claude-agent-sdk-linux-x64-musl` from 0.2.121 to
0.2.123

Updates `@anthropic-ai/claude-agent-sdk-win32-arm64` from 0.2.121 to
0.2.123

Updates `@anthropic-ai/claude-agent-sdk-win32-x64` from 0.2.121 to
0.2.123

Updates `tinyexec` from 1.1.1 to 1.1.2
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/tinylibs/tinyexec/releases">tinyexec's
releases</a>.</em></p>
<blockquote>
<h2>1.1.2</h2>
<h2>What's Changed</h2>
<ul>
<li>feat: accept readonly string[] for args parameters by <a
href="https://github.com/tatzyr"><code>@​tatzyr</code></a> in <a
href="https://redirect.github.com/tinylibs/tinyexec/pull/112">tinylibs/tinyexec#112</a></li>
<li>chore(deps-dev): bump the development-dependencies group with 7
updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/tinylibs/tinyexec/pull/113">tinylibs/tinyexec#113</a></li>
<li>fix: Fix typo in <code>README</code> by <a
href="https://github.com/porada"><code>@​porada</code></a> in <a
href="https://redirect.github.com/tinylibs/tinyexec/pull/114">tinylibs/tinyexec#114</a></li>
<li>chore(deps-dev): bump the development-dependencies group with 5
updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/tinylibs/tinyexec/pull/116">tinylibs/tinyexec#116</a></li>
<li>chore(deps): bump actions/setup-node from 6.3.0 to 6.4.0 in the
github-actions group by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/tinylibs/tinyexec/pull/115">tinylibs/tinyexec#115</a></li>
<li>chore(deps-dev): bump the development-dependencies group with 4
updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/tinylibs/tinyexec/pull/117">tinylibs/tinyexec#117</a></li>
<li>fix: remove normalisation of commands by <a
href="https://github.com/43081j"><code>@​43081j</code></a> in <a
href="https://redirect.github.com/tinylibs/tinyexec/pull/118">tinylibs/tinyexec#118</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/tatzyr"><code>@​tatzyr</code></a> made
their first contribution in <a
href="https://redirect.github.com/tinylibs/tinyexec/pull/112">tinylibs/tinyexec#112</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/tinylibs/tinyexec/compare/1.1.1...1.1.2">https://github.com/tinylibs/tinyexec/compare/1.1.1...1.1.2</a></p>
<h2>1.1.2-next.0</h2>
<h2>What's Changed</h2>
<ul>
<li>feat: accept readonly string[] for args parameters by <a
href="https://github.com/tatzyr"><code>@​tatzyr</code></a> in <a
href="https://redirect.github.com/tinylibs/tinyexec/pull/112">tinylibs/tinyexec#112</a></li>
<li>chore(deps-dev): bump the development-dependencies group with 7
updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/tinylibs/tinyexec/pull/113">tinylibs/tinyexec#113</a></li>
<li>fix: Fix typo in <code>README</code> by <a
href="https://github.com/porada"><code>@​porada</code></a> in <a
href="https://redirect.github.com/tinylibs/tinyexec/pull/114">tinylibs/tinyexec#114</a></li>
<li>chore(deps-dev): bump the development-dependencies group with 5
updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/tinylibs/tinyexec/pull/116">tinylibs/tinyexec#116</a></li>
<li>chore(deps): bump actions/setup-node from 6.3.0 to 6.4.0 in the
github-actions group by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/tinylibs/tinyexec/pull/115">tinylibs/tinyexec#115</a></li>
<li>chore(deps-dev): bump the development-dependencies group with 4
updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/tinylibs/tinyexec/pull/117">tinylibs/tinyexec#117</a></li>
<li>fix: remove normalisation of commands by <a
href="https://github.com/43081j"><code>@​43081j</code></a> in <a
href="https://redirect.github.com/tinylibs/tinyexec/pull/118">tinylibs/tinyexec#118</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/tatzyr"><code>@​tatzyr</code></a> made
their first contribution in <a
href="https://redirect.github.com/tinylibs/tinyexec/pull/112">tinylibs/tinyexec#112</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/tinylibs/tinyexec/compare/1.1.1...1.1.2-next.0">https://github.com/tinylibs/tinyexec/compare/1.1.1...1.1.2-next.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="66f20383d8"><code>66f2038</code></a>
fix: remove normalisation of commands (<a
href="https://redirect.github.com/tinylibs/tinyexec/issues/118">#118</a>)</li>
<li><a
href="7a412e2c4b"><code>7a412e2</code></a>
chore(deps-dev): bump the development-dependencies group with 4 updates
(<a
href="https://redirect.github.com/tinylibs/tinyexec/issues/117">#117</a>)</li>
<li><a
href="bb8e95baeb"><code>bb8e95b</code></a>
chore(deps): bump actions/setup-node in the github-actions group (<a
href="https://redirect.github.com/tinylibs/tinyexec/issues/115">#115</a>)</li>
<li><a
href="2eb8aa1089"><code>2eb8aa1</code></a>
chore(deps-dev): bump the development-dependencies group with 5 updates
(<a
href="https://redirect.github.com/tinylibs/tinyexec/issues/116">#116</a>)</li>
<li><a
href="64d77c1c95"><code>64d77c1</code></a>
chore: fix typo in readme (<a
href="https://redirect.github.com/tinylibs/tinyexec/issues/114">#114</a>)</li>
<li><a
href="d5443a19f3"><code>d5443a1</code></a>
chore(deps-dev): bump the development-dependencies group with 7 updates
(<a
href="https://redirect.github.com/tinylibs/tinyexec/issues/113">#113</a>)</li>
<li><a
href="76f9e38477"><code>76f9e38</code></a>
feat: accept readonly string[] for args parameters (<a
href="https://redirect.github.com/tinylibs/tinyexec/issues/112">#112</a>)</li>
<li>See full diff in <a
href="https://github.com/tinylibs/tinyexec/compare/1.1.1...1.1.2">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>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 18:22:42 +02:00
acp-release-bot[bot]
9957b54882 chore(main): release 0.31.4 (#611)
🤖 I have created a release *beep* *boop*
---


##
[0.31.4](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.31.3...v0.31.4)
(2026-04-28)


### Bug Fixes

* gate auto mode on model support
([#604](https://github.com/agentclientprotocol/claude-agent-acp/issues/604))
([ec47d34](ec47d3446f))

---
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-04-28 18:10:28 +02:00
davethedev3
ec47d3446f fix: gate auto mode on model support (#604)
## Summary

Closes #594

Only advertise and accept `auto` permission mode when the active SDK
model reports `supportsAutoMode: true`.

This fixes the Haiku path where clients could select `auto`, only for
the SDK to later reject it with `auto mode unavailable for this model`.

Changes included:

- build available permission modes from the active model's `ModelInfo`
- omit `auto` from `availableModes` when `supportsAutoMode` is absent or
false
- clamp `permissions.defaultMode: "auto"` to `default` when the resolved
model cannot use auto mode
- recompute and clamp the current mode on model switch
- reject direct `setSessionMode("auto")` when `auto` is not available
- filter `ExitPlanMode` choices so unavailable modes are not offered or
accepted
- add regression coverage for session creation, model switching, direct
mode setting, and `ExitPlanMode`

## Verification

- `npm run lint`
- `npx prettier --check src/acp-agent.ts
src/tests/acp-agent-settings.test.ts src/tests/acp-agent.test.ts
src/tests/session-config-options.test.ts`
- `npm run build`
- `npm run test:run`
- Manual ACP subprocess smoke test with `ANTHROPIC_MODEL=haiku`
  - `auto` was not advertised
  - `setSessionMode("auto")` was rejected before reaching the SDK

---------

Co-authored-by: dave <dave@mint.lan>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
2026-04-28 16:08:46 +00:00
acp-release-bot[bot]
546040ffe7 chore(main): release 0.31.3 (#610)
🤖 I have created a release *beep* *boop*
---


##
[0.31.3](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.31.2...v0.31.3)
(2026-04-28)


### Bug Fixes

* Rename effort config category to thought_level
([#609](https://github.com/agentclientprotocol/claude-agent-acp/issues/609))
([76b4e96](76b4e96508))

---
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-04-28 16:59:32 +02:00
dependabot[bot]
a52ab4ab64 chore(deps): bump the minor group across 1 directory with 25 updates (#608)
Bumps the minor group with 9 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
|
[@agentclientprotocol/sdk](https://github.com/agentclientprotocol/typescript-sdk)
| `0.20.0` | `0.21.0` |
|
[@anthropic-ai/claude-agent-sdk](https://github.com/anthropics/claude-agent-sdk-typescript)
| `0.2.119` | `0.2.121` |
|
[@anthropic-ai/sdk](https://github.com/anthropics/anthropic-sdk-typescript)
| `0.89.0` | `0.91.1` |
|
[@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin)
| `8.59.0` | `8.59.1` |
|
[@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser)
| `8.59.0` | `8.59.1` |
| [es-module-lexer](https://github.com/guybedford/es-module-lexer) |
`2.0.0` | `2.1.0` |
|
[express-rate-limit](https://github.com/express-rate-limit/express-rate-limit)
| `8.4.0` | `8.4.1` |
| [jose](https://github.com/panva/jose) | `6.2.2` | `6.2.3` |
| [postcss](https://github.com/postcss/postcss) | `8.5.10` | `8.5.12` |


Updates `@agentclientprotocol/sdk` from 0.20.0 to 0.21.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/agentclientprotocol/typescript-sdk/releases"><code>@​agentclientprotocol/sdk</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v0.21.0</h2>
<h2><a
href="https://github.com/agentclientprotocol/typescript-sdk/compare/v0.20.0...v0.21.0">0.21.0</a>
(2026-04-28)</h2>
<h3>Features</h3>
<ul>
<li><strong>unstable:</strong> Add <code>providers/*</code> support (<a
href="https://redirect.github.com/agentclientprotocol/typescript-sdk/issues/138">#138</a>)
(<a
href="e234c213d3">e234c21</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/agentclientprotocol/typescript-sdk/blob/main/CHANGELOG.md"><code>@​agentclientprotocol/sdk</code>'s
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/agentclientprotocol/typescript-sdk/compare/v0.20.0...v0.21.0">0.21.0</a>
(2026-04-28)</h2>
<h3>Features</h3>
<ul>
<li><strong>unstable:</strong> Add <code>providers/*</code> support (<a
href="https://redirect.github.com/agentclientprotocol/typescript-sdk/issues/138">#138</a>)
(<a
href="e234c213d3">e234c21</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="74372b59f8"><code>74372b5</code></a>
chore(main): release 0.21.0 (<a
href="https://redirect.github.com/agentclientprotocol/typescript-sdk/issues/139">#139</a>)</li>
<li><a
href="e234c213d3"><code>e234c21</code></a>
feat(unstable): Add <code>providers/*</code> support (<a
href="https://redirect.github.com/agentclientprotocol/typescript-sdk/issues/138">#138</a>)</li>
<li><a
href="b0e1d046a5"><code>b0e1d04</code></a>
chore(deps): bump actions/setup-node from 6.3.0 to 6.4.0 (<a
href="https://redirect.github.com/agentclientprotocol/typescript-sdk/issues/134">#134</a>)</li>
<li><a
href="c6ee38997c"><code>c6ee389</code></a>
chore(deps): bump googleapis/release-please-action from 4.4.1 to 5.0.0
(<a
href="https://redirect.github.com/agentclientprotocol/typescript-sdk/issues/135">#135</a>)</li>
<li><a
href="bd0b486fce"><code>bd0b486</code></a>
chore(deps): bump the minor group with 43 updates (<a
href="https://redirect.github.com/agentclientprotocol/typescript-sdk/issues/136">#136</a>)</li>
<li><a
href="3becf2ee80"><code>3becf2e</code></a>
chore(deps-dev): bump ajv from 6.14.0 to 6.15.0 (<a
href="https://redirect.github.com/agentclientprotocol/typescript-sdk/issues/137">#137</a>)</li>
<li>See full diff in <a
href="https://github.com/agentclientprotocol/typescript-sdk/compare/v0.20.0...v0.21.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `@anthropic-ai/claude-agent-sdk` from 0.2.119 to 0.2.121
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/anthropics/claude-agent-sdk-typescript/releases"><code>@​anthropic-ai/claude-agent-sdk</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v0.2.121</h2>
<h2>What's changed</h2>
<ul>
<li>Added <code>updatedToolOutput</code> to
<code>PostToolUseHookSpecificOutput</code> for replacing tool output on
all tools. <code>updatedMCPToolOutput</code> is deprecated.</li>
</ul>
<h2>Update</h2>
<pre lang="sh"><code>npm install @anthropic-ai/claude-agent-sdk@0.2.121
# or
yarn add @anthropic-ai/claude-agent-sdk@0.2.121
# or
pnpm add @anthropic-ai/claude-agent-sdk@0.2.121
# or
bun add @anthropic-ai/claude-agent-sdk@0.2.121
</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"><code>@​anthropic-ai/claude-agent-sdk</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>0.2.121</h2>
<ul>
<li>Added <code>updatedToolOutput</code> to
<code>PostToolUseHookSpecificOutput</code> for replacing tool output on
all tools. <code>updatedMCPToolOutput</code> is deprecated.</li>
</ul>
<h2>0.2.120</h2>
<ul>
<li>Added <code>skills</code> option (<code>string[] | 'all'</code>) to
control which Skills are loaded into the main session, matching the
Python SDK</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="49b6b0e827"><code>49b6b0e</code></a>
chore: Update CHANGELOG.md</li>
<li><a
href="2b1ffcc321"><code>2b1ffcc</code></a>
chore: Update CHANGELOG.md</li>
<li><a
href="48de067ca4"><code>48de067</code></a>
chore: Update CHANGELOG.md</li>
<li>See full diff in <a
href="https://github.com/anthropics/claude-agent-sdk-typescript/compare/v0.2.119...v0.2.121">compare
view</a></li>
</ul>
</details>
<br />

Updates `@anthropic-ai/sdk` from 0.89.0 to 0.91.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/anthropics/anthropic-sdk-typescript/releases"><code>@​anthropic-ai/sdk</code>'s
releases</a>.</em></p>
<blockquote>
<h2>sdk: v0.91.1</h2>
<h2>0.91.1 (2026-04-24)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.91.0...sdk-v0.91.1">sdk-v0.91.0...sdk-v0.91.1</a></p>
<h3>Bug Fixes</h3>
<ul>
<li><strong>memory:</strong> use restrictive file mode for memory files
(<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/901">#901</a>)
(<a
href="6db3b7e221">6db3b7e</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><strong>formatter:</strong> run prettier and eslint separately (<a
href="974d22fcf6">974d22f</a>)</li>
</ul>
<h2>sdk: v0.91.0</h2>
<h2>0.91.0 (2026-04-23)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.90.0...sdk-v0.91.0">sdk-v0.90.0...sdk-v0.91.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> CMA Memory public beta (<a
href="ddf732f5f3">ddf732f</a>)</li>
<li><strong>bedrock:</strong> use auth header for mantle client (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/866">#866</a>)
(<a
href="aec801afc7">aec801a</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>api:</strong> fix errors in api spec (<a
href="ae10768485">ae10768</a>)</li>
<li><strong>api:</strong> restore missing features (<a
href="1a5b47b689">1a5b47b</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><strong>internal:</strong> more robust bootstrap script (<a
href="7716e19f7f">7716e19</a>)</li>
<li><strong>tests:</strong> bump steady to v0.22.1 (<a
href="219a971743">219a971</a>)</li>
</ul>
<h2>sdk: v0.90.0</h2>
<h2>0.90.0 (2026-04-16)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.89.0...sdk-v0.90.0">sdk-v0.89.0...sdk-v0.90.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> add claude-opus-4-7, token budgets and
user_profiles (<a
href="b26134bb17">b26134b</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li>actually delete release-doctor.yml (<a
href="0fe984d1f6">0fe984d</a>)</li>
<li><strong>ci:</strong> remove release-doctor workflow (<a
href="08e58bd0bb">08e58bd</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"><code>@​anthropic-ai/sdk</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>0.91.1 (2026-04-24)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.91.0...sdk-v0.91.1">sdk-v0.91.0...sdk-v0.91.1</a></p>
<h3>Bug Fixes</h3>
<ul>
<li><strong>memory:</strong> use restrictive file mode for memory files
(<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/901">#901</a>)
(<a
href="6db3b7e221">6db3b7e</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><strong>formatter:</strong> run prettier and eslint separately (<a
href="974d22fcf6">974d22f</a>)</li>
</ul>
<h2>0.91.0 (2026-04-23)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.90.0...sdk-v0.91.0">sdk-v0.90.0...sdk-v0.91.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> CMA Memory public beta (<a
href="ddf732f5f3">ddf732f</a>)</li>
<li><strong>bedrock:</strong> use auth header for mantle client (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/866">#866</a>)
(<a
href="aec801afc7">aec801a</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>api:</strong> fix errors in api spec (<a
href="ae10768485">ae10768</a>)</li>
<li><strong>api:</strong> restore missing features (<a
href="1a5b47b689">1a5b47b</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><strong>internal:</strong> more robust bootstrap script (<a
href="7716e19f7f">7716e19</a>)</li>
<li><strong>tests:</strong> bump steady to v0.22.1 (<a
href="219a971743">219a971</a>)</li>
</ul>
<h2>0.90.0 (2026-04-16)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.89.0...sdk-v0.90.0">sdk-v0.89.0...sdk-v0.90.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> add claude-opus-4-7, token budgets and
user_profiles (<a
href="b26134bb17">b26134b</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li>actually delete release-doctor.yml (<a
href="0fe984d1f6">0fe984d</a>)</li>
<li><strong>ci:</strong> remove release-doctor workflow (<a
href="08e58bd0bb">08e58bd</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="74ac150346"><code>74ac150</code></a>
chore: release main (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/1014">#1014</a>)</li>
<li><a
href="22cb810364"><code>22cb810</code></a>
chore: release main (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/1008">#1008</a>)</li>
<li><a
href="93ac7c7e05"><code>93ac7c7</code></a>
chore: release main (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/1003">#1003</a>)</li>
<li>See full diff in <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.89.0...sdk-v0.91.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/eslint-plugin` from 8.59.0 to 8.59.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases"><code>@​typescript-eslint/eslint-plugin</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v8.59.1</h2>
<h2>8.59.1 (2026-04-27)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion] fix
crash &quot;TypeError: checker.getTypeArguments is not a function&quot;
(<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12246">#12246</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve index signatures in undefined unions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12257">#12257</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve phantom type arguments in generic inference (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12269">#12269</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
avoid false positive in logical assignment assertions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12278">#12278</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-arguments]
handle instantiation expressions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12220">#12220</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] treat
void as nullish in no-unnecessary-condition (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12241">#12241</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>anasm266 <a
href="https://github.com/anasm266"><code>@​anasm266</code></a></li>
<li>Anshika Jain <a
href="https://github.com/Anshikakalpana"><code>@​Anshikakalpana</code></a></li>
<li>Ulrich Stark</li>
<li>yugo innami <a
href="https://github.com/nami8824"><code>@​nami8824</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.59.1">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"><code>@​typescript-eslint/eslint-plugin</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>8.59.1 (2026-04-27)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] treat
void as nullish in no-unnecessary-condition (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12241">#12241</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-arguments]
handle instantiation expressions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12220">#12220</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
avoid false positive in logical assignment assertions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12278">#12278</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve phantom type arguments in generic inference (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12269">#12269</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve index signatures in undefined unions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12257">#12257</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion] fix
crash &quot;TypeError: checker.getTypeArguments is not a function&quot;
(<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12246">#12246</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>anasm266 <a
href="https://github.com/anasm266"><code>@​anasm266</code></a></li>
<li>Anshika Jain <a
href="https://github.com/Anshikakalpana"><code>@​Anshikakalpana</code></a></li>
<li>Ulrich Stark</li>
<li>yugo innami <a
href="https://github.com/nami8824"><code>@​nami8824</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.59.1">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="52457932e5"><code>5245793</code></a>
chore(release): publish 8.59.1</li>
<li><a
href="3cef124716"><code>3cef124</code></a>
chore(eslint-plugin): switch auto-generated test cases to hand-written
in dot...</li>
<li><a
href="27c507b6e5"><code>27c507b</code></a>
test: make sort-type-constituents tests fully static (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin/issues/12262">#12262</a>)</li>
<li><a
href="a03b31d422"><code>a03b31d</code></a>
chore(eslint-plugin): switch auto-generated test cases to hand-written
in no-...</li>
<li><a
href="a7099a7895"><code>a7099a7</code></a>
chore(eslint-plugin): switch auto-generated test cases to hand-written
in no-...</li>
<li><a
href="bfbd4a5433"><code>bfbd4a5</code></a>
chore(eslint-plugin): switch auto-generated test cases to hand-written
in no-...</li>
<li><a
href="b49d4b15cd"><code>b49d4b1</code></a>
chore(eslint-plugin): switch auto-generated test cases to hand-written
in no-...</li>
<li><a
href="3097e72747"><code>3097e72</code></a>
chore(eslint-plugin): switch auto-generated test cases to hand-written
in nam...</li>
<li><a
href="676191bbd7"><code>676191b</code></a>
chore(eslint-plugin): switch auto-generated test cases to hand-written
in mem...</li>
<li><a
href="e9dce8bdc6"><code>e9dce8b</code></a>
fix(eslint-plugin): [no-unnecessary-condition] treat void as nullish in
no-un...</li>
<li>Additional commits viewable in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.59.1/packages/eslint-plugin">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/parser` from 8.59.0 to 8.59.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases"><code>@​typescript-eslint/parser</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v8.59.1</h2>
<h2>8.59.1 (2026-04-27)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion] fix
crash &quot;TypeError: checker.getTypeArguments is not a function&quot;
(<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12246">#12246</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve index signatures in undefined unions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12257">#12257</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve phantom type arguments in generic inference (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12269">#12269</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
avoid false positive in logical assignment assertions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12278">#12278</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-arguments]
handle instantiation expressions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12220">#12220</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] treat
void as nullish in no-unnecessary-condition (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12241">#12241</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>anasm266 <a
href="https://github.com/anasm266"><code>@​anasm266</code></a></li>
<li>Anshika Jain <a
href="https://github.com/Anshikakalpana"><code>@​Anshikakalpana</code></a></li>
<li>Ulrich Stark</li>
<li>yugo innami <a
href="https://github.com/nami8824"><code>@​nami8824</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.59.1">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"><code>@​typescript-eslint/parser</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>8.59.1 (2026-04-27)</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.59.1">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="52457932e5"><code>5245793</code></a>
chore(release): publish 8.59.1</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.59.1/packages/parser">compare
view</a></li>
</ul>
</details>
<br />

Updates `@anthropic-ai/claude-agent-sdk-darwin-arm64` from 0.2.119 to
0.2.121

Updates `@anthropic-ai/claude-agent-sdk-darwin-x64` from 0.2.119 to
0.2.121

Updates `@anthropic-ai/claude-agent-sdk-linux-arm64` from 0.2.119 to
0.2.121

Updates `@anthropic-ai/claude-agent-sdk-linux-arm64-musl` from 0.2.119
to 0.2.121

Updates `@anthropic-ai/claude-agent-sdk-linux-x64` from 0.2.119 to
0.2.121

Updates `@anthropic-ai/claude-agent-sdk-linux-x64-musl` from 0.2.119 to
0.2.121

Updates `@anthropic-ai/claude-agent-sdk-win32-arm64` from 0.2.119 to
0.2.121

Updates `@anthropic-ai/claude-agent-sdk-win32-x64` from 0.2.119 to
0.2.121

Updates `@typescript-eslint/project-service` from 8.59.0 to 8.59.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases"><code>@​typescript-eslint/project-service</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v8.59.1</h2>
<h2>8.59.1 (2026-04-27)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion] fix
crash &quot;TypeError: checker.getTypeArguments is not a function&quot;
(<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12246">#12246</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve index signatures in undefined unions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12257">#12257</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve phantom type arguments in generic inference (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12269">#12269</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
avoid false positive in logical assignment assertions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12278">#12278</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-arguments]
handle instantiation expressions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12220">#12220</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] treat
void as nullish in no-unnecessary-condition (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12241">#12241</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>anasm266 <a
href="https://github.com/anasm266"><code>@​anasm266</code></a></li>
<li>Anshika Jain <a
href="https://github.com/Anshikakalpana"><code>@​Anshikakalpana</code></a></li>
<li>Ulrich Stark</li>
<li>yugo innami <a
href="https://github.com/nami8824"><code>@​nami8824</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.59.1">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"><code>@​typescript-eslint/project-service</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>8.59.1 (2026-04-27)</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.59.1">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="52457932e5"><code>5245793</code></a>
chore(release): publish 8.59.1</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.59.1/packages/project-service">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/scope-manager` from 8.59.0 to 8.59.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases"><code>@​typescript-eslint/scope-manager</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v8.59.1</h2>
<h2>8.59.1 (2026-04-27)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion] fix
crash &quot;TypeError: checker.getTypeArguments is not a function&quot;
(<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12246">#12246</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve index signatures in undefined unions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12257">#12257</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve phantom type arguments in generic inference (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12269">#12269</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
avoid false positive in logical assignment assertions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12278">#12278</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-arguments]
handle instantiation expressions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12220">#12220</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] treat
void as nullish in no-unnecessary-condition (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12241">#12241</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>anasm266 <a
href="https://github.com/anasm266"><code>@​anasm266</code></a></li>
<li>Anshika Jain <a
href="https://github.com/Anshikakalpana"><code>@​Anshikakalpana</code></a></li>
<li>Ulrich Stark</li>
<li>yugo innami <a
href="https://github.com/nami8824"><code>@​nami8824</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.59.1">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"><code>@​typescript-eslint/scope-manager</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>8.59.1 (2026-04-27)</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.59.1">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="52457932e5"><code>5245793</code></a>
chore(release): publish 8.59.1</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.59.1/packages/scope-manager">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/tsconfig-utils` from 8.59.0 to 8.59.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases"><code>@​typescript-eslint/tsconfig-utils</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v8.59.1</h2>
<h2>8.59.1 (2026-04-27)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion] fix
crash &quot;TypeError: checker.getTypeArguments is not a function&quot;
(<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12246">#12246</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve index signatures in undefined unions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12257">#12257</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve phantom type arguments in generic inference (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12269">#12269</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
avoid false positive in logical assignment assertions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12278">#12278</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-arguments]
handle instantiation expressions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12220">#12220</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] treat
void as nullish in no-unnecessary-condition (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12241">#12241</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>anasm266 <a
href="https://github.com/anasm266"><code>@​anasm266</code></a></li>
<li>Anshika Jain <a
href="https://github.com/Anshikakalpana"><code>@​Anshikakalpana</code></a></li>
<li>Ulrich Stark</li>
<li>yugo innami <a
href="https://github.com/nami8824"><code>@​nami8824</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.59.1">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"><code>@​typescript-eslint/tsconfig-utils</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>8.59.1 (2026-04-27)</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.59.1">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="52457932e5"><code>5245793</code></a>
chore(release): publish 8.59.1</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.59.1/packages/tsconfig-utils">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/type-utils` from 8.59.0 to 8.59.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases"><code>@​typescript-eslint/type-utils</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v8.59.1</h2>
<h2>8.59.1 (2026-04-27)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion] fix
crash &quot;TypeError: checker.getTypeArguments is not a function&quot;
(<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12246">#12246</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve index signatures in undefined unions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12257">#12257</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve phantom type arguments in generic inference (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12269">#12269</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
avoid false positive in logical assignment assertions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12278">#12278</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-arguments]
handle instantiation expressions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12220">#12220</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] treat
void as nullish in no-unnecessary-condition (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12241">#12241</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>anasm266 <a
href="https://github.com/anasm266"><code>@​anasm266</code></a></li>
<li>Anshika Jain <a
href="https://github.com/Anshikakalpana"><code>@​Anshikakalpana</code></a></li>
<li>Ulrich Stark</li>
<li>yugo innami <a
href="https://github.com/nami8824"><code>@​nami8824</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.59.1">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"><code>@​typescript-eslint/type-utils</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>8.59.1 (2026-04-27)</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.59.1">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="52457932e5"><code>5245793</code></a>
chore(release): publish 8.59.1</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.59.1/packages/type-utils">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/types` from 8.59.0 to 8.59.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases"><code>@​typescript-eslint/types</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v8.59.1</h2>
<h2>8.59.1 (2026-04-27)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion] fix
crash &quot;TypeError: checker.getTypeArguments is not a function&quot;
(<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12246">#12246</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve index signatures in undefined unions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12257">#12257</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve phantom type arguments in generic inference (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12269">#12269</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
avoid false positive in logical assignment assertions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12278">#12278</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-arguments]
handle instantiation expressions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12220">#12220</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] treat
void as nullish in no-unnecessary-condition (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12241">#12241</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>anasm266 <a
href="https://github.com/anasm266"><code>@​anasm266</code></a></li>
<li>Anshika Jain <a
href="https://github.com/Anshikakalpana"><code>@​Anshikakalpana</code></a></li>
<li>Ulrich Stark</li>
<li>yugo innami <a
href="https://github.com/nami8824"><code>@​nami8824</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.59.1">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"><code>@​typescript-eslint/types</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>8.59.1 (2026-04-27)</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.59.1">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="52457932e5"><code>5245793</code></a>
chore(release): publish 8.59.1</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.59.1/packages/types">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/typescript-estree` from 8.59.0 to 8.59.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases"><code>@​typescript-eslint/typescript-estree</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v8.59.1</h2>
<h2>8.59.1 (2026-04-27)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion] fix
crash &quot;TypeError: checker.getTypeArguments is not a function&quot;
(<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12246">#12246</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve index signatures in undefined unions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12257">#12257</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve phantom type arguments in generic inference (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12269">#12269</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
avoid false positive in logical assignment assertions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12278">#12278</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-arguments]
handle instantiation expressions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12220">#12220</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] treat
void as nullish in no-unnecessary-condition (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12241">#12241</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>anasm266 <a
href="https://github.com/anasm266"><code>@​anasm266</code></a></li>
<li>Anshika Jain <a
href="https://github.com/Anshikakalpana"><code>@​Anshikakalpana</code></a></li>
<li>Ulrich Stark</li>
<li>yugo innami <a
href="https://github.com/nami8824"><code>@​nami8824</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.59.1">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"><code>@​typescript-eslint/typescript-estree</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>8.59.1 (2026-04-27)</h2>
<p>This was a version bump only for typescript-estree 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.59.1">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="52457932e5"><code>5245793</code></a>
chore(release): publish 8.59.1</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.59.1/packages/typescript-estree">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/utils` from 8.59.0 to 8.59.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases"><code>@​typescript-eslint/utils</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v8.59.1</h2>
<h2>8.59.1 (2026-04-27)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion] fix
crash &quot;TypeError: checker.getTypeArguments is not a function&quot;
(<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12246">#12246</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve index signatures in undefined unions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12257">#12257</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve phantom type arguments in generic inference (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12269">#12269</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
avoid false positive in logical assignment assertions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12278">#12278</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-arguments]
handle instantiation expressions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12220">#12220</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] treat
void as nullish in no-unnecessary-condition (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12241">#12241</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>anasm266 <a
href="https://github.com/anasm266"><code>@​anasm266</code></a></li>
<li>Anshika Jain <a
href="https://github.com/Anshikakalpana"><code>@​Anshikakalpana</code></a></li>
<li>Ulrich Stark</li>
<li>yugo innami <a
href="https://github.com/nami8824"><code>@​nami8824</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.59.1">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"><code>@​typescript-eslint/utils</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>8.59.1 (2026-04-27)</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.59.1">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="52457932e5"><code>5245793</code></a>
chore(release): publish 8.59.1</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.59.1/packages/utils">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/visitor-keys` from 8.59.0 to 8.59.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases"><code>@​typescript-eslint/visitor-keys</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v8.59.1</h2>
<h2>8.59.1 (2026-04-27)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion] fix
crash &quot;TypeError: checker.getTypeArguments is not a function&quot;
(<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12246">#12246</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve index signatures in undefined unions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12257">#12257</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
preserve phantom type arguments in generic inference (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12269">#12269</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-assertion]
avoid false positive in logical assignment assertions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12278">#12278</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-arguments]
handle instantiation expressions (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12220">#12220</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-condition] treat
void as nullish in no-unnecessary-condition (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12241">#12241</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>anasm266 <a
href="https://github.com/anasm266"><code>@​anasm266</code></a></li>
<li>Anshika Jain <a
href="https://github.com/Anshikakalpana"><code>@​Anshikakalpana</code></a></li>
<li>Ulrich Stark</li>
<li>yugo innami <a
href="https://github.com/nami8824"><code>@​nami8824</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.59.1">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"><code>@​typescript-eslint/visitor-keys</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>8.59.1 (2026-04-27)</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.59.1">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="52457932e5"><code>5245793</code></a>
chore(release): publish 8.59.1</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.59.1/packages/visitor-keys">compare
view</a></li>
</ul>
</details>
<br />

Updates `es-module-lexer` from 2.0.0 to 2.1.0
<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.1.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Always exclude dyn import attributes from specifier range by <a
href="https://github.com/nicolo-ribaudo"><code>@​nicolo-ribaudo</code></a>
in <a
href="https://redirect.github.com/guybedford/es-module-lexer/pull/197">guybedford/es-module-lexer#197</a></li>
<li>fix: handle new of conflict cases by <a
href="https://github.com/guybedford"><code>@​guybedford</code></a> in <a
href="https://redirect.github.com/guybedford/es-module-lexer/pull/195">guybedford/es-module-lexer#195</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/nicolo-ribaudo"><code>@​nicolo-ribaudo</code></a>
made their first contribution in <a
href="https://redirect.github.com/guybedford/es-module-lexer/pull/197">guybedford/es-module-lexer#197</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/guybedford/es-module-lexer/compare/2.0.0...2.1.0">https://github.com/guybedford/es-module-lexer/compare/2.0.0...2.1.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="5fb11e1e01"><code>5fb11e1</code></a>
2.1.0</li>
<li><a
href="cc29c5bca1"><code>cc29c5b</code></a>
fix: handle new of conflict cases (<a
href="https://redirect.github.com/guybedford/es-module-lexer/issues/195">#195</a>)</li>
<li><a
href="4a351384ff"><code>4a35138</code></a>
Always exclude dyn import attributes from specifier range (<a
href="https://redirect.github.com/guybedford/es-module-lexer/issues/197">#197</a>)</li>
<li>See full diff in <a
href="https://github.com/guybedford/es-module-lexer/compare/2.0.0...2.1.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `express-rate-limit` from 8.4.0 to 8.4.1
<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.4.1</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="69568d4ea7"><code>69568d4</code></a>
8.4.1</li>
<li><a
href="c686acd0ba"><code>c686acd</code></a>
v8.4.1 changelog</li>
<li><a
href="ba71353abb"><code>ba71353</code></a>
test: bump timeout in flakey skipFailedRequests test (<a
href="https://redirect.github.com/express-rate-limit/express-rate-limit/issues/618">#618</a>)</li>
<li><a
href="dd4c8944d4"><code>dd4c894</code></a>
feat: allow usage of custom logger (<a
href="https://redirect.github.com/express-rate-limit/express-rate-limit/issues/616">#616</a>)</li>
<li>See full diff in <a
href="https://github.com/express-rate-limit/express-rate-limit/compare/v8.4.0...v8.4.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `jose` from 6.2.2 to 6.2.3
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/panva/jose/releases">jose's
releases</a>.</em></p>
<blockquote>
<h2>v6.2.3</h2>
<h3>Refactor</h3>
<ul>
<li>cleanly reject invalid PBES2 p2c (<a
href="0cdb851ca5">0cdb851</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/panva/jose/blob/main/CHANGELOG.md">jose's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/panva/jose/compare/v6.2.2...v6.2.3">6.2.3</a>
(2026-04-27)</h2>
<h3>Refactor</h3>
<ul>
<li>cleanly reject invalid PBES2 p2c (<a
href="0cdb851ca5">0cdb851</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="41ad7e9a76"><code>41ad7e9</code></a>
chore(release): 6.2.3</li>
<li><a
href="988e90f666"><code>988e90f</code></a>
chore: account for commit-and-tag-version instead of
standard-version</li>
<li><a
href="4b24656b61"><code>4b24656</code></a>
chore: update CHANGELOG.md header</li>
<li><a
href="0cdb851ca5"><code>0cdb851</code></a>
refactor: cleanly reject invalid PBES2 p2c</li>
<li><a
href="a0b261eddf"><code>a0b261e</code></a>
test: update Bun expectations</li>
<li><a
href="b39dc1af78"><code>b39dc1a</code></a>
chore: use fs.globSync</li>
<li><a
href="0675be19a6"><code>0675be1</code></a>
build: replace rollup umd build with a custom esbuild iife wrap</li>
<li><a
href="9b0332370a"><code>9b03323</code></a>
chore: bump packages</li>
<li><a
href="914b73dad2"><code>914b73d</code></a>
chore(deps-dev): bump lodash</li>
<li><a
href="9dce817d74"><code>9dce817</code></a>
chore: bump packages</li>
<li>Additional commits viewable in <a
href="https://github.com/panva/jose/compare/v6.2.2...v6.2.3">compare
view</a></li>
</ul>
</details>
<br />

Updates `postcss` from 8.5.10 to 8.5.12
<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.12</h2>
<ul>
<li>Fixed reading any file via user-generated CSS.</li>
<li>Added <code>opts.unsafeMap</code> to disable checks.</li>
</ul>
<h2>8.5.11</h2>
<ul>
<li>Fixed nested brackets parsing performance (by <a
href="https://github.com/offset"><code>@​offset</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.12</h2>
<ul>
<li>Fixed reading any file via user-generated CSS.</li>
<li>Added <code>opts.unsafeMap</code> to disable checks.</li>
</ul>
<h2>8.5.11</h2>
<ul>
<li>Fixed nested brackets parsing performance (by <a
href="https://github.com/offset"><code>@​offset</code></a>).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="9bc81c48f0"><code>9bc81c4</code></a>
Release 8.5.12 version</li>
<li><a
href="85c4d7dab8"><code>85c4d7d</code></a>
Another try to fix coverage</li>
<li><a
href="94484cae6d"><code>94484ca</code></a>
Try to fix coverage</li>
<li><a
href="c64b7488d2"><code>c64b748</code></a>
Load only .map source maps</li>
<li><a
href="aaec7b78b3"><code>aaec7b7</code></a>
Avoid throwing JSON parsing errors for non-JSON source maps</li>
<li><a
href="233fb264ea"><code>233fb26</code></a>
Mention original author of the solution</li>
<li><a
href="2502f75030"><code>2502f75</code></a>
Release 8.5.11 version</li>
<li><a
href="5ca1901949"><code>5ca1901</code></a>
Speed up parsing many nested brackets</li>
<li><a
href="42b5337dd7"><code>42b5337</code></a>
Update dependencies</li>
<li><a
href="7e36e153d0"><code>7e36e15</code></a>
Cache node.raws locally in Stringifier hot methods</li>
<li>Additional commits viewable in <a
href="https://github.com/postcss/postcss/compare/8.5.10...8.5.12">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-04-28 16:57:58 +02:00
Ben Brandt
76b4e96508 fix: Rename effort config category to thought_level (#609)
Uses the canonical version it should have been using.
2026-04-28 14:54:53 +00:00
acp-release-bot[bot]
32abd34bc0 chore(main): release 0.31.2 (#605)
🤖 I have created a release *beep* *boop*
---


##
[0.31.2](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.31.1...v0.31.2)
(2026-04-28)


### Bug Fixes

* tolerate invalid values in settings.json
([#601](https://github.com/agentclientprotocol/claude-agent-acp/issues/601))
([67af018](67af018d02))

---
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-04-28 10:21:32 +02:00
Alex Plãte
67af018d02 fix: tolerate invalid values in settings.json (#601)
A broken value in settings.json (e.g. an unknown permissions.defaultMode
or a non-string model) used to throw and prevent session creation. Match
the Claude Code SDK behavior by logging the bad value and falling back
to defaults instead, so a single typo no longer renders the agent
unusable.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
2026-04-28 10:20:09 +02:00
acp-release-bot[bot]
a55f31a6b0 chore(main): release 0.31.1 (#600)
🤖 I have created a release *beep* *boop*
---


##
[0.31.1](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.31.0...v0.31.1)
(2026-04-27)


### Bug Fixes

* detect glibc and use correct binary
([#599](https://github.com/agentclientprotocol/claude-agent-acp/issues/599))
([f0801d5](f0801d50b2))

---
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-04-27 12:02:31 +02:00
Ben Brandt
f0801d50b2 fix: detect glibc and use correct binary (#599)
## Summary

Re-records #598 with a Conventional Commit prefix so
[release-please](https://github.com/googleapis/release-please) generates
a release PR. The actual fix was merged in 1a9d3ce but lacked the `fix:`
prefix, so release-please skipped it.

This PR is intentionally empty — once squash-merged, the resulting
commit message on `main` will start with `fix:` and trigger a patch
release.

See https://github.com/tidewave-ai/tidewave_app/issues/141.
2026-04-27 10:01:08 +00:00
Steffen Deusch
1a9d3cebcd detect glibc and use correct binary (#598)
On Linux, the current code prefers the musl binary if multiple versions
are installed, but it actually crashes on glibc systems.

Supersedes
https://github.com/agentclientprotocol/claude-agent-acp/pull/597.

Importantly, we need to pass the detected binary to
`pathToClaudeCodeExecutable`, otherwise the SDK itself selects the wrong
version.

See https://github.com/tidewave-ai/tidewave_app/issues/141.
Closes https://github.com/tidewave-ai/tidewave_app/issues/141.
2026-04-27 11:54:12 +02:00
15 changed files with 1289 additions and 327 deletions

View File

@@ -11,6 +11,13 @@ updates:
interval: "daily"
allow:
- dependency-type: "all"
ignore:
# Pinned to match the peer range of @anthropic-ai/claude-agent-sdk;
# newer minors break type compatibility between the two installed copies.
- dependency-name: "@anthropic-ai/sdk"
update-types:
- "version-update:semver-minor"
- "version-update:semver-major"
groups:
minor:
update-types:

View File

@@ -1,3 +1,3 @@
{
".": "0.31.0"
".": "0.32.0"
}

View File

@@ -1,5 +1,48 @@
# Changelog
## [0.32.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.31.4...v0.32.0) (2026-05-03)
### Features
* **deps-dev:** Bump nanoid from 3.3.11 to 3.3.12 ([#622](https://github.com/agentclientprotocol/claude-agent-acp/issues/622)) ([c78ac62](https://github.com/agentclientprotocol/claude-agent-acp/commit/c78ac62d05283e5683b87b198923818f9f556a03))
* **deps:** Bump the minor group with 2 updates ([#619](https://github.com/agentclientprotocol/claude-agent-acp/issues/619)) ([6ccd37c](https://github.com/agentclientprotocol/claude-agent-acp/commit/6ccd37ce947fbd55691f676344b15ef1266191ad))
* **deps:** update to @anthropic-ai/claude-agent-sdk 0.2.126 ([#621](https://github.com/agentclientprotocol/claude-agent-acp/issues/621)) ([becc3b8](https://github.com/agentclientprotocol/claude-agent-acp/commit/becc3b86e60eb33e7823a0ef27d5aa99758750d7))
* **deps:** Update to @anthropic-ai/claude-agent-sdk@0.2.123 ([#614](https://github.com/agentclientprotocol/claude-agent-acp/issues/614)) ([5b93119](https://github.com/agentclientprotocol/claude-agent-acp/commit/5b9311938d97f47400debd66f2b4792e50639c6b))
### Bug Fixes
* emit a real diff when Write overwrites an existing file ([#618](https://github.com/agentclientprotocol/claude-agent-acp/issues/618)) ([8d7e220](https://github.com/agentclientprotocol/claude-agent-acp/commit/8d7e22026e15d54208414bd9ad128eb86d41b451))
## [0.31.4](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.31.3...v0.31.4) (2026-04-28)
### Bug Fixes
* gate auto mode on model support ([#604](https://github.com/agentclientprotocol/claude-agent-acp/issues/604)) ([ec47d34](https://github.com/agentclientprotocol/claude-agent-acp/commit/ec47d3446fa22e5895621f57c0a4497dbe044505))
## [0.31.3](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.31.2...v0.31.3) (2026-04-28)
### Bug Fixes
* Rename effort config category to thought_level ([#609](https://github.com/agentclientprotocol/claude-agent-acp/issues/609)) ([76b4e96](https://github.com/agentclientprotocol/claude-agent-acp/commit/76b4e9650832e59a6b4915ad898fc66a065022fa))
## [0.31.2](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.31.1...v0.31.2) (2026-04-28)
### Bug Fixes
* tolerate invalid values in settings.json ([#601](https://github.com/agentclientprotocol/claude-agent-acp/issues/601)) ([67af018](https://github.com/agentclientprotocol/claude-agent-acp/commit/67af018d0216238d8f3cd5bad04a55200518239b))
## [0.31.1](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.31.0...v0.31.1) (2026-04-27)
### Bug Fixes
* detect glibc and use correct binary ([#599](https://github.com/agentclientprotocol/claude-agent-acp/issues/599)) ([f0801d5](https://github.com/agentclientprotocol/claude-agent-acp/commit/f0801d50b2390a3c48150b9b8507d30a70c18b84))
## [0.31.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.30.0...v0.31.0) (2026-04-24)

302
package-lock.json generated
View File

@@ -1,16 +1,16 @@
{
"name": "@agentclientprotocol/claude-agent-acp",
"version": "0.31.0",
"version": "0.32.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@agentclientprotocol/claude-agent-acp",
"version": "0.31.0",
"version": "0.32.0",
"license": "Apache-2.0",
"dependencies": {
"@agentclientprotocol/sdk": "0.20.0",
"@anthropic-ai/claude-agent-sdk": "0.2.119",
"@agentclientprotocol/sdk": "0.21.0",
"@anthropic-ai/claude-agent-sdk": "0.2.126",
"zod": "^3.25.0 || ^4.0.0"
},
"bin": {
@@ -20,11 +20,11 @@
"@anthropic-ai/sdk": "0.89.0",
"@eslint/js": "10.0.1",
"@types/node": "25.6.0",
"@typescript-eslint/eslint-plugin": "8.59.0",
"@typescript-eslint/parser": "8.59.0",
"eslint": "10.2.1",
"@typescript-eslint/eslint-plugin": "8.59.1",
"@typescript-eslint/parser": "8.59.1",
"eslint": "10.3.0",
"eslint-config-prettier": "10.1.8",
"globals": "17.5.0",
"globals": "17.6.0",
"prettier": "3.8.3",
"ts-node": "10.9.2",
"typescript": "6.0.3",
@@ -32,18 +32,18 @@
}
},
"node_modules/@agentclientprotocol/sdk": {
"version": "0.20.0",
"resolved": "https://registry.npmjs.org/@agentclientprotocol/sdk/-/sdk-0.20.0.tgz",
"integrity": "sha512-BxEHyE4MvwyOsdyVPub1vEtyrq8E0JSdjC+ckXWimY1VabFCTXdPyXv2y2Omz1j+iod7Z8oBJDXFCJptM0GBqQ==",
"version": "0.21.0",
"resolved": "https://registry.npmjs.org/@agentclientprotocol/sdk/-/sdk-0.21.0.tgz",
"integrity": "sha512-ONj+Q8qOdNQp5XbH5jnMwzT9IKZJsSN0p0lkceS4GtUtNOPVLpNzSS8gqQdGMKfBvA0ESbkL8BTaSN1Rc9miEw==",
"license": "Apache-2.0",
"peerDependencies": {
"zod": "^3.25.0 || ^4.0.0"
}
},
"node_modules/@anthropic-ai/claude-agent-sdk": {
"version": "0.2.119",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.2.119.tgz",
"integrity": "sha512-6AvthpsaOTlkn514brSGOcCSLHDXODnU+ExN1O3CJCjxr5RBcmzR057C9EIM0G7IchnXsRfMZgRO1QKsjTXdbA==",
"version": "0.2.126",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.2.126.tgz",
"integrity": "sha512-4ZrVu0XUEwNG6wxvsLgppRAmSfAf3oeEMEUPhgazb0AXUUe/7W8MxwZKJWOffqSLWaNYzOt3ZCIL7NJY6toqWw==",
"license": "SEE LICENSE IN README.md",
"dependencies": {
"@anthropic-ai/sdk": "^0.81.0",
@@ -53,23 +53,23 @@
"node": ">=18.0.0"
},
"optionalDependencies": {
"@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.2.119",
"@anthropic-ai/claude-agent-sdk-darwin-x64": "0.2.119",
"@anthropic-ai/claude-agent-sdk-linux-arm64": "0.2.119",
"@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.2.119",
"@anthropic-ai/claude-agent-sdk-linux-x64": "0.2.119",
"@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.2.119",
"@anthropic-ai/claude-agent-sdk-win32-arm64": "0.2.119",
"@anthropic-ai/claude-agent-sdk-win32-x64": "0.2.119"
"@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.2.126",
"@anthropic-ai/claude-agent-sdk-darwin-x64": "0.2.126",
"@anthropic-ai/claude-agent-sdk-linux-arm64": "0.2.126",
"@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.2.126",
"@anthropic-ai/claude-agent-sdk-linux-x64": "0.2.126",
"@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.2.126",
"@anthropic-ai/claude-agent-sdk-win32-arm64": "0.2.126",
"@anthropic-ai/claude-agent-sdk-win32-x64": "0.2.126"
},
"peerDependencies": {
"zod": "^4.0.0"
}
},
"node_modules/@anthropic-ai/claude-agent-sdk-darwin-arm64": {
"version": "0.2.119",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.2.119.tgz",
"integrity": "sha512-kxnG37SZqUata2Jcp/YQ0n9Y7o/sinE/8LdG4ltM1gePh+z+0Mfa4vBUUTEBMBFth9PTovKoesIuVuyFpvO/Cw==",
"version": "0.2.126",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.2.126.tgz",
"integrity": "sha512-JFlJBbeAlx7Ic5s4lGUN9SppobryXk/lIqPCvhp6KrJTQIerh3MIBzxsVIJ0MaDut7jVni/oYgsvDni7NIyqHA==",
"cpu": [
"arm64"
],
@@ -80,9 +80,9 @@
]
},
"node_modules/@anthropic-ai/claude-agent-sdk-darwin-x64": {
"version": "0.2.119",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.2.119.tgz",
"integrity": "sha512-9Aj8g3ELsmZuOFg17TCkikeg/Wt2ucVT8hOOPQUatzLd7BKhydrHLA0RP42nBpWECO1B/n/mPdQ4iS/LS3s2Fg==",
"version": "0.2.126",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.2.126.tgz",
"integrity": "sha512-J8BpMj16NK9FUaG3HnHSivyp4Xww9DKWHiC8QSHT9oiT8pH5IG7nl0jxyjIq/lY79evlTY+ubgDVWlMUhUAN/g==",
"cpu": [
"x64"
],
@@ -93,15 +93,12 @@
]
},
"node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64": {
"version": "0.2.119",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.2.119.tgz",
"integrity": "sha512-v3o464XkiYehp/OKidQQirxdVb+aGSvdJvHF2zH9p33W8M/NC21zwwh4dhwDnKsyrtBIgkt2CcMwzIl30r0OtA==",
"version": "0.2.126",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.2.126.tgz",
"integrity": "sha512-LM+mnfQsgI+1i5mYZwIPDDf14NGBu5wbhzm5U8P11dCa2p8sXmKoWpkbO16BFM2NxeW44I/RXCxE5qFsbz4zcg==",
"cpu": [
"arm64"
],
"libc": [
"glibc"
],
"license": "SEE LICENSE IN LICENSE.md",
"optional": true,
"os": [
@@ -109,15 +106,12 @@
]
},
"node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64-musl": {
"version": "0.2.119",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.2.119.tgz",
"integrity": "sha512-IPGWgtz+gGnD7fxKAvSf913EUT/lYBTBE8EZ7lh3+x5ZP2859LWLmrCm053Lf3nMWo/CWikZsVPwkDVwpz6tIQ==",
"version": "0.2.126",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.2.126.tgz",
"integrity": "sha512-GO0BnIUw3LQ3XAy+nipAabkN0GwQGPhHB6ITI4XLoR99fLHB3TA6WfyvTf0fnpxd25A+c/+UsAoxz4zBQaHlhA==",
"cpu": [
"arm64"
],
"libc": [
"musl"
],
"license": "SEE LICENSE IN LICENSE.md",
"optional": true,
"os": [
@@ -125,15 +119,12 @@
]
},
"node_modules/@anthropic-ai/claude-agent-sdk-linux-x64": {
"version": "0.2.119",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.2.119.tgz",
"integrity": "sha512-9ePt4ZN+hsqDw4AgS4KtcWIGKfL9Oq28kwkrTER/QAcSrVKxiLonp81cCLzg7Ok/IUJu4Cfd71GZbFv/WE54zw==",
"version": "0.2.126",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.2.126.tgz",
"integrity": "sha512-yaOTDcYCdscxC0LKg9w8IwSa5g+993WggFZJBTZpqvflA2+WMQeTarDnKlsFTCw9XUZkL8XZeBALYJGx0HutuA==",
"cpu": [
"x64"
],
"libc": [
"glibc"
],
"license": "SEE LICENSE IN LICENSE.md",
"optional": true,
"os": [
@@ -141,15 +132,12 @@
]
},
"node_modules/@anthropic-ai/claude-agent-sdk-linux-x64-musl": {
"version": "0.2.119",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.2.119.tgz",
"integrity": "sha512-QYxFNAe4FFridPkKhGlNcNBJ0TaIygWYyvfI9g4kX0i+RVbresUWuZVkWY06ioJ0fXoixFJ+HNQBMB7dLrIp8Q==",
"version": "0.2.126",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.2.126.tgz",
"integrity": "sha512-ByJGO0+mu7EplxSFSCIHd7QWsXdrF3qgtzQ177o/j+oSppLoqR1ot5ktf8aw5oR3CC5lFHg4tqd6TnneQpEoIg==",
"cpu": [
"x64"
],
"libc": [
"musl"
],
"license": "SEE LICENSE IN LICENSE.md",
"optional": true,
"os": [
@@ -157,9 +145,9 @@
]
},
"node_modules/@anthropic-ai/claude-agent-sdk-win32-arm64": {
"version": "0.2.119",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.2.119.tgz",
"integrity": "sha512-p/TjcKQvkCYtXGPlR+mdyNwqCmvRcQL34Wtq0yUZ+iqmI/eyCe59IJ3AZrE0EZoqmiAevEYzatPIt9sncC9uxw==",
"version": "0.2.126",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.2.126.tgz",
"integrity": "sha512-gv3MOsOBkCx3LajOOIjD7AKsOtz/qNHsS2oshGt2GVoy7JA3XbCDeCetDjM6SorV4SE+7F/IH0UJdXe5ejI/Zg==",
"cpu": [
"arm64"
],
@@ -170,9 +158,9 @@
]
},
"node_modules/@anthropic-ai/claude-agent-sdk-win32-x64": {
"version": "0.2.119",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.2.119.tgz",
"integrity": "sha512-k98Ju0wtktm6FhqTE/cXlVr6K4kGqBolVjEGzeKkW6ZILc7124euwNapAvkQCwMAavAxS/ZnO3jdKMtHtwTVTA==",
"version": "0.2.126",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.2.126.tgz",
"integrity": "sha512-oRV75HwyoOd1/t5+kipAM2g62CaElpKGvSBx3Ys4lCwCiFUyOnmet/O+hRXENsY6ShDeQZEcJL2UWljr2d5NQw==",
"cpu": [
"x64"
],
@@ -662,9 +650,6 @@
"arm64"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
@@ -682,9 +667,6 @@
"arm64"
],
"dev": true,
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
@@ -702,9 +684,6 @@
"ppc64"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
@@ -722,9 +701,6 @@
"s390x"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
@@ -742,9 +718,6 @@
"x64"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
@@ -762,9 +735,6 @@
"x64"
],
"dev": true,
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
@@ -947,17 +917,17 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.59.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.0.tgz",
"integrity": "sha512-HyAZtpdkgZwpq8Sz3FSUvCR4c+ScbuWa9AksK2Jweub7w4M3yTz4O11AqVJzLYjy/B9ZWPyc81I+mOdJU/bDQw==",
"version": "8.59.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.1.tgz",
"integrity": "sha512-BOziFIfE+6osHO9FoJG4zjoHUcvI7fTNBSpdAwrNH0/TLvzjsk2oo8XSSOT2HhqUyhZPfHv4UOffoJ9oEEQ7Ag==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.12.2",
"@typescript-eslint/scope-manager": "8.59.0",
"@typescript-eslint/type-utils": "8.59.0",
"@typescript-eslint/utils": "8.59.0",
"@typescript-eslint/visitor-keys": "8.59.0",
"@typescript-eslint/scope-manager": "8.59.1",
"@typescript-eslint/type-utils": "8.59.1",
"@typescript-eslint/utils": "8.59.1",
"@typescript-eslint/visitor-keys": "8.59.1",
"ignore": "^7.0.5",
"natural-compare": "^1.4.0",
"ts-api-utils": "^2.5.0"
@@ -970,22 +940,22 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^8.59.0",
"@typescript-eslint/parser": "8.59.1",
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.59.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.0.tgz",
"integrity": "sha512-TI1XGwKbDpo9tRW8UDIXCOeLk55qe9ZFGs8MTKU6/M08HWTw52DD/IYhfQtOEhEdPhLMT26Ka/x7p70nd3dzDg==",
"version": "8.59.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.1.tgz",
"integrity": "sha512-HDQH9O/47Dxi1ceDhBXdaldtf/WV9yRYMjbjCuNk3qnaTD564qwv61Y7+gTxwxRKzSrgO5uhtw584igXVuuZkA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.59.0",
"@typescript-eslint/types": "8.59.0",
"@typescript-eslint/typescript-estree": "8.59.0",
"@typescript-eslint/visitor-keys": "8.59.0",
"@typescript-eslint/scope-manager": "8.59.1",
"@typescript-eslint/types": "8.59.1",
"@typescript-eslint/typescript-estree": "8.59.1",
"@typescript-eslint/visitor-keys": "8.59.1",
"debug": "^4.4.3"
},
"engines": {
@@ -1001,14 +971,14 @@
}
},
"node_modules/@typescript-eslint/project-service": {
"version": "8.59.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.0.tgz",
"integrity": "sha512-Lw5ITrR5s5TbC19YSvlr63ZfLaJoU6vtKTHyB0GQOpX0W7d5/Ir6vUahWi/8Sps/nOukZQ0IB3SmlxZnjaKVnw==",
"version": "8.59.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.1.tgz",
"integrity": "sha512-+MuHQlHiEr00Of/IQbE/MmEoi44znZHbR/Pz7Opq4HryUOlRi+/44dro9Ycy8Fyo+/024IWtw8m4JUMCGTYxDg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.59.0",
"@typescript-eslint/types": "^8.59.0",
"@typescript-eslint/tsconfig-utils": "^8.59.1",
"@typescript-eslint/types": "^8.59.1",
"debug": "^4.4.3"
},
"engines": {
@@ -1023,14 +993,14 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "8.59.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.0.tgz",
"integrity": "sha512-UzR16Ut8IpA3Mc4DbgAShlPPkVm8xXMWafXxB0BocaVRHs8ZGakAxGRskF7FId3sdk9lgGD73GSFaWmWFDE4dg==",
"version": "8.59.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.1.tgz",
"integrity": "sha512-LwuHQI4pDOYVKvmH2dkaJo6YZCSgouVgnS/z7yBPKBMvgtBvyLqiLy9Z6b7+m/TRcX1NFYUqZetI5Y+aT4GEfg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.59.0",
"@typescript-eslint/visitor-keys": "8.59.0"
"@typescript-eslint/types": "8.59.1",
"@typescript-eslint/visitor-keys": "8.59.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1041,9 +1011,9 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.59.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.0.tgz",
"integrity": "sha512-91Sbl3s4Kb3SybliIY6muFBmHVv+pYXfybC4Oolp3dvk8BvIE3wOPc+403CWIT7mJNkfQRGtdqghzs2+Z91Tqg==",
"version": "8.59.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.1.tgz",
"integrity": "sha512-/0nEyPbX7gRsk0Uwfe4ALwwgxuA66d/l2mhRDNlAvaj4U3juhUtJNq0DsY8M2AYwwb9rEq2hrC3IcIcEt++iJA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1058,15 +1028,15 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.59.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.0.tgz",
"integrity": "sha512-3TRiZaQSltGqGeNrJzzr1+8YcEobKH9rHnqIp/1psfKFmhRQDNMGP5hBufanYTGznwShzVLs3Mz+gDN7HkWfXg==",
"version": "8.59.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.1.tgz",
"integrity": "sha512-klWPBR2ciQHS3f++ug/mVnWKPjBUo7icEL3FAO1lhAR1Z1i5NQYZ1EannMSRYcq5qCv5wNALlXr6fksRHyYl7w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.59.0",
"@typescript-eslint/typescript-estree": "8.59.0",
"@typescript-eslint/utils": "8.59.0",
"@typescript-eslint/types": "8.59.1",
"@typescript-eslint/typescript-estree": "8.59.1",
"@typescript-eslint/utils": "8.59.1",
"debug": "^4.4.3",
"ts-api-utils": "^2.5.0"
},
@@ -1083,9 +1053,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "8.59.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.0.tgz",
"integrity": "sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A==",
"version": "8.59.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.1.tgz",
"integrity": "sha512-ZDCjgccSdYPw5Bxh+my4Z0lJU96ZDN7jbBzvmEn0FZx3RtU1C7VWl6NbDx94bwY3V5YsgwRzJPOgeY2Q/nLG8A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1097,16 +1067,16 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "8.59.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.0.tgz",
"integrity": "sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw==",
"version": "8.59.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.1.tgz",
"integrity": "sha512-OUd+vJS05sSkOip+BkZ/2NS8RMxrAAJemsC6vU3kmfLyeaJT0TftHkV9mcx2107MmsBVXXexhVu4F0TZXyMl4g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.59.0",
"@typescript-eslint/tsconfig-utils": "8.59.0",
"@typescript-eslint/types": "8.59.0",
"@typescript-eslint/visitor-keys": "8.59.0",
"@typescript-eslint/project-service": "8.59.1",
"@typescript-eslint/tsconfig-utils": "8.59.1",
"@typescript-eslint/types": "8.59.1",
"@typescript-eslint/visitor-keys": "8.59.1",
"debug": "^4.4.3",
"minimatch": "^10.2.2",
"semver": "^7.7.3",
@@ -1125,16 +1095,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "8.59.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.0.tgz",
"integrity": "sha512-I1R/K7V07XsMJ12Oaxg/O9GfrysGTmCRhvZJBv0RE0NcULMzjqVpR5kRRQjHsz3J/bElU7HwCO7zkqL+MSUz+g==",
"version": "8.59.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.1.tgz",
"integrity": "sha512-3pIeoXhCeYH9FSCBI8P3iNwJlGuzPlYKkTlen2O9T1DSeeg8UG8jstq6BLk+Mda0qup7mgk4z4XL4OzRaxZ8LA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.9.1",
"@typescript-eslint/scope-manager": "8.59.0",
"@typescript-eslint/types": "8.59.0",
"@typescript-eslint/typescript-estree": "8.59.0"
"@typescript-eslint/scope-manager": "8.59.1",
"@typescript-eslint/types": "8.59.1",
"@typescript-eslint/typescript-estree": "8.59.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1149,13 +1119,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.59.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.0.tgz",
"integrity": "sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q==",
"version": "8.59.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.1.tgz",
"integrity": "sha512-LdDNl6C5iJExcM0Yh0PwAIBb9PrSiCsWamF/JyEZawm3kFDnRoaq3LGE4bpyRao/fWeGKKyw7icx0YxrLFC5Cg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.59.0",
"@typescript-eslint/types": "8.59.1",
"eslint-visitor-keys": "^5.0.0"
},
"engines": {
@@ -1672,9 +1642,9 @@
}
},
"node_modules/es-module-lexer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz",
"integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz",
"integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==",
"dev": true,
"license": "MIT"
},
@@ -1710,9 +1680,9 @@
}
},
"node_modules/eslint": {
"version": "10.2.1",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-10.2.1.tgz",
"integrity": "sha512-wiyGaKsDgqXvF40P8mDwiUp/KQjE1FdrIEJsM8PZ3XCiniTMXS3OHWWUe5FI5agoCnr8x4xPrTDZuxsBlNHl+Q==",
"version": "10.3.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-10.3.0.tgz",
"integrity": "sha512-XbEXaRva5cF0ZQB8w6MluHA0kZZfV2DuCMJ3ozyEOHLwDpZX2Lmm/7Pp0xdJmI0GL1W05VH5VwIFHEm1Vcw2gw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2031,9 +2001,9 @@
}
},
"node_modules/express-rate-limit": {
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.4.0.tgz",
"integrity": "sha512-gDK8yiqKxrGta+3WtON59arrrw6GLmadA1qoFgYXzdcch8fmKDID2XqO8itsi3f1wufXYPT51387dN6cvVBS3Q==",
"version": "8.4.1",
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.4.1.tgz",
"integrity": "sha512-NGVYwQSAyEQgzxX1iCM978PP9AdO/hW93gMcF6ZwQCm+rFvLsBH6w4xcXWTcliS8La5EPRN3p9wzItqBwJrfNw==",
"license": "MIT",
"dependencies": {
"ip-address": "10.1.0"
@@ -2267,9 +2237,9 @@
}
},
"node_modules/globals": {
"version": "17.5.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-17.5.0.tgz",
"integrity": "sha512-qoV+HK2yFl/366t2/Cb3+xxPUo5BuMynomoDmiaZBIdbs+0pYbjfZU+twLhGKp4uCZ/+NbtpVepH5bGCxRyy2g==",
"version": "17.6.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-17.6.0.tgz",
"integrity": "sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2316,9 +2286,9 @@
}
},
"node_modules/hono": {
"version": "4.12.15",
"resolved": "https://registry.npmjs.org/hono/-/hono-4.12.15.tgz",
"integrity": "sha512-qM0jDhFEaCBb4TxoW7f53Qrpv9RBiayUHo0S52JudprkhvpjIrGoU1mnnr29Fvd1U335ZFPZQY1wlkqgfGXyLg==",
"version": "4.12.16",
"resolved": "https://registry.npmjs.org/hono/-/hono-4.12.16.tgz",
"integrity": "sha512-jN0ZewiNAWSe5khM3EyCmBb250+b40wWbwNILNfEvq84VREWwOIkuUsFONk/3i3nqkz7Oe1PcpM2mwQEK2L9Kg==",
"license": "MIT",
"engines": {
"node": ">=16.9.0"
@@ -2440,9 +2410,9 @@
"license": "ISC"
},
"node_modules/jose": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/jose/-/jose-6.2.2.tgz",
"integrity": "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==",
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/jose/-/jose-6.2.3.tgz",
"integrity": "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/panva"
@@ -2654,9 +2624,6 @@
"arm64"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -2678,9 +2645,6 @@
"arm64"
],
"dev": true,
"libc": [
"musl"
],
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -2702,9 +2666,6 @@
"x64"
],
"dev": true,
"libc": [
"glibc"
],
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -2726,9 +2687,6 @@
"x64"
],
"dev": true,
"libc": [
"musl"
],
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -2895,9 +2853,9 @@
"license": "MIT"
},
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"version": "3.3.12",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
"integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
"dev": true,
"funding": [
{
@@ -3107,9 +3065,9 @@
}
},
"node_modules/postcss": {
"version": "8.5.10",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz",
"integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==",
"version": "8.5.13",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.13.tgz",
"integrity": "sha512-qif0+jGGZoLWdHey3UFHHWP0H7Gbmsk8T5VEqyYFbWqPr1XqvLGBbk/sl8V5exGmcYJklJOhOQq1pV9IcsiFag==",
"dev": true,
"funding": [
{
@@ -3493,9 +3451,9 @@
"license": "MIT"
},
"node_modules/tinyexec": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.1.tgz",
"integrity": "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==",
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz",
"integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3932,9 +3890,9 @@
}
},
"node_modules/zod": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/zod/-/zod-4.4.1.tgz",
"integrity": "sha512-a6ENMBBGZBsnlSebQ/eKCguSBeGKSf4O7BPnqVPmYGtpBYI7VSqoVqw+QcB7kPRjbqPwhYTpFbVj/RqNz/CT0Q==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"

View File

@@ -3,7 +3,7 @@
"publishConfig": {
"access": "public"
},
"version": "0.31.0",
"version": "0.32.0",
"description": "An ACP-compatible coding agent powered by the Claude Agent SDK (TypeScript)",
"main": "dist/lib.js",
"types": "dist/lib.d.ts",
@@ -60,19 +60,19 @@
"author": "Zed Industries",
"license": "Apache-2.0",
"dependencies": {
"@agentclientprotocol/sdk": "0.20.0",
"@anthropic-ai/claude-agent-sdk": "0.2.119",
"@agentclientprotocol/sdk": "0.21.0",
"@anthropic-ai/claude-agent-sdk": "0.2.126",
"zod": "^3.25.0 || ^4.0.0"
},
"devDependencies": {
"@anthropic-ai/sdk": "0.89.0",
"@eslint/js": "10.0.1",
"@types/node": "25.6.0",
"@typescript-eslint/eslint-plugin": "8.59.0",
"@typescript-eslint/parser": "8.59.0",
"eslint": "10.2.1",
"@typescript-eslint/eslint-plugin": "8.59.1",
"@typescript-eslint/parser": "8.59.1",
"eslint": "10.3.0",
"eslint-config-prettier": "10.1.8",
"globals": "17.5.0",
"globals": "17.6.0",
"prettier": "3.8.3",
"ts-node": "10.9.2",
"typescript": "6.0.3",

View File

@@ -73,7 +73,7 @@ import {
planEntries,
registerHookCallback,
toolInfoFromToolUse,
toolUpdateFromEditToolResponse,
toolUpdateFromDiffToolResponse,
toolUpdateFromToolResult,
} from "./tools.js";
import { nodeToWebReadable, nodeToWebWritable, Pushable, unreachable } from "./utils.js";
@@ -274,12 +274,22 @@ export async function claudeCliPath(): Promise<string> {
const { createRequire } = await import("node:module");
const req = createRequire(import.meta.resolve("@anthropic-ai/claude-agent-sdk"));
const ext = process.platform === "win32" ? ".exe" : "";
// On linux, both glibc and musl variants may be installed side-by-side
// (e.g. bunx hydrates every optional dep), so picking one by trial is
// unreliable: the wrong binary segfaults at runtime instead of failing to
// spawn. Detect the runtime libc and prefer the matching variant, falling
// back to the other only if the preferred one isn't installed.
const candidates =
process.platform === "linux"
? [
`@anthropic-ai/claude-agent-sdk-linux-${process.arch}-musl/claude${ext}`,
`@anthropic-ai/claude-agent-sdk-linux-${process.arch}/claude${ext}`,
]
? isMuslLibc()
? [
`@anthropic-ai/claude-agent-sdk-linux-${process.arch}-musl/claude${ext}`,
`@anthropic-ai/claude-agent-sdk-linux-${process.arch}/claude${ext}`,
]
: [
`@anthropic-ai/claude-agent-sdk-linux-${process.arch}/claude${ext}`,
`@anthropic-ai/claude-agent-sdk-linux-${process.arch}-musl/claude${ext}`,
]
: [`@anthropic-ai/claude-agent-sdk-${process.platform}-${process.arch}/claude${ext}`];
for (const candidate of candidates) {
try {
@@ -294,6 +304,15 @@ export async function claudeCliPath(): Promise<string> {
);
}
function isMuslLibc(): boolean {
// process.report.getReport().header.glibcVersionRuntime is populated when
// Node is dynamically linked against glibc, and absent on musl.
const report = process.report?.getReport() as
| { header?: { glibcVersionRuntime?: string } }
| undefined;
return !report?.header?.glibcVersionRuntime;
}
function shouldHideClaudeAuth(): boolean {
return process.argv.includes("--hide-claude-auth");
}
@@ -366,29 +385,36 @@ const PERMISSION_MODE_ALIASES: Record<string, PermissionMode> = {
bypass: "bypassPermissions",
};
export function resolvePermissionMode(defaultMode?: unknown): PermissionMode {
export function resolvePermissionMode(
defaultMode?: unknown,
logger: Logger = console,
): PermissionMode {
if (defaultMode === undefined) {
return "default";
}
if (typeof defaultMode !== "string") {
throw new Error("Invalid permissions.defaultMode: expected a string.");
logger.error("Ignoring permissions.defaultMode from settings: expected a string.");
return "default";
}
const normalized = defaultMode.trim().toLowerCase();
if (normalized === "") {
throw new Error("Invalid permissions.defaultMode: expected a non-empty string.");
logger.error("Ignoring permissions.defaultMode from settings: expected a non-empty string.");
return "default";
}
const mapped = PERMISSION_MODE_ALIASES[normalized];
if (!mapped) {
throw new Error(`Invalid permissions.defaultMode: ${defaultMode}.`);
logger.error(`Ignoring permissions.defaultMode from settings: unknown value '${defaultMode}'.`);
return "default";
}
if (mapped === "bypassPermissions" && !ALLOW_BYPASS) {
throw new Error(
"Invalid permissions.defaultMode: bypassPermissions is not available when running as root.",
logger.error(
"Ignoring permissions.defaultMode from settings: bypassPermissions is not available when running as root.",
);
return "default";
}
return mapped;
@@ -1283,7 +1309,7 @@ export class ClaudeAcpAgent implements Agent {
// effort changes and effort changes induced by a model switch go through
// the same path.
await this.applyConfigOptionValue(session, params.configId, resolvedValue);
await this.applyConfigOptionValue(params.sessionId, session, params.configId, resolvedValue);
return { configOptions: session.configOptions };
}
@@ -1300,8 +1326,17 @@ export class ClaudeAcpAgent implements Agent {
default:
throw new Error("Invalid Mode");
}
const session = this.sessions[sessionId];
if (!session) {
throw new Error("Session not found");
}
if (!session.modes.availableModes.some((mode) => mode.id === modeId)) {
throw new Error(`Mode ${modeId} is not available in this session`);
}
try {
await this.sessions[sessionId].query.setPermissionMode(modeId);
await session.query.setPermissionMode(modeId);
} catch (error) {
if (error instanceof Error) {
if (!error.message) {
@@ -1371,7 +1406,7 @@ export class ClaudeAcpAgent implements Agent {
}
if (toolName === "ExitPlanMode") {
const options = [
const optionsAll = [
{ kind: "allow_always", name: 'Yes, and use "auto" mode', optionId: "auto" },
{
kind: "allow_always",
@@ -1382,12 +1417,20 @@ export class ClaudeAcpAgent implements Agent {
{ kind: "reject_once", name: "No, keep planning", optionId: "plan" },
];
if (ALLOW_BYPASS) {
options.unshift({
optionsAll.unshift({
kind: "allow_always",
name: "Yes, and bypass permissions",
optionId: "bypassPermissions",
});
}
// Filter against the session's currently-advertised modes so we never
// present options the active model can't honor (e.g. `auto` on Haiku).
// `bypassPermissions` is already covered by `availableModes` via
// `buildAvailableModes`/`ALLOW_BYPASS`. The `plan` option is a
// "keep planning" reject path; it's always present in `availableModes`.
const options = optionsAll.filter((o) =>
session.modes.availableModes.some((m) => m.id === o.optionId),
);
const response = await this.client.requestPermission({
options,
@@ -1406,27 +1449,30 @@ export class ClaudeAcpAgent implements Agent {
if (signal.aborted || response.outcome?.outcome === "cancelled") {
throw new Error("Tool use aborted");
}
const selectedMode =
response.outcome?.outcome === "selected" ? response.outcome.optionId : undefined;
const selectedModeWasOffered = options.some((option) => option.optionId === selectedMode);
if (
response.outcome?.outcome === "selected" &&
(response.outcome.optionId === "default" ||
response.outcome.optionId === "acceptEdits" ||
response.outcome.optionId === "auto" ||
response.outcome.optionId === "bypassPermissions")
selectedModeWasOffered &&
(selectedMode === "default" ||
selectedMode === "acceptEdits" ||
selectedMode === "auto" ||
selectedMode === "bypassPermissions")
) {
await this.client.sessionUpdate({
sessionId,
update: {
sessionUpdate: "current_mode_update",
currentModeId: response.outcome.optionId,
currentModeId: selectedMode,
},
});
await this.updateConfigOption(sessionId, "mode", response.outcome.optionId);
await this.updateConfigOption(sessionId, "mode", selectedMode);
return {
behavior: "allow",
updatedInput: toolInput,
updatedPermissions: suggestions ?? [
{ type: "setMode", mode: response.outcome.optionId, destination: "session" },
{ type: "setMode", mode: selectedMode, destination: "session" },
],
};
} else {
@@ -1524,7 +1570,7 @@ export class ClaudeAcpAgent implements Agent {
const session = this.sessions[sessionId];
if (!session) return;
await this.applyConfigOptionValue(session, configId, value);
await this.applyConfigOptionValue(sessionId, session, configId, value);
await this.client.sessionUpdate({
sessionId,
@@ -1536,13 +1582,16 @@ export class ClaudeAcpAgent implements Agent {
}
private async applyConfigOptionValue(
sessionId: string,
session: Session,
configId: string,
value: string,
): Promise<void> {
// Sync top-level session state
if (configId === "mode") {
session.modes = { ...session.modes, currentModeId: value };
session.configOptions = session.configOptions.map((o) =>
o.id === configId && typeof o.currentValue === "string" ? { ...o, currentValue: value } : o,
);
} else if (configId === "model") {
if (session.models.currentModelId !== value) {
// The cached context window was learned for the previous model; reset
@@ -1552,10 +1601,38 @@ export class ClaudeAcpAgent implements Agent {
session.contextWindowSize = inferContextWindowFromModel(value) ?? DEFAULT_CONTEXT_WINDOW;
}
session.models = { ...session.models, currentModelId: value };
}
// Update configOptions
if (configId === "model") {
// Recompute availableModes for the new model and clamp the current
// mode if the SDK no longer offers it (today: "auto" on Haiku).
// `ModelInfo.supportsAutoMode` is the canonical SDK signal.
const newModelInfo = session.modelInfos.find((m) => m.value === value);
const newAvailableModes = buildAvailableModes(newModelInfo);
// Capture BEFORE mutating session.modes so the log message reflects
// the invalidated mode rather than "default".
const previousModeId = session.modes.currentModeId;
let modeDowngraded = false;
if (!newAvailableModes.some((m) => m.id === previousModeId)) {
session.modes = {
availableModes: newAvailableModes,
currentModeId: "default",
};
try {
await session.query.setPermissionMode("default");
} catch (err) {
// Failing the entire model switch over a bookkeeping sync error is
// worse UX than logging and continuing; the user explicitly asked
// to change models. The next setPermissionMode from the user will
// either succeed or surface a fresh error.
this.logger.error(
`Failed to sync permissionMode to "default" after model switch invalidated "${previousModeId}":`,
err,
);
}
modeDowngraded = true;
} else {
session.modes = { ...session.modes, availableModes: newAvailableModes };
}
// Rebuild config options since effort levels depend on the selected model
const effortOpt = session.configOptions.find((o) => o.id === "effort");
const currentEffort =
@@ -1576,6 +1653,22 @@ export class ClaudeAcpAgent implements Agent {
effortLevel: newEffort as Settings["effortLevel"],
});
}
// Emit current_mode_update only after session.modes AND
// session.configOptions have been fully reconciled. This way, a failure
// in the configOptions/effort rebuild above can't leave the client with
// a clamped currentModeId but stale configOptions, and the notification
// still precedes the caller's config_option_update so order-sensitive
// clients update currentModeId before re-rendering the option list.
if (modeDowngraded) {
await this.client.sessionUpdate({
sessionId,
update: {
sessionUpdate: "current_mode_update",
currentModeId: "default",
},
});
}
} else {
session.configOptions = session.configOptions.map((o) =>
o.id === configId && typeof o.currentValue === "string" ? { ...o, currentValue: value } : o,
@@ -1701,6 +1794,7 @@ export class ClaudeAcpAgent implements Agent {
const permissionMode = resolvePermissionMode(
settingsManager.getSettings().permissions?.defaultMode,
this.logger,
);
// Extract options from _meta if provided
@@ -1760,7 +1854,7 @@ export class ClaudeAcpAgent implements Agent {
allowDangerouslySkipPermissions: ALLOW_BYPASS,
permissionMode,
canUseTool: this.canUseTool(sessionId),
pathToClaudeCodeExecutable: process.env.CLAUDE_CODE_EXECUTABLE,
pathToClaudeCodeExecutable: process.env.CLAUDE_CODE_EXECUTABLE ?? (await claudeCliPath()),
extraArgs: {
...userProvidedOptions?.extraArgs,
"replay-user-messages": "",
@@ -1839,46 +1933,52 @@ export class ClaudeAcpAgent implements Agent {
);
}
const models = await getAvailableModels(q, initializationResult.models, settingsManager);
const models = await getAvailableModels(
q,
initializationResult.models,
settingsManager,
this.logger,
);
const availableModes = [
{
id: "auto",
name: "Auto",
description: "Use a model classifier to approve/deny permission prompts",
},
{
id: "default",
name: "Default",
description: "Standard behavior, prompts for dangerous operations",
},
{
id: "acceptEdits",
name: "Accept Edits",
description: "Auto-accept file edit operations",
},
{
id: "plan",
name: "Plan Mode",
description: "Planning mode, no actual tool execution",
},
{
id: "dontAsk",
name: "Don't Ask",
description: "Don't prompt for permissions, deny if not pre-approved",
},
];
// Only works in non-root mode
if (ALLOW_BYPASS) {
availableModes.push({
id: "bypassPermissions",
name: "Bypass Permissions",
description: "Bypass all permission checks",
});
// Gate `auto` (and future model-specific modes) on the resolved model's
// `ModelInfo`. See `buildAvailableModes` for the canonical SDK signal.
const currentModelInfo = initializationResult.models.find(
(m) => m.value === models.currentModelId,
);
const availableModes = buildAvailableModes(currentModelInfo);
// Clamp `permissionMode` if the resolved session does not offer it. The
// common case is `permissions.defaultMode: "auto"` resolving to a model
// that does not support auto mode (e.g. Haiku); without this clamp the
// SDK would later throw `"auto mode unavailable for this model"` from
// `setPermissionMode`. Keep `permissionMode` as the resolved user intent
// (matches what was passed into `options.permissionMode` above) and use
// `effectiveMode` for the post-clamp value the session actually runs in.
let effectiveMode: PermissionMode = permissionMode;
if (!availableModes.some((m) => m.id === effectiveMode)) {
if (effectiveMode === "auto") {
this.logger.error(
`permissions.defaultMode "auto" is not available for model ` +
`"${models.currentModelId}"; falling back to "default".`,
);
} else {
this.logger.error(
`permissions.defaultMode "${effectiveMode}" is not available in ` +
`this session; falling back to "default".`,
);
}
effectiveMode = "default";
// Sync the SDK so it doesn't keep "auto" cached internally. Wrapped in
// try/catch since failing here would abort session creation entirely.
try {
await q.setPermissionMode("default");
} catch (err) {
this.logger.error("Failed to sync clamped permissionMode to SDK:", err);
}
}
const modes = {
currentModeId: permissionMode,
currentModeId: effectiveMode,
availableModes,
};
@@ -2021,6 +2121,58 @@ function createEnvForGateway(gatewayMeta?: GatewayAuthMeta) {
};
}
/**
* Build the list of permission modes the agent will advertise for the given
* model. `auto` is gated by `ModelInfo.supportsAutoMode === true`, which is
* the SDK's model-level availability signal. `undefined`/`false` both exclude
* `auto`. `bypassPermissions` is still gated by `ALLOW_BYPASS`.
*/
function buildAvailableModes(modelInfo: ModelInfo | undefined): SessionModeState["availableModes"] {
const modes: SessionModeState["availableModes"] = [];
// Only advertise "auto" when the SDK reports the model supports it.
if (modelInfo?.supportsAutoMode === true) {
modes.push({
id: "auto",
name: "Auto",
description: "Use a model classifier to approve/deny permission prompts",
});
}
modes.push(
{
id: "default",
name: "Default",
description: "Standard behavior, prompts for dangerous operations",
},
{
id: "acceptEdits",
name: "Accept Edits",
description: "Auto-accept file edit operations",
},
{
id: "plan",
name: "Plan Mode",
description: "Planning mode, no actual tool execution",
},
{
id: "dontAsk",
name: "Don't Ask",
description: "Don't prompt for permissions, deny if not pre-approved",
},
);
if (ALLOW_BYPASS) {
modes.push({
id: "bypassPermissions",
name: "Bypass Permissions",
description: "Bypass all permission checks",
});
}
return modes;
}
function buildConfigOptions(
modes: SessionModeState,
models: SessionModelState,
@@ -2087,7 +2239,7 @@ function buildConfigOptions(
id: "effort",
name: "Effort",
description: "Available effort levels for this model",
category: "effort",
category: "thought_level",
type: "select",
currentValue: validEffort,
options: effortOptions,
@@ -2170,10 +2322,27 @@ function resolveModelPreference(models: ModelInfo[], preference: string): ModelI
return bestMatch;
}
function resolveSettingsModel(
models: ModelInfo[],
settingsModel: unknown,
logger: Logger,
): ModelInfo | null {
if (settingsModel === undefined) {
return null;
}
if (typeof settingsModel !== "string") {
const typeLabel = settingsModel === null ? "null" : typeof settingsModel;
logger.error(`Ignoring model from settings: expected a string, got ${typeLabel}.`);
return null;
}
return resolveModelPreference(models, settingsModel);
}
async function getAvailableModels(
query: Query,
models: ModelInfo[],
settingsManager: SettingsManager,
logger: Logger,
): Promise<SessionModelState> {
const settings = settingsManager.getSettings();
@@ -2188,8 +2357,8 @@ async function getAvailableModels(
if (match) {
currentModel = match;
}
} else if (settings.model) {
const match = resolveModelPreference(models, settings.model);
} else {
const match = resolveSettingsModel(models, settings.model, logger);
if (match) {
currentModel = match;
}
@@ -2434,8 +2603,18 @@ export function toAcpNotifications(
onPostToolUseHook: async (toolUseId, toolInput, toolResponse) => {
const toolUse = toolUseCache[toolUseId];
if (toolUse) {
// Both `Edit` and `Write` produce a structuredPatch in their
// PostToolUse tool_response. For Edit the diff replaces the
// optimistic content built at tool_use time. For Write the
// optimistic content (built from `input.content` alone with
// `oldText: null`) shows "creation" semantics regardless of
// whether the file existed; the structuredPatch from the
// hook lets us emit the real diff for `type: "update"`. The
// helper returns `{}` if the response shape isn't usable.
const editDiff =
toolUse.name === "Edit" ? toolUpdateFromEditToolResponse(toolResponse) : {};
toolUse.name === "Edit" || toolUse.name === "Write"
? toolUpdateFromDiffToolResponse(toolResponse)
: {};
const update: SessionNotification["update"] = {
_meta: {
claudeCode: {

View File

@@ -39,7 +39,7 @@ describe("ClaudeAcpAgent settings", () => {
initializationResult: async () => ({
models: [
{
value: "claude-sonnet-4-5",
value: "claude-sonnet-4-6",
displayName: "Claude Sonnet 4.5",
description: "Default",
},
@@ -145,12 +145,12 @@ describe("ClaudeAcpAgent settings", () => {
expect(response.modes.currentModeId).toBe("default");
});
it("throws when permissions.defaultMode is not a string", async () => {
it("falls back to 'default' when permissions.defaultMode is invalid", async () => {
await fs.promises.writeFile(
path.join(tempDir, "settings.json"),
JSON.stringify({
permissions: {
defaultMode: 123,
defaultMode: "not-a-real-mode",
},
}),
);
@@ -158,18 +158,208 @@ describe("ClaudeAcpAgent settings", () => {
const projectDir = path.join(tempDir, "project");
await fs.promises.mkdir(projectDir, { recursive: true });
mockQuery();
const { getCapturedOptions } = mockQuery();
const { ClaudeAcpAgent } = await import("../acp-agent.js");
const agent: ClaudeAcpAgentType = new ClaudeAcpAgent(createMockClient());
await expect(
(agent as any).createSession({
const response = await (agent as any).createSession({
cwd: projectDir,
mcpServers: [],
_meta: { disableBuiltInTools: true },
});
// Bad mode is ignored at the usage site; session creation must not throw.
expect(getCapturedOptions().permissionMode).toBe("default");
expect(response.modes.currentModeId).toBe("default");
});
it("ignores model from settings when it is not a string", async () => {
await fs.promises.writeFile(
path.join(tempDir, "settings.json"),
JSON.stringify({
model: 123,
}),
);
const projectDir = path.join(tempDir, "project");
await fs.promises.mkdir(projectDir, { recursive: true });
const { setModelSpy } = mockQuery();
const { ClaudeAcpAgent } = await import("../acp-agent.js");
const agent: ClaudeAcpAgentType = new ClaudeAcpAgent(createMockClient());
const response = await (agent as any).createSession({
cwd: projectDir,
mcpServers: [],
_meta: { disableBuiltInTools: true },
});
// Bad model is ignored at the usage site; falls back to the first SDK model.
expect(setModelSpy).toHaveBeenCalledWith("claude-sonnet-4-6");
expect(response.models.currentModelId).toBe("claude-sonnet-4-6");
});
describe("auto mode availability per model", () => {
function mockQueryWithModels(models: any[]): {
getCapturedOptions: () => any;
setModelSpy: ReturnType<typeof vi.fn>;
setPermissionModeSpy: ReturnType<typeof vi.fn>;
} {
let capturedOptions: any;
const setModelSpy = vi.fn();
const setPermissionModeSpy = vi.fn();
querySpy.mockImplementation(({ options }: any) => {
capturedOptions = options;
return {
initializationResult: async () => ({ models }),
setModel: setModelSpy,
setPermissionMode: setPermissionModeSpy,
supportedCommands: async () => [],
} as any;
});
return {
getCapturedOptions: () => capturedOptions,
setModelSpy,
setPermissionModeSpy,
};
}
it("omits `auto` from availableModes when the resolved model lacks supportsAutoMode", async () => {
const projectDir = path.join(tempDir, "project");
await fs.promises.mkdir(projectDir, { recursive: true });
mockQueryWithModels([
{
value: "claude-haiku-4-5",
displayName: "Claude Haiku",
description: "Fast",
// supportsAutoMode intentionally omitted
},
]);
const { ClaudeAcpAgent } = await import("../acp-agent.js");
const agent: ClaudeAcpAgentType = new ClaudeAcpAgent(createMockClient());
const response = await (agent as any).createSession({
cwd: projectDir,
mcpServers: [],
_meta: { disableBuiltInTools: true },
}),
).rejects.toThrow("Invalid permissions.defaultMode");
});
const modeIds: string[] = response.modes.availableModes.map((m: any) => m.id);
expect(modeIds).not.toContain("auto");
expect(modeIds).toEqual(
expect.arrayContaining(["default", "acceptEdits", "plan", "dontAsk"]),
);
});
it("includes `auto` when the resolved model has supportsAutoMode: true", async () => {
const projectDir = path.join(tempDir, "project");
await fs.promises.mkdir(projectDir, { recursive: true });
mockQueryWithModels([
{
value: "claude-opus-4-5",
displayName: "Claude Opus",
description: "Most capable",
supportsAutoMode: true,
},
]);
const { ClaudeAcpAgent } = await import("../acp-agent.js");
const agent: ClaudeAcpAgentType = new ClaudeAcpAgent(createMockClient());
const response = await (agent as any).createSession({
cwd: projectDir,
mcpServers: [],
_meta: { disableBuiltInTools: true },
});
const modeIds: string[] = response.modes.availableModes.map((m: any) => m.id);
expect(modeIds).toContain("auto");
});
it("clamps permissions.defaultMode='auto' to 'default' on a model that lacks supportsAutoMode", async () => {
await fs.promises.writeFile(
path.join(tempDir, "settings.json"),
JSON.stringify({ permissions: { defaultMode: "auto" } }),
);
const projectDir = path.join(tempDir, "project");
await fs.promises.mkdir(projectDir, { recursive: true });
const { getCapturedOptions, setPermissionModeSpy } = mockQueryWithModels([
{
value: "claude-haiku-4-5",
displayName: "Claude Haiku",
description: "Fast",
// supportsAutoMode intentionally omitted
},
]);
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
try {
const { ClaudeAcpAgent } = await import("../acp-agent.js");
const agent: ClaudeAcpAgentType = new ClaudeAcpAgent(createMockClient());
const response = await (agent as any).createSession({
cwd: projectDir,
mcpServers: [],
_meta: { disableBuiltInTools: true },
});
// Options.permissionMode is built before init resolves, so it still
// carries the user-typed value; the SDK was synced via
// setPermissionMode after we discovered the model can't honor it.
expect(getCapturedOptions().permissionMode).toBe("auto");
expect(setPermissionModeSpy).toHaveBeenCalledWith("default");
expect(response.modes.currentModeId).toBe("default");
expect(response.modes.availableModes.map((m: any) => m.id)).not.toContain("auto");
// A descriptive warning was logged so operators see the clamp.
const messages = errorSpy.mock.calls.map((c) => c.join(" "));
expect(messages.some((m) => m.includes("auto") && m.includes("claude-haiku-4-5"))).toBe(
true,
);
} finally {
errorSpy.mockRestore();
}
});
it("does not clamp permissions.defaultMode='auto' on a model that supports auto", async () => {
await fs.promises.writeFile(
path.join(tempDir, "settings.json"),
JSON.stringify({ permissions: { defaultMode: "auto" } }),
);
const projectDir = path.join(tempDir, "project");
await fs.promises.mkdir(projectDir, { recursive: true });
const { getCapturedOptions, setPermissionModeSpy } = mockQueryWithModels([
{
value: "claude-opus-4-5",
displayName: "Claude Opus",
description: "Most capable",
supportsAutoMode: true,
},
]);
const { ClaudeAcpAgent } = await import("../acp-agent.js");
const agent: ClaudeAcpAgentType = new ClaudeAcpAgent(createMockClient());
const response = await (agent as any).createSession({
cwd: projectDir,
mcpServers: [],
_meta: { disableBuiltInTools: true },
});
expect(getCapturedOptions().permissionMode).toBe("auto");
expect(setPermissionModeSpy).not.toHaveBeenCalled();
expect(response.modes.currentModeId).toBe("auto");
});
});
it("resolves model aliases like opus[1m] to the correct model", async () => {

View File

@@ -22,7 +22,7 @@ import {
toolInfoFromToolUse,
toDisplayPath,
toolUpdateFromToolResult,
toolUpdateFromEditToolResponse,
toolUpdateFromDiffToolResponse,
} from "../tools.js";
import {
toAcpNotifications,
@@ -998,17 +998,17 @@ describe("toDisplayPath", () => {
});
});
describe("toolUpdateFromEditToolResponse", () => {
describe("toolUpdateFromDiffToolResponse", () => {
it("should return empty for non-object input", () => {
expect(toolUpdateFromEditToolResponse(null)).toEqual({});
expect(toolUpdateFromEditToolResponse(undefined)).toEqual({});
expect(toolUpdateFromEditToolResponse("string")).toEqual({});
expect(toolUpdateFromDiffToolResponse(null)).toEqual({});
expect(toolUpdateFromDiffToolResponse(undefined)).toEqual({});
expect(toolUpdateFromDiffToolResponse("string")).toEqual({});
});
it("should return empty when filePath or structuredPatch is missing", () => {
expect(toolUpdateFromEditToolResponse({})).toEqual({});
expect(toolUpdateFromEditToolResponse({ filePath: "/foo.ts" })).toEqual({});
expect(toolUpdateFromEditToolResponse({ structuredPatch: [] })).toEqual({});
expect(toolUpdateFromDiffToolResponse({})).toEqual({});
expect(toolUpdateFromDiffToolResponse({ filePath: "/foo.ts" })).toEqual({});
expect(toolUpdateFromDiffToolResponse({ structuredPatch: [] })).toEqual({});
});
it("should build diff content from a single-hunk structuredPatch", () => {
@@ -1025,7 +1025,7 @@ describe("toolUpdateFromEditToolResponse", () => {
],
};
expect(toolUpdateFromEditToolResponse(toolResponse)).toEqual({
expect(toolUpdateFromDiffToolResponse(toolResponse)).toEqual({
content: [
{
type: "diff",
@@ -1059,7 +1059,7 @@ describe("toolUpdateFromEditToolResponse", () => {
],
};
expect(toolUpdateFromEditToolResponse(toolResponse)).toEqual({
expect(toolUpdateFromDiffToolResponse(toolResponse)).toEqual({
content: [
{
type: "diff",
@@ -1095,7 +1095,7 @@ describe("toolUpdateFromEditToolResponse", () => {
],
};
expect(toolUpdateFromEditToolResponse(toolResponse)).toEqual({
expect(toolUpdateFromDiffToolResponse(toolResponse)).toEqual({
content: [
{
type: "diff",
@@ -1114,7 +1114,7 @@ describe("toolUpdateFromEditToolResponse", () => {
structuredPatch: [],
};
expect(toolUpdateFromEditToolResponse(toolResponse)).toEqual({});
expect(toolUpdateFromDiffToolResponse(toolResponse)).toEqual({});
});
});
@@ -2502,7 +2502,12 @@ describe("usage_update computation", () => {
const session = agent.sessions["test-session"];
expect(session.contextWindowSize).toBe(200000);
await (agent as any).applyConfigOptionValue(session, "model", "claude-opus-4-6-1m");
await (agent as any).applyConfigOptionValue(
"test-session",
session,
"model",
"claude-opus-4-6-1m",
);
expect(session.contextWindowSize).toBe(1000000);
await agent.prompt({ sessionId: "test-session", prompt: [{ type: "text", text: "test" }] });
@@ -2587,7 +2592,12 @@ describe("usage_update computation", () => {
session.models = { ...session.models, currentModelId: "claude-opus-4-6-1m" };
// User flips the selector to a 200k model.
await (agent as any).applyConfigOptionValue(session, "model", "claude-sonnet-4-6");
await (agent as any).applyConfigOptionValue(
"test-session",
session,
"model",
"claude-sonnet-4-6",
);
await agent.prompt({ sessionId: "test-session", prompt: [{ type: "text", text: "test" }] });

View File

@@ -15,9 +15,17 @@ vi.mock("@anthropic-ai/claude-agent-sdk", async () => ({
capturedOptions = options;
return {
initializationResult: async () => ({
models: [{ value: "claude-sonnet-4-5", displayName: "Claude Sonnet", description: "Fast" }],
models: [
{
value: "claude-sonnet-4-6",
displayName: "Claude Sonnet",
description: "Fast",
supportsAutoMode: true,
},
],
}),
setModel: async () => {},
setPermissionMode: async () => {},
supportedCommands: async () => [],
[Symbol.asyncIterator]: async function* () {},
};

View File

@@ -5,9 +5,12 @@ import { AgentSideConnection } from "@agentclientprotocol/sdk";
const mockQuery = vi.hoisted(() =>
vi.fn(() => ({
initializationResult: vi.fn().mockResolvedValue({
models: [{ value: "id", displayName: "name", description: "description" }],
models: [
{ value: "id", displayName: "name", description: "description", supportsAutoMode: true },
],
}),
setModel: vi.fn(),
setPermissionMode: vi.fn(),
supportedCommands: vi.fn().mockResolvedValue([]),
})),
);

View File

@@ -15,10 +15,16 @@ vi.mock("@anthropic-ai/claude-agent-sdk", async () => {
return {
initializationResult: async () => ({
models: [
{ value: "claude-sonnet-4-5", displayName: "Claude Sonnet", description: "Fast" },
{
value: "claude-sonnet-4-6",
displayName: "Claude Sonnet",
description: "Fast",
supportsAutoMode: true,
},
],
}),
setModel: async () => {},
setPermissionMode: async () => {},
supportedCommands: async () => [],
[Symbol.asyncIterator]: async function* () {},
};
@@ -414,7 +420,7 @@ describe("createSession options merging", () => {
claudeCode: {
options: {
settings: {
model: "claude-sonnet-4-5",
model: "claude-sonnet-4-6",
modelOverrides: { "claude-opus-4-6": "meta-value" },
},
},
@@ -424,7 +430,7 @@ describe("createSession options merging", () => {
// _meta settings take precedence; env var is ignored entirely
expect(capturedOptions!.settings).toEqual({
model: "claude-sonnet-4-5",
model: "claude-sonnet-4-6",
modelOverrides: { "claude-opus-4-6": "meta-value" },
});
});

View File

@@ -1,5 +1,12 @@
import { describe, it, expect } from "vitest";
import { resolvePermissionMode } from "../acp-agent.js";
import { describe, it, expect, vi } from "vitest";
import { resolvePermissionMode, type Logger } from "../acp-agent.js";
function mockLogger() {
const error = vi.fn<(...args: any[]) => void>();
const log = vi.fn<(...args: any[]) => void>();
const logger: Logger = { log, error };
return { logger, error, log };
}
describe("resolvePermissionMode", () => {
it("returns 'default' when no mode is provided", () => {
@@ -26,18 +33,25 @@ describe("resolvePermissionMode", () => {
expect(resolvePermissionMode(" dontAsk ")).toBe("dontAsk");
});
it("throws on non-string values", () => {
expect(() => resolvePermissionMode(123)).toThrow("expected a string");
expect(() => resolvePermissionMode(true)).toThrow("expected a string");
expect(() => resolvePermissionMode({})).toThrow("expected a string");
it("falls back to 'default' and logs on non-string values", () => {
for (const value of [123, true, {}]) {
const { logger, error } = mockLogger();
expect(resolvePermissionMode(value, logger)).toBe("default");
expect(error).toHaveBeenCalledWith(expect.stringContaining("expected a string"));
}
});
it("throws on empty string", () => {
expect(() => resolvePermissionMode("")).toThrow("expected a non-empty string");
expect(() => resolvePermissionMode(" ")).toThrow("expected a non-empty string");
it("falls back to 'default' and logs on empty string", () => {
for (const value of ["", " "]) {
const { logger, error } = mockLogger();
expect(resolvePermissionMode(value, logger)).toBe("default");
expect(error).toHaveBeenCalledWith(expect.stringContaining("expected a non-empty string"));
}
});
it("throws on unknown mode", () => {
expect(() => resolvePermissionMode("yolo")).toThrow("Invalid permissions.defaultMode: yolo");
it("falls back to 'default' and logs on unknown mode", () => {
const { logger, error } = mockLogger();
expect(resolvePermissionMode("yolo", logger)).toBe("default");
expect(error).toHaveBeenCalledWith(expect.stringContaining("yolo"));
});
});

View File

@@ -30,7 +30,7 @@ const MOCK_MODELS = {
currentModelId: "claude-opus-4-5",
availableModels: [
{ modelId: "claude-opus-4-5", name: "Claude Opus", description: "Most capable" },
{ modelId: "claude-sonnet-4-5", name: "Claude Sonnet", description: "Balanced" },
{ modelId: "claude-sonnet-4-6", name: "Claude Sonnet", description: "Balanced" },
],
};
@@ -242,10 +242,10 @@ describe("session config options", () => {
await agent.setSessionConfigOption({
sessionId: SESSION_ID,
configId: "model",
value: "claude-sonnet-4-5",
value: "claude-sonnet-4-6",
});
expect(setModelSpy).toHaveBeenCalledWith("claude-sonnet-4-5");
expect(setModelSpy).toHaveBeenCalledWith("claude-sonnet-4-6");
const configUpdate = sessionUpdates.find(
(n) => n.update.sessionUpdate === "config_option_update",
@@ -273,7 +273,7 @@ describe("session config options", () => {
value: "sonnet",
});
expect(setModelSpy).toHaveBeenCalledWith("claude-sonnet-4-5");
expect(setModelSpy).toHaveBeenCalledWith("claude-sonnet-4-6");
});
it("resolves display name to model ID", async () => {
@@ -283,19 +283,19 @@ describe("session config options", () => {
value: "Claude Sonnet",
});
expect(setModelSpy).toHaveBeenCalledWith("claude-sonnet-4-5");
expect(setModelSpy).toHaveBeenCalledWith("claude-sonnet-4-6");
});
it("still works with exact model ID", async () => {
const response = await agent.setSessionConfigOption({
sessionId: SESSION_ID,
configId: "model",
value: "claude-sonnet-4-5",
value: "claude-sonnet-4-6",
});
expect(setModelSpy).toHaveBeenCalledWith("claude-sonnet-4-5");
expect(setModelSpy).toHaveBeenCalledWith("claude-sonnet-4-6");
const modelOption = response.configOptions.find((o) => o.id === "model");
expect(modelOption?.currentValue).toBe("claude-sonnet-4-5");
expect(modelOption?.currentValue).toBe("claude-sonnet-4-6");
});
it("throws for completely invalid model value", async () => {
@@ -384,7 +384,7 @@ describe("session config options", () => {
it("sends config_option_update when model is changed via setSessionModel", async () => {
await agent.unstable_setSessionModel({
sessionId: SESSION_ID,
modelId: "claude-sonnet-4-5",
modelId: "claude-sonnet-4-6",
});
const configUpdate = sessionUpdates.find(
@@ -394,7 +394,7 @@ describe("session config options", () => {
expect(configUpdate?.update).toMatchObject({
sessionUpdate: "config_option_update",
configOptions: expect.arrayContaining([
expect.objectContaining({ id: "model", currentValue: "claude-sonnet-4-5" }),
expect.objectContaining({ id: "model", currentValue: "claude-sonnet-4-6" }),
]),
});
});
@@ -402,7 +402,7 @@ describe("session config options", () => {
it("updates stored configOptions currentValue when model changes", async () => {
await agent.unstable_setSessionModel({
sessionId: SESSION_ID,
modelId: "claude-sonnet-4-5",
modelId: "claude-sonnet-4-6",
});
const session = (
@@ -411,7 +411,7 @@ describe("session config options", () => {
}
).sessions[SESSION_ID];
const modelOption = session.configOptions.find((o) => o.id === "model");
expect(modelOption?.currentValue).toBe("claude-sonnet-4-5");
expect(modelOption?.currentValue).toBe("claude-sonnet-4-6");
});
it("includes updated effort in config_option_update when model drops effort support", async () => {
@@ -425,7 +425,7 @@ describe("session config options", () => {
supportedEffortLevels: ["low", "medium", "high"],
},
{
value: "claude-sonnet-4-5",
value: "claude-sonnet-4-6",
displayName: "Claude Sonnet",
description: "Balanced",
supportsEffort: false,
@@ -434,7 +434,7 @@ describe("session config options", () => {
await agent.unstable_setSessionModel({
sessionId: SESSION_ID,
modelId: "claude-sonnet-4-5",
modelId: "claude-sonnet-4-6",
});
const configUpdate = sessionUpdates.find(
@@ -463,7 +463,7 @@ describe("session config options", () => {
supportedEffortLevels: ["low", "medium", "high", "max"],
},
{
value: "claude-sonnet-4-5",
value: "claude-sonnet-4-6",
displayName: "Claude Sonnet",
description: "Balanced",
supportsEffort: true,
@@ -473,7 +473,7 @@ describe("session config options", () => {
await agent.unstable_setSessionModel({
sessionId: SESSION_ID,
modelId: "claude-sonnet-4-5",
modelId: "claude-sonnet-4-6",
});
const configUpdate = sessionUpdates.find(
@@ -495,7 +495,7 @@ describe("session config options", () => {
await agent.unstable_setSessionModel({
sessionId: SESSION_ID,
modelId: "claude-sonnet-4-5",
modelId: "claude-sonnet-4-6",
});
const configUpdate = sessionUpdates.find(
@@ -532,7 +532,7 @@ describe("session config options", () => {
await agent.setSessionConfigOption({
sessionId: SESSION_ID,
configId: "model",
value: "claude-sonnet-4-5",
value: "claude-sonnet-4-6",
});
const configUpdates = sessionUpdates.filter(
@@ -622,7 +622,7 @@ describe("session config options", () => {
supportedEffortLevels: ["low", "medium", "high"],
},
{
value: "claude-sonnet-4-5",
value: "claude-sonnet-4-6",
displayName: "Claude Sonnet",
description: "Balanced",
supportsEffort: false,
@@ -632,7 +632,7 @@ describe("session config options", () => {
const response = await agent.setSessionConfigOption({
sessionId: SESSION_ID,
configId: "model",
value: "claude-sonnet-4-5",
value: "claude-sonnet-4-6",
});
const effortOption = response.configOptions.find((o) => o.id === "effort");
@@ -650,7 +650,7 @@ describe("session config options", () => {
supportedEffortLevels: ["low", "medium", "high"],
},
{
value: "claude-sonnet-4-5",
value: "claude-sonnet-4-6",
displayName: "Claude Sonnet",
description: "Balanced",
supportsEffort: false,
@@ -660,7 +660,7 @@ describe("session config options", () => {
await agent.setSessionConfigOption({
sessionId: SESSION_ID,
configId: "model",
value: "claude-sonnet-4-5",
value: "claude-sonnet-4-6",
});
expect(applyFlagSettingsSpy).toHaveBeenCalledWith({ effortLevel: undefined });
@@ -669,7 +669,7 @@ describe("session config options", () => {
it("adds effort option when switching to a model that supports effort", async () => {
const session = (agent as unknown as { sessions: Record<string, any> }).sessions[SESSION_ID];
// Start with sonnet (no effort) as current
session.models = { ...session.models, currentModelId: "claude-sonnet-4-5" };
session.models = { ...session.models, currentModelId: "claude-sonnet-4-6" };
session.modelInfos = [
{
value: "claude-opus-4-5",
@@ -679,7 +679,7 @@ describe("session config options", () => {
supportedEffortLevels: ["low", "medium", "high"],
},
{
value: "claude-sonnet-4-5",
value: "claude-sonnet-4-6",
displayName: "Claude Sonnet",
description: "Balanced",
supportsEffort: false,
@@ -715,7 +715,7 @@ describe("session config options", () => {
supportedEffortLevels: ["low", "medium", "high", "max"],
},
{
value: "claude-sonnet-4-5",
value: "claude-sonnet-4-6",
displayName: "Claude Sonnet",
description: "Balanced",
supportsEffort: true,
@@ -726,7 +726,7 @@ describe("session config options", () => {
const response = await agent.setSessionConfigOption({
sessionId: SESSION_ID,
configId: "model",
value: "claude-sonnet-4-5",
value: "claude-sonnet-4-6",
});
const effortOption = response.configOptions.find((o) => o.id === "effort");
@@ -749,7 +749,7 @@ describe("session config options", () => {
const response = await agent.setSessionConfigOption({
sessionId: SESSION_ID,
configId: "model",
value: "claude-sonnet-4-5",
value: "claude-sonnet-4-6",
});
const effortOption = response.configOptions.find((o) => o.id === "effort");
@@ -778,10 +778,10 @@ describe("session config options", () => {
await agent.setSessionConfigOption({
sessionId: SESSION_ID,
configId: "model",
value: "claude-sonnet-4-5",
value: "claude-sonnet-4-6",
});
expect(setModelSpy).toHaveBeenCalledWith("claude-sonnet-4-5");
expect(setModelSpy).toHaveBeenCalledWith("claude-sonnet-4-6");
});
it("setSessionMode also syncs configOptions", async () => {
@@ -798,7 +798,7 @@ describe("session config options", () => {
it("setSessionModel also syncs configOptions", async () => {
await agent.unstable_setSessionModel({
sessionId: SESSION_ID,
modelId: "claude-sonnet-4-5",
modelId: "claude-sonnet-4-6",
});
const session = (
@@ -807,8 +807,395 @@ describe("session config options", () => {
}
).sessions[SESSION_ID];
expect(session.configOptions.find((o) => o.id === "model")?.currentValue).toBe(
"claude-sonnet-4-5",
"claude-sonnet-4-6",
);
});
});
describe("auto mode availability per model", () => {
/**
* Augment the session populated by `populateSession()` with a Haiku entry
* (no `supportsAutoMode`), Opus + Sonnet entries with `supportsAutoMode:
* true`, and seed `availableModes` so it currently includes `auto`. This
* exercises the per-model recomputation done by `applyConfigOptionValue`
* on a model switch.
*/
function setupHaikuOpusSession(currentModeId: string = "default") {
const session = (agent as unknown as { sessions: Record<string, any> }).sessions[SESSION_ID];
session.modelInfos = [
{
value: "claude-opus-4-5",
displayName: "Claude Opus",
description: "Most capable",
supportsEffort: true,
supportedEffortLevels: ["low", "medium", "high"],
supportsAutoMode: true,
},
{
value: "claude-sonnet-4-6",
displayName: "Claude Sonnet",
description: "Balanced",
supportsEffort: true,
supportedEffortLevels: ["low", "medium", "high"],
supportsAutoMode: true,
},
{
value: "claude-haiku-4-5",
displayName: "Claude Haiku",
description: "Fast",
supportsEffort: true,
supportedEffortLevels: ["low", "medium", "high"],
// supportsAutoMode intentionally omitted
},
];
session.models = {
currentModelId: "claude-opus-4-5",
availableModels: [
{ modelId: "claude-opus-4-5", name: "Claude Opus", description: "Most capable" },
{ modelId: "claude-sonnet-4-6", name: "Claude Sonnet", description: "Balanced" },
{ modelId: "claude-haiku-4-5", name: "Claude Haiku", description: "Fast" },
],
};
session.modes = {
currentModeId,
availableModes: [
{
id: "auto",
name: "Auto",
description: "Use a model classifier to approve/deny permission prompts",
},
{
id: "default",
name: "Default",
description: "Standard behavior, prompts for dangerous operations",
},
{
id: "acceptEdits",
name: "Accept Edits",
description: "Auto-accept file edit operations",
},
{ id: "plan", name: "Plan Mode", description: "Planning mode" },
{
id: "dontAsk",
name: "Don't Ask",
description: "Don't prompt for permissions, deny if not pre-approved",
},
],
};
// Reflect the seeded availableModes/availableModels in configOptions so
// the pre-state matches what `createSession` would have produced for
// Opus, and `setSessionConfigOption` validation can accept the seeded
// model ids (notably the new Haiku entry).
session.configOptions = session.configOptions.map((o: any) => {
if (o.id === "mode") {
return {
...o,
currentValue: currentModeId,
options: session.modes.availableModes.map((m: any) => ({
value: m.id,
name: m.name,
description: m.description,
})),
};
}
if (o.id === "model") {
return {
...o,
currentValue: session.models.currentModelId,
options: session.models.availableModels.map((m: any) => ({
value: m.modelId,
name: m.name,
description: m.description,
})),
};
}
return o;
});
return session;
}
beforeEach(() => {
populateSession();
});
it("drops `auto` from available modes when switching to Haiku", async () => {
setupHaikuOpusSession("default");
await agent.unstable_setSessionModel({
sessionId: SESSION_ID,
modelId: "claude-haiku-4-5",
});
const configUpdate = sessionUpdates.find(
(n) => n.update.sessionUpdate === "config_option_update",
);
expect(configUpdate).toBeDefined();
const modeOption = (configUpdate?.update as any).configOptions.find(
(o: any) => o.id === "mode",
);
expect(modeOption).toBeDefined();
const modeValues = modeOption.options.map((o: any) => o.value);
expect(modeValues).not.toContain("auto");
expect(modeValues).toEqual(
expect.arrayContaining(["default", "acceptEdits", "plan", "dontAsk"]),
);
});
it("clamps to `default` and emits current_mode_update when Opus(auto) → Haiku", async () => {
setupHaikuOpusSession("auto");
await agent.unstable_setSessionModel({
sessionId: SESSION_ID,
modelId: "claude-haiku-4-5",
});
// SDK was synced to "default".
expect(setPermissionModeSpy).toHaveBeenCalledWith("default");
// current_mode_update was emitted before config_option_update so a
// client applying notifications in order observes the mode change
// before re-rendering the config-option list.
const modeUpdateIdx = sessionUpdates.findIndex(
(n) => n.update.sessionUpdate === "current_mode_update",
);
const configUpdateIdx = sessionUpdates.findIndex(
(n) => n.update.sessionUpdate === "config_option_update",
);
expect(modeUpdateIdx).toBeGreaterThanOrEqual(0);
expect(configUpdateIdx).toBeGreaterThanOrEqual(0);
expect(modeUpdateIdx).toBeLessThan(configUpdateIdx);
expect((sessionUpdates[modeUpdateIdx].update as any).currentModeId).toBe("default");
// configOptions reflect the clamped mode.
const modeOption = (sessionUpdates[configUpdateIdx].update as any).configOptions.find(
(o: any) => o.id === "mode",
);
expect(modeOption.currentValue).toBe("default");
});
it("re-adds `auto` when switching from Haiku back to Opus", async () => {
const session = setupHaikuOpusSession("default");
// Pretend Haiku is the current model with no `auto`.
session.models.currentModelId = "claude-haiku-4-5";
session.modes.availableModes = session.modes.availableModes.filter(
(m: any) => m.id !== "auto",
);
const modeOpt = session.configOptions.find((o: any) => o.id === "mode");
modeOpt.options = session.modes.availableModes.map((m: any) => ({
value: m.id,
name: m.name,
description: m.description,
}));
await agent.unstable_setSessionModel({
sessionId: SESSION_ID,
modelId: "claude-opus-4-5",
});
const configUpdate = sessionUpdates.find(
(n) => n.update.sessionUpdate === "config_option_update",
);
expect(configUpdate).toBeDefined();
const modeOption = (configUpdate?.update as any).configOptions.find(
(o: any) => o.id === "mode",
);
const modeValues = modeOption.options.map((o: any) => o.value);
expect(modeValues).toContain("auto");
// The current mode ("default") is still valid on Opus, so no
// current_mode_update should have been emitted by the model switch.
const modeUpdates = sessionUpdates.filter(
(n) => n.update.sessionUpdate === "current_mode_update",
);
expect(modeUpdates).toHaveLength(0);
});
it("preserves the current mode when it remains valid after a model switch", async () => {
setupHaikuOpusSession("plan");
await agent.unstable_setSessionModel({
sessionId: SESSION_ID,
modelId: "claude-haiku-4-5",
});
// `plan` is in availableModes for both Opus and Haiku, so no clamp.
expect(setPermissionModeSpy).not.toHaveBeenCalledWith("default");
const modeUpdates = sessionUpdates.filter(
(n) => n.update.sessionUpdate === "current_mode_update",
);
expect(modeUpdates).toHaveLength(0);
const configUpdate = sessionUpdates.find(
(n) => n.update.sessionUpdate === "config_option_update",
);
const modeOption = (configUpdate?.update as any).configOptions.find(
(o: any) => o.id === "mode",
);
expect(modeOption.currentValue).toBe("plan");
});
it("clamps mode and emits current_mode_update via setSessionConfigOption(model)", async () => {
// Mirrors the unstable_setSessionModel(auto → Haiku) test, but goes
// through the request/response API. The `current_mode_update` side
// effect must still fire so clients learn about the clamp regardless of
// which entry point triggered the model switch.
setupHaikuOpusSession("auto");
const response = await agent.setSessionConfigOption({
sessionId: SESSION_ID,
configId: "model",
value: "claude-haiku-4-5",
});
expect(setPermissionModeSpy).toHaveBeenCalledWith("default");
const modeUpdates = sessionUpdates.filter(
(n) => n.update.sessionUpdate === "current_mode_update",
);
expect(modeUpdates).toHaveLength(1);
expect((modeUpdates[0].update as any).currentModeId).toBe("default");
// setSessionConfigOption is a request/response API: it returns the new
// configOptions in the response rather than emitting a
// config_option_update notification.
const configUpdates = sessionUpdates.filter(
(n) => n.update.sessionUpdate === "config_option_update",
);
expect(configUpdates).toHaveLength(0);
const modeOption = response.configOptions.find((o: any) => o.id === "mode");
expect(modeOption).toBeDefined();
expect((modeOption as any).currentValue).toBe("default");
expect((modeOption as any).options.map((o: any) => o.value)).not.toContain("auto");
});
it("rejects direct setSessionMode to `auto` when the active model does not offer it", async () => {
const session = setupHaikuOpusSession("default");
session.models.currentModelId = "claude-haiku-4-5";
session.modes.availableModes = session.modes.availableModes.filter(
(mode: any) => mode.id !== "auto",
);
await expect(agent.setSessionMode({ sessionId: SESSION_ID, modeId: "auto" })).rejects.toThrow(
"Mode auto is not available in this session",
);
expect(setPermissionModeSpy).not.toHaveBeenCalledWith("auto");
expect(sessionUpdates).toHaveLength(0);
});
});
describe("ExitPlanMode permission options filtered by availableModes", () => {
let capturedPermissionRequest: any;
let permissionResponse: any;
beforeEach(() => {
capturedPermissionRequest = null;
permissionResponse = { outcome: { outcome: "cancelled" } };
// Replace the default mock client with one that captures the
// requestPermission call so we can assert on the offered options.
(agent as any).client = {
sessionUpdate: async (notification: SessionNotification) => {
sessionUpdates.push(notification);
},
requestPermission: async (params: any) => {
capturedPermissionRequest = params;
return permissionResponse;
},
readTextFile: async () => ({ content: "" }),
writeTextFile: async () => ({}),
};
populateSession();
});
it("omits the `auto` option on a model without supportsAutoMode", async () => {
const session = (agent as unknown as { sessions: Record<string, any> }).sessions[SESSION_ID];
// Haiku-shaped session: availableModes does NOT include `auto`.
session.modes = {
currentModeId: "plan",
availableModes: [
{ id: "default", name: "Default", description: "Standard" },
{ id: "acceptEdits", name: "Accept Edits", description: "Auto-accept edits" },
{ id: "plan", name: "Plan Mode", description: "Planning mode" },
{ id: "dontAsk", name: "Don't Ask", description: "Deny if not pre-approved" },
],
};
const canUseTool = (agent as any).canUseTool(SESSION_ID);
const signal = new AbortController().signal;
try {
await canUseTool(
"ExitPlanMode",
{ plan: "do stuff" },
{ signal, suggestions: undefined, toolUseID: "toolu_1" },
);
} catch {
// The mock client returns `cancelled`, which makes canUseTool throw.
// We only care about the captured requestPermission options.
}
expect(capturedPermissionRequest).not.toBeNull();
const optionIds = capturedPermissionRequest.options.map((o: any) => o.optionId);
expect(optionIds).not.toContain("auto");
expect(optionIds).toEqual(expect.arrayContaining(["default", "acceptEdits", "plan"]));
});
it("denies a selected `auto` option if the client did not receive that option", async () => {
const session = (agent as unknown as { sessions: Record<string, any> }).sessions[SESSION_ID];
session.modes = {
currentModeId: "plan",
availableModes: [
{ id: "default", name: "Default", description: "Standard" },
{ id: "acceptEdits", name: "Accept Edits", description: "Auto-accept edits" },
{ id: "plan", name: "Plan Mode", description: "Planning mode" },
{ id: "dontAsk", name: "Don't Ask", description: "Deny if not pre-approved" },
],
};
permissionResponse = { outcome: { outcome: "selected", optionId: "auto" } };
const canUseTool = (agent as any).canUseTool(SESSION_ID);
const result = await canUseTool(
"ExitPlanMode",
{ plan: "do stuff" },
{ signal: new AbortController().signal, suggestions: undefined, toolUseID: "toolu_2" },
);
expect(capturedPermissionRequest).not.toBeNull();
const optionIds = capturedPermissionRequest.options.map((o: any) => o.optionId);
expect(optionIds).not.toContain("auto");
expect(result.behavior).toBe("deny");
expect(sessionUpdates).toHaveLength(0);
});
it("includes the `auto` option on a model with supportsAutoMode", async () => {
const session = (agent as unknown as { sessions: Record<string, any> }).sessions[SESSION_ID];
session.modes = {
currentModeId: "plan",
availableModes: [
{ id: "auto", name: "Auto", description: "Use a model classifier" },
{ id: "default", name: "Default", description: "Standard" },
{ id: "acceptEdits", name: "Accept Edits", description: "Auto-accept edits" },
{ id: "plan", name: "Plan Mode", description: "Planning mode" },
{ id: "dontAsk", name: "Don't Ask", description: "Deny if not pre-approved" },
],
};
const canUseTool = (agent as any).canUseTool(SESSION_ID);
const signal = new AbortController().signal;
try {
await canUseTool(
"ExitPlanMode",
{ plan: "do stuff" },
{ signal, suggestions: undefined, toolUseID: "toolu_3" },
);
} catch {
// mock returns cancelled
}
expect(capturedPermissionRequest).not.toBeNull();
const optionIds = capturedPermissionRequest.options.map((o: any) => o.optionId);
expect(optionIds).toContain("auto");
});
});
});

View File

@@ -1056,6 +1056,162 @@ describe("Bash terminal output", () => {
});
});
describe("post-tool-use hook sends diff content for Write tool", () => {
// Regression: previously the dispatch only invoked
// toolUpdateFromDiffToolResponse for `Edit`, so a Write that
// overwrote an existing file (FileWriteOutput.type === "update")
// showed a "creation" diff (oldText: null, full new content) at
// tool_use time and was never corrected after the tool ran.
it("should emit a real diff for Write of an existing file (type: update)", async () => {
const toolUseCache: ToolUseCache = {};
const hookUpdates: any[] = [];
const mockClientWithUpdate = {
sessionUpdate: async (notification: any) => {
hookUpdates.push(notification);
},
} as unknown as AgentSideConnection;
toAcpNotifications(
[
{
type: "tool_use" as const,
id: "toolu_write_update",
name: "Write",
input: {
file_path: "/Users/test/project/file.ts",
content: "line1\nNEW line2\nline3",
},
},
],
"assistant",
"test-session",
toolUseCache,
mockClientWithUpdate,
mockLogger,
);
const hook = createPostToolUseHook(mockLogger);
await hook(
{
hook_event_name: "PostToolUse",
tool_name: "Write",
tool_input: {
file_path: "/Users/test/project/file.ts",
content: "line1\nNEW line2\nline3",
},
tool_response: {
type: "update",
filePath: "/Users/test/project/file.ts",
content: "line1\nNEW line2\nline3",
originalFile: "line1\nold line2\nline3",
structuredPatch: [
{
oldStart: 1,
oldLines: 3,
newStart: 1,
newLines: 3,
lines: [" line1", "-old line2", "+NEW line2", " line3"],
},
],
userModified: false,
},
tool_use_id: "toolu_write_update",
session_id: "test-session",
transcript_path: "/tmp/test",
cwd: "/tmp",
},
"toolu_write_update",
{ signal: AbortSignal.abort() },
);
expect(hookUpdates).toHaveLength(1);
const hookUpdate = hookUpdates[0].update;
expect(hookUpdate._meta.claudeCode.toolName).toBe("Write");
expect(hookUpdate.content).toEqual([
{
type: "diff",
path: "/Users/test/project/file.ts",
oldText: "line1\nold line2\nline3",
newText: "line1\nNEW line2\nline3",
},
]);
expect(hookUpdate.locations).toEqual([{ path: "/Users/test/project/file.ts", line: 1 }]);
});
it("should still emit a sensible diff for Write of a brand-new file (type: create)", async () => {
const toolUseCache: ToolUseCache = {};
const hookUpdates: any[] = [];
const mockClientWithUpdate = {
sessionUpdate: async (notification: any) => {
hookUpdates.push(notification);
},
} as unknown as AgentSideConnection;
toAcpNotifications(
[
{
type: "tool_use" as const,
id: "toolu_write_create",
name: "Write",
input: {
file_path: "/Users/test/project/new.ts",
content: "first\nsecond",
},
},
],
"assistant",
"test-session",
toolUseCache,
mockClientWithUpdate,
mockLogger,
);
const hook = createPostToolUseHook(mockLogger);
await hook(
{
hook_event_name: "PostToolUse",
tool_name: "Write",
tool_input: {
file_path: "/Users/test/project/new.ts",
content: "first\nsecond",
},
tool_response: {
type: "create",
filePath: "/Users/test/project/new.ts",
content: "first\nsecond",
originalFile: null,
structuredPatch: [
{
oldStart: 0,
oldLines: 0,
newStart: 1,
newLines: 2,
lines: ["+first", "+second"],
},
],
},
tool_use_id: "toolu_write_create",
session_id: "test-session",
transcript_path: "/tmp/test",
cwd: "/tmp",
},
"toolu_write_create",
{ signal: AbortSignal.abort() },
);
expect(hookUpdates).toHaveLength(1);
const hookUpdate = hookUpdates[0].update;
expect(hookUpdate.content).toEqual([
{
type: "diff",
path: "/Users/test/project/new.ts",
oldText: null,
newText: "first\nsecond",
},
]);
});
});
describe("post-tool-use hook preserves terminal _meta", () => {
it("should send terminal_output and terminal_exit as separate notifications, and hook should only have claudeCode", async () => {
const clientCapabilities: ClientCapabilities = {

View File

@@ -679,7 +679,7 @@ export function markdownEscape(text: string): string {
return escape + "\n" + text + (text.endsWith("\n") ? "" : "\n") + escape;
}
interface EditToolResponseHunk {
interface DiffToolResponseHunk {
oldStart: number;
oldLines: number;
newStart: number;
@@ -687,23 +687,24 @@ interface EditToolResponseHunk {
lines: string[];
}
interface EditToolResponse {
interface DiffToolResponse {
filePath?: string;
structuredPatch?: EditToolResponseHunk[];
structuredPatch?: DiffToolResponseHunk[];
}
/**
* Builds diff ToolUpdate content from the structured Edit toolResponse provided
* by the PostToolUse hook. Unlike parsing the plain unified diff string, this uses
* the pre-parsed structuredPatch which supports multiple replacement sites (replaceAll)
* and always includes context lines for better readability.
* Builds diff ToolUpdate content from the structured toolResponse provided by
* the PostToolUse hook for diff-producing tools (Edit, Write). Unlike parsing
* the plain unified diff string, this uses the pre-parsed structuredPatch
* which supports multiple replacement sites (replaceAll) and always includes
* context lines for better readability.
*/
export function toolUpdateFromEditToolResponse(toolResponse: unknown): {
export function toolUpdateFromDiffToolResponse(toolResponse: unknown): {
content?: ToolCallContent[];
locations?: ToolCallLocation[];
} {
if (!toolResponse || typeof toolResponse !== "object") return {};
const response = toolResponse as EditToolResponse;
const response = toolResponse as DiffToolResponse;
if (!response.filePath || !Array.isArray(response.structuredPatch)) return {};
const content: ToolCallContent[] = [];