Compare commits

...

7 Commits

Author SHA1 Message Date
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
9 changed files with 970 additions and 240 deletions

View File

@@ -1,3 +1,3 @@
{
".": "0.31.1"
".": "0.31.4"
}

View File

@@ -1,5 +1,26 @@
# Changelog
## [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)

262
package-lock.json generated
View File

@@ -1,16 +1,16 @@
{
"name": "@agentclientprotocol/claude-agent-acp",
"version": "0.31.1",
"version": "0.31.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@agentclientprotocol/claude-agent-acp",
"version": "0.31.1",
"version": "0.31.4",
"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.121",
"zod": "^3.25.0 || ^4.0.0"
},
"bin": {
@@ -20,8 +20,8 @@
"@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",
"@typescript-eslint/eslint-plugin": "8.59.1",
"@typescript-eslint/parser": "8.59.1",
"eslint": "10.2.1",
"eslint-config-prettier": "10.1.8",
"globals": "17.5.0",
@@ -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.121",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.2.121.tgz",
"integrity": "sha512-hwZNYTkGLKVixd/V/OCJwfH/SdfxZXGV0m6wvy5EBq6qfB+lvJTRz/MSOSa7dHqo4/F7zJY68crEEca68Wrxpw==",
"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.121",
"@anthropic-ai/claude-agent-sdk-darwin-x64": "0.2.121",
"@anthropic-ai/claude-agent-sdk-linux-arm64": "0.2.121",
"@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.2.121",
"@anthropic-ai/claude-agent-sdk-linux-x64": "0.2.121",
"@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.2.121",
"@anthropic-ai/claude-agent-sdk-win32-arm64": "0.2.121",
"@anthropic-ai/claude-agent-sdk-win32-x64": "0.2.121"
},
"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.121",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.2.121.tgz",
"integrity": "sha512-zVHcXvx6Hl/glDcOCH+EyNx4KPE9cMGLk42eEBSZe014tAN5W8bwM/By08iM6dxijnpH0NQRNNEAW+BryWzuDg==",
"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.121",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.2.121.tgz",
"integrity": "sha512-lIXdqKj+bpfDxCk/eU1F1TXNqsIsLTRrkUG/wx19WIGZ8gLUmmVSveUKGlNegTs7S6evMvuezprJzDJT4TcvPA==",
"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.121",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.2.121.tgz",
"integrity": "sha512-AQSnJzaiFvQpUPfO1tWLvsHgb6KNar4QYEQ/5/sk1itfgr3Fx9gxTreq43wX7AXSvkBX1QlDaP1aR1sfM/g/lQ==",
"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.121",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.2.121.tgz",
"integrity": "sha512-4XaGK+dRBYy7krln7BrDG0WsdE6ejUSgHjWHlUGXoubFfZUvls4GSahLcYjJBArLi4dLnxKw8zEuiQguPAIbrw==",
"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.121",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.2.121.tgz",
"integrity": "sha512-DJUgpm7au086WaQV/S7BGOt2M8D90spGZRizT3twYsacf1BxzK1qsXqB/Pw1lUjPy6pI107pml/TaPzWuS/Vzg==",
"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.121",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.2.121.tgz",
"integrity": "sha512-sQoGIgzLlBRrwizxsCV/lbaEuxXom/cfOwlDtQ2HnS1IzDDSjSf5d5pugpWItkOyXBWcHzMUu731WTTutvd/BQ==",
"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.121",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.2.121.tgz",
"integrity": "sha512-6n/NHkHxs0/lCJX3XPADjo1EFzXBf0IwYz/nyzJGBCDJjGKmgTe0i8eYBr/hviwt1/OPeK7dmVzVSVl6EL9Azg==",
"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.121",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.2.121.tgz",
"integrity": "sha512-v2/R918/t94cCwc6rmbxk+UYeQPtF2oBLtQAk+cT0M60hvqmCZO2noyZx5uTp8TQncOlG4MkINIeNY2yfmWSoQ==",
"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"
},
@@ -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"
@@ -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": [
@@ -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.12",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.12.tgz",
"integrity": "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==",
"dev": true,
"funding": [
{

View File

@@ -3,7 +3,7 @@
"publishConfig": {
"access": "public"
},
"version": "0.31.1",
"version": "0.31.4",
"description": "An ACP-compatible coding agent powered by the Claude Agent SDK (TypeScript)",
"main": "dist/lib.js",
"types": "dist/lib.d.ts",
@@ -60,16 +60,16 @@
"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.121",
"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",
"@typescript-eslint/eslint-plugin": "8.59.1",
"@typescript-eslint/parser": "8.59.1",
"eslint": "10.2.1",
"eslint-config-prettier": "10.1.8",
"globals": "17.5.0",

View File

@@ -385,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;
@@ -1302,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 };
}
@@ -1319,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) {
@@ -1390,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",
@@ -1401,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,
@@ -1425,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 {
@@ -1543,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,
@@ -1555,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
@@ -1571,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 =
@@ -1595,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,
@@ -1720,6 +1794,7 @@ export class ClaudeAcpAgent implements Agent {
const permissionMode = resolvePermissionMode(
settingsManager.getSettings().permissions?.defaultMode,
this.logger,
);
// Extract options from _meta if provided
@@ -1858,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,
};
@@ -2040,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,
@@ -2106,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,
@@ -2189,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();
@@ -2207,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;
}

View File

@@ -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-5");
expect(response.models.currentModelId).toBe("claude-sonnet-4-5");
});
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

@@ -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

@@ -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

@@ -811,4 +811,391 @@ describe("session config options", () => {
);
});
});
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-5",
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-5", 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");
});
});
});