Compare commits

...

11 Commits

Author SHA1 Message Date
acp-release-bot[bot]
5de5d4a2c8 chore(main): release 0.68.0 (#1000)
🤖 I have created a release *beep* *boop*
---


##
[0.68.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.67.0...v0.68.0)
(2026-08-14)


### Features

* align typed session failures with AIR protocol
([#992](https://github.com/agentclientprotocol/claude-agent-acp/issues/992))
([0581b9c](0581b9cf39))

---
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-08-14 17:32:04 +04:00
nikita-ashihmin
0581b9cf39 feat: align typed session failures with AIR protocol (#992)
## Summary

- align `_meta.jetbrains.air.sessionFailure` with AIR's
transcript-record protocol: `id`, `revision`, broad `category`, explicit
`severity`, `title`, optional `details`, and `actions`
- forward Claude's actual user-facing assistant/result text as `title`;
use adapter-authored fallback text only when Claude provides no message
(transport failure, worker EOF, or an internal invariant)
- reduce categories to `connection`, `access`, `limit`, `request`,
`service`, and `unknown`
- limit suggested actions to `retry`, `login`, and `new_session`
- update one transcript record through `id + revision`, keep resolved
entries in history, and remove clear/tombstone updates
- suppress duplicate assistant failure prose only for clients that
negotiated typed failures; preserve legacy ACP behavior otherwise
- restore synthetic usage-limit failures from session history with their
exact stored Claude text
- document negotiation, delivery carriers, identity/revision behavior,
mappings, recovery, and compatibility in the extension spec

## Validation

- `npm run check`
- `npm run build`
- `npm run test:run` — 777 passed, 26 skipped
- `git diff --check`

## Tracking

[IJAI-993](https://youtrack.jetbrains.com/issue/IJAI-993)
2026-08-14 17:29:39 +04:00
Sergey Shkredov
11dd73b871 Restore native provider state after overrides (#996)
### Summary
- expose one `main` provider slot because Anthropic, Bedrock, and Vertex
routing are mutually exclusive
- advertise all three supported API types on that slot and replace the
active configuration on each `providers/set`
- restore native Claude routing when the slot is disabled
- keep provider headers out of list responses and preserve per-protocol
validation

### Testing
- `npm run test:run` (767 passed, 20 skipped)
- `npm run build`
- `npm run check`

Co-authored-by: Junie <junie@jetbrains.com>
2026-08-14 10:13:38 +00:00
acp-release-bot[bot]
e4dba808ea chore(main): release 0.67.0 (#975)
🤖 I have created a release *beep* *boop*
---


##
[0.67.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.66.0...v0.67.0)
(2026-08-14)


### Features

* **deps:** Update to @anthropic-ai/claude-agent-sdk v0.3.232
([#993](https://github.com/agentclientprotocol/claude-agent-acp/issues/993))
([de0d0e2](de0d0e2b7d))
* expose typed session failures for AIR
([#979](https://github.com/agentclientprotocol/claude-agent-acp/issues/979))
([8157ee1](8157ee113e))
* publish the model fallback as a warning advisory
([#990](https://github.com/agentclientprotocol/claude-agent-acp/issues/990))
([35aaddb](35aaddb3d5))
* surface resolved model name in default model option description
([#982](https://github.com/agentclientprotocol/claude-agent-acp/issues/982))
([ec73cd8](ec73cd8560))
* surface Skill tool calls with name and kind in _meta
([#986](https://github.com/agentclientprotocol/claude-agent-acp/issues/986))
([1f09e9a](1f09e9a3ca))


### Bug Fixes

* preserve task plans across prompts
([#974](https://github.com/agentclientprotocol/claude-agent-acp/issues/974))
([1afa940](1afa940a2c))
* show a pending title while Claude prepares a file
([#978](https://github.com/agentclientprotocol/claude-agent-acp/issues/978))
([3df1ede](3df1ede89f))

---
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-08-14 00:40:45 +00:00
dependabot[bot]
de0d0e2b7d feat(deps): Update to @anthropic-ai/claude-agent-sdk v0.3.232 (#993)
Bumps the minor group with 12 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
|
[@anthropic-ai/claude-agent-sdk](https://github.com/anthropics/claude-agent-sdk-typescript)
| `0.3.220` | `0.3.226` |
|
[@anthropic-ai/sdk](https://github.com/anthropics/anthropic-sdk-typescript)
| `0.115.0` | `0.116.0` |
|
[@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node)
| `26.1.2` | `26.2.0` |
|
[@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin)
| `8.65.0` | `8.66.0` |
|
[@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser)
| `8.65.0` | `8.66.0` |
| [eslint](https://github.com/eslint/eslint) | `10.8.0` | `10.8.1` |
|
[express-rate-limit](https://github.com/express-rate-limit/express-rate-limit)
| `8.6.1` | `8.6.2` |
| [hono](https://github.com/honojs/hono) | `4.13.0` | `4.13.2` |
| [ip-address](https://github.com/beaugunderson/ip-address) | `10.4.0` |
`10.5.0` |
| [postcss](https://github.com/postcss/postcss) | `8.5.25` | `8.5.26` |
|
[rolldown](https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown)
| `1.2.2` | `1.2.4` |
| [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) |
`8.2.0` | `8.2.1` |


Updates `@anthropic-ai/claude-agent-sdk` from 0.3.220 to 0.3.226
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/anthropics/claude-agent-sdk-typescript/releases">@​anthropic-ai/claude-agent-sdk's
releases</a>.</em></p>
<blockquote>
<h2>v0.3.226</h2>
<h2>What's changed</h2>
<ul>
<li>Updated to parity with Claude Code v2.1.226</li>
</ul>
<h2>Update</h2>
<pre lang="sh"><code>npm install @anthropic-ai/claude-agent-sdk@0.3.226
# or
yarn add @anthropic-ai/claude-agent-sdk@0.3.226
# or
pnpm add @anthropic-ai/claude-agent-sdk@0.3.226
# or
bun add @anthropic-ai/claude-agent-sdk@0.3.226
</code></pre>
<h2>v0.3.225</h2>
<h2>What's changed</h2>
<ul>
<li>Fixed background subagents in headless/SDK sessions never resuming
when a background shell command or Monitor they left running completed,
so the subagent never saw the result</li>
</ul>
<h2>Update</h2>
<pre lang="sh"><code>npm install @anthropic-ai/claude-agent-sdk@0.3.225
# or
yarn add @anthropic-ai/claude-agent-sdk@0.3.225
# or
pnpm add @anthropic-ai/claude-agent-sdk@0.3.225
# or
bun add @anthropic-ai/claude-agent-sdk@0.3.225
</code></pre>
<h2>v0.3.224</h2>
<h2>What's changed</h2>
<ul>
<li>Added <code>crossSessionInbound</code> and <code>dialogExpiry</code>
settings: cross-session messages sent to a session running with bypassed
permissions are held for your approval, and messages to other sessions
auto-deliver</li>
<li>Added <code>subkind: 'peer-send-message'</code> to the
<code>task-notification</code> member of <code>SDKMessageOrigin</code>,
marking a notification raised by a cross-session
<code>SendMessage</code></li>
<li>Added <code>source: 'archive'</code> plugin config variant to
<code>Settings</code>, with <code>url</code> and optional
<code>sha256</code>, for installing plugins from a zip over HTTPS</li>
<li>Added sandbox credential-masking fields to <code>Settings</code>:
<code>decode: 'jwt'</code> with <code>maskClaims</code>,
<code>extract</code>/<code>onExtractNoMatch</code> on
<code>envVars</code>, and <code>awsPairs</code>/<code>sigv4</code> for
AWS SigV4 re-signing</li>
<li>Fixed long (&gt;200 char) project paths resolving to another
project's session directory under a shared sanitized prefix; session
list/get/rename/tag/fork/delete and <code>/resume</code> no longer cross
projects</li>
</ul>
<h2>Update</h2>
<pre lang="sh"><code>npm install @anthropic-ai/claude-agent-sdk@0.3.224
# or
yarn add @anthropic-ai/claude-agent-sdk@0.3.224
# or
&lt;/tr&gt;&lt;/table&gt; 
</code></pre>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/anthropics/claude-agent-sdk-typescript/blob/main/CHANGELOG.md">@​anthropic-ai/claude-agent-sdk's
changelog</a>.</em></p>
<blockquote>
<h2>0.3.226</h2>
<ul>
<li>Updated to parity with Claude Code v2.1.226</li>
</ul>
<h2>0.3.225</h2>
<ul>
<li>Fixed background subagents in headless/SDK sessions never resuming
when a background shell command or Monitor they left running completed,
so the subagent never saw the result</li>
</ul>
<h2>0.3.224</h2>
<ul>
<li>Added <code>crossSessionInbound</code> and <code>dialogExpiry</code>
settings: cross-session messages sent to a session running with bypassed
permissions are held for your approval, and messages to other sessions
auto-deliver</li>
<li>Added <code>subkind: 'peer-send-message'</code> to the
<code>task-notification</code> member of <code>SDKMessageOrigin</code>,
marking a notification raised by a cross-session
<code>SendMessage</code></li>
<li>Added <code>source: 'archive'</code> plugin config variant to
<code>Settings</code>, with <code>url</code> and optional
<code>sha256</code>, for installing plugins from a zip over HTTPS</li>
<li>Added sandbox credential-masking fields to <code>Settings</code>:
<code>decode: 'jwt'</code> with <code>maskClaims</code>,
<code>extract</code>/<code>onExtractNoMatch</code> on
<code>envVars</code>, and <code>awsPairs</code>/<code>sigv4</code> for
AWS SigV4 re-signing</li>
<li>Fixed long (&gt;200 char) project paths resolving to another
project's session directory under a shared sanitized prefix; session
list/get/rename/tag/fork/delete and <code>/resume</code> no longer cross
projects</li>
</ul>
<h2>0.3.223</h2>
<ul>
<li>Added <code>resumeDropsTurn</code> option: with
<code>resumeSessionAt</code>, declares the turn a truncating resume
intends to drop; the CLI refuses the resume if anything else would be
discarded</li>
<li>Result messages for repeated 529 overload failures now include
<code>api_error_status: 529</code>, so SDK consumers can detect overload
terminations structurally instead of matching message text</li>
<li>Bare headless (<code>-p</code> / SDK <code>query()</code> without
<code>canUseTool</code>) now emits <code>system/permission_denied</code>
stream events when a tool call is auto-denied</li>
<li>Documented <code>usage</code> vs <code>modelUsage</code> on
stream-json results: <code>usage</code> is main-loop-only and per-turn;
<code>modelUsage</code> is cumulative, covers all query-pipeline calls,
and is the field for cost accounting</li>
</ul>
<h2>0.3.222</h2>
<ul>
<li>Fixed <code>query({ sessionStore, resume })</code> not carrying user
<code>settings.json</code> (<code>apiKeyHelper</code>, <code>env</code>,
<code>hooks</code>, <code>permissions</code>) into the resumed
subprocess</li>
</ul>
<h2>0.3.221</h2>
<ul>
<li>Improved <code>skills</code> option validation: malformed names
(delimiters or control characters) and wildcard-form names are rejected
with a clear error; use <code>skills: 'all'</code> to enable every
skill</li>
<li>Fixed external MCP servers passed via the <code>mcpServers</code>
option not being connected before the first turn, which caused the model
to emit tool calls as literal text</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="d13c50c54d"><code>d13c50c</code></a>
chore: Update CHANGELOG.md</li>
<li><a
href="4cae4b7634"><code>4cae4b7</code></a>
chore: Update CHANGELOG.md</li>
<li><a
href="86e3e9464e"><code>86e3e94</code></a>
chore: Update CHANGELOG.md</li>
<li><a
href="064793e6da"><code>064793e</code></a>
chore: Update CHANGELOG.md</li>
<li><a
href="6e143f440c"><code>6e143f4</code></a>
chore: Update CHANGELOG.md</li>
<li><a
href="31fbbfd403"><code>31fbbfd</code></a>
chore: Update CHANGELOG.md</li>
<li>See full diff in <a
href="https://github.com/anthropics/claude-agent-sdk-typescript/compare/v0.3.220...v0.3.226">compare
view</a></li>
</ul>
</details>
<br />

Updates `@anthropic-ai/sdk` from 0.115.0 to 0.116.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/anthropics/anthropic-sdk-typescript/releases">@​anthropic-ai/sdk's
releases</a>.</em></p>
<blockquote>
<h2>sdk: v0.116.0</h2>
<h2>0.116.0 (2026-08-07)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.115.0...sdk-v0.116.0">sdk-v0.115.0...sdk-v0.116.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> add
<code>mid-conversation-tool-changes-2026-07-01</code> beta (<a
href="70e04f689b">70e04f6</a>)</li>
<li><strong>api:</strong> add support for session budgets, advisor tool,
pinned inference location and skills auto-loading from GitHub (<a
href="1336a4a903">1336a4a</a>)</li>
<li><strong>tools:</strong> make bash timeout and abort errors matchable
by class (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/259">#259</a>)
(<a
href="201b12e8ac">201b12e</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>client:</strong> use hardcoded User-Agent strings instead of
constructor names (<a
href="0b97f04042">0b97f04</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><strong>api:</strong> remove retired Claude Opus 4.1 models (<a
href="11b685c66f">11b685c</a>)</li>
<li><strong>docs:</strong> small updates to descriptions (<a
href="413cc5f120">413cc5f</a>)</li>
<li><strong>docs:</strong> updates to a few documentation strings (<a
href="c38a1de5fc">c38a1de</a>)</li>
<li><strong>internal/deps:</strong> bump <code>@​babel/core</code> and
<code>@​babel/helpers</code> to 7.29.7 (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/261">#261</a>)
(<a
href="3164934680">3164934</a>)</li>
<li><strong>internal/deps:</strong> install tsc-multi from the npm
registry (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/265">#265</a>)
(<a
href="c787f9022c">c787f90</a>)</li>
<li><strong>internal:</strong> codegen related update (<a
href="73b3d61009">73b3d61</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/anthropics/anthropic-sdk-typescript/blob/main/CHANGELOG.md">@​anthropic-ai/sdk's
changelog</a>.</em></p>
<blockquote>
<h2>0.116.0 (2026-08-07)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.115.0...sdk-v0.116.0">sdk-v0.115.0...sdk-v0.116.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> add
<code>mid-conversation-tool-changes-2026-07-01</code> beta (<a
href="70e04f689b">70e04f6</a>)</li>
<li><strong>api:</strong> add support for session budgets, advisor tool,
pinned inference location and skills auto-loading from GitHub (<a
href="1336a4a903">1336a4a</a>)</li>
<li><strong>tools:</strong> make bash timeout and abort errors matchable
by class (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/259">#259</a>)
(<a
href="201b12e8ac">201b12e</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>client:</strong> use hardcoded User-Agent strings instead of
constructor names (<a
href="0b97f04042">0b97f04</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><strong>api:</strong> remove retired Claude Opus 4.1 models (<a
href="11b685c66f">11b685c</a>)</li>
<li><strong>docs:</strong> small updates to descriptions (<a
href="413cc5f120">413cc5f</a>)</li>
<li><strong>docs:</strong> updates to a few documentation strings (<a
href="c38a1de5fc">c38a1de</a>)</li>
<li><strong>internal/deps:</strong> bump <code>@​babel/core</code> and
<code>@​babel/helpers</code> to 7.29.7 (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/261">#261</a>)
(<a
href="3164934680">3164934</a>)</li>
<li><strong>internal/deps:</strong> install tsc-multi from the npm
registry (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/265">#265</a>)
(<a
href="c787f9022c">c787f90</a>)</li>
<li><strong>internal:</strong> codegen related update (<a
href="73b3d61009">73b3d61</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="ed02a89f5b"><code>ed02a89</code></a>
chore: release main</li>
<li><a
href="d72ff6a1c8"><code>d72ff6a</code></a>
feat(api): add support for session budgets, advisor tool, pinned
inference lo...</li>
<li><a
href="dbfed51cc3"><code>dbfed51</code></a>
chore(internal/deps): refresh provider package dev tooling to
mirror-availabl...</li>
<li><a
href="b7e95bfe0f"><code>b7e95bf</code></a>
chore(docs): updates to a few documentation strings</li>
<li><a
href="9efb7bd454"><code>9efb7bd</code></a>
chore(api): remove retired Claude Opus 4.1 models</li>
<li><a
href="342d422c7d"><code>342d422</code></a>
chore(internal/deps): install tsc-multi from the npm registry (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/265">#265</a>)</li>
<li><a
href="501955ca31"><code>501955c</code></a>
chore(internal/deps): bump <code>@​babel/core</code> and
<code>@​babel/helpers</code> to 7.29.7 (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/261">#261</a>)</li>
<li><a
href="6641c5e46f"><code>6641c5e</code></a>
feat(tools): make bash timeout and abort errors matchable by class (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/259">#259</a>)</li>
<li><a
href="e421c43ea6"><code>e421c43</code></a>
chore(docs): small updates to descriptions</li>
<li><a
href="c0a963eff5"><code>c0a963e</code></a>
feat(api): add <code>mid-conversation-tool-changes-2026-07-01</code>
beta</li>
<li>Additional commits viewable in <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.115.0...sdk-v0.116.0">compare
view</a></li>
</ul>
</details>
<br />

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

Updates `@typescript-eslint/eslint-plugin` from 8.65.0 to 8.66.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/eslint-plugin's
releases</a>.</em></p>
<blockquote>
<h2>v8.66.0</h2>
<h2>8.66.0 (2026-08-03)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>typescript-estree:</strong> handle import.defer() as
ImportExpression (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12609">#12609</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-conversion]
ignore shadowed built-ins (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12590">#12590</a>)</li>
<li><strong>eslint-plugin:</strong> [prefer-nullish-coalescing] handle
shadowed Boolean calls (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12591">#12591</a>)</li>
<li><strong>eslint-plugin:</strong> [no-useless-default-assignment]
don't report defaults used by other overloads (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12607">#12607</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-parameters]
check MappedType key remapping (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12588">#12588</a>)</li>
<li><strong>eslint-plugin:</strong> [class-literal-property-style]
preserve type annotations and don't drop decorators (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12617">#12617</a>)</li>
<li><strong>website:</strong> list
<code>onUnsupportedTypeScriptVersion</code> in parser options (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12585">#12585</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>Steffen Schroeder</li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.66.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md">@​typescript-eslint/eslint-plugin's
changelog</a>.</em></p>
<blockquote>
<h2>8.66.0 (2026-08-03)</h2>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [class-literal-property-style]
preserve type annotations and don't drop decorators (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12617">#12617</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-parameters]
check MappedType key remapping (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12588">#12588</a>)</li>
<li><strong>eslint-plugin:</strong> [no-useless-default-assignment]
don't report defaults used by other overloads (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12607">#12607</a>)</li>
<li><strong>eslint-plugin:</strong> [prefer-nullish-coalescing] handle
shadowed Boolean calls (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12591">#12591</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-conversion]
ignore shadowed built-ins (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12590">#12590</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.66.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="e51b11ba3a"><code>e51b11b</code></a>
chore(release): publish 8.66.0</li>
<li><a
href="20cb15e6db"><code>20cb15e</code></a>
chore(eslint-plugin): split prefer-optional-chain.test.ts in multiple
files (...</li>
<li><a
href="780a8c52d5"><code>780a8c5</code></a>
docs(eslint-plugin): [member-ordering] add lacking commas in default
member t...</li>
<li><a
href="08dd427202"><code>08dd427</code></a>
fix(eslint-plugin): [class-literal-property-style] preserve type
annotations ...</li>
<li><a
href="6be25d3b45"><code>6be25d3</code></a>
fix(eslint-plugin): [no-unnecessary-type-parameters] check MappedType
key rem...</li>
<li><a
href="8826d0ebf7"><code>8826d0e</code></a>
fix(eslint-plugin): [no-useless-default-assignment] don't report
defaults use...</li>
<li><a
href="7aecc1c44e"><code>7aecc1c</code></a>
fix(eslint-plugin): [prefer-nullish-coalescing] handle shadowed Boolean
calls...</li>
<li><a
href="5ffa76049a"><code>5ffa760</code></a>
fix(eslint-plugin): [no-unnecessary-type-conversion] ignore shadowed
built-in...</li>
<li><a
href="f282f84df6"><code>f282f84</code></a>
chore(eslint-plugin): fix all
<code>eslint-plugin/require-test-error-positions</code> (#...</li>
<li><a
href="c8cafa4ae9"><code>c8cafa4</code></a>
docs: [unified-signatures] caveats regarding signatures merging (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin/issues/12581">#12581</a>)</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.66.0/packages/eslint-plugin">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/parser` from 8.65.0 to 8.66.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/parser's
releases</a>.</em></p>
<blockquote>
<h2>v8.66.0</h2>
<h2>8.66.0 (2026-08-03)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>typescript-estree:</strong> handle import.defer() as
ImportExpression (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12609">#12609</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-conversion]
ignore shadowed built-ins (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12590">#12590</a>)</li>
<li><strong>eslint-plugin:</strong> [prefer-nullish-coalescing] handle
shadowed Boolean calls (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12591">#12591</a>)</li>
<li><strong>eslint-plugin:</strong> [no-useless-default-assignment]
don't report defaults used by other overloads (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12607">#12607</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-parameters]
check MappedType key remapping (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12588">#12588</a>)</li>
<li><strong>eslint-plugin:</strong> [class-literal-property-style]
preserve type annotations and don't drop decorators (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12617">#12617</a>)</li>
<li><strong>website:</strong> list
<code>onUnsupportedTypeScriptVersion</code> in parser options (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12585">#12585</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>Steffen Schroeder</li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.66.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md">@​typescript-eslint/parser's
changelog</a>.</em></p>
<blockquote>
<h2>8.66.0 (2026-08-03)</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.66.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="e51b11ba3a"><code>e51b11b</code></a>
chore(release): publish 8.66.0</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.66.0/packages/parser">compare
view</a></li>
</ul>
</details>
<br />

Updates `eslint` from 10.8.0 to 10.8.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/eslint/eslint/releases">eslint's
releases</a>.</em></p>
<blockquote>
<h2>v10.8.1</h2>
<h2>Bug Fixes</h2>
<ul>
<li><a
href="18eb0a7e78"><code>18eb0a7</code></a>
fix: prevent ASI hazard in <code>no-unused-labels</code> autofix (<a
href="https://redirect.github.com/eslint/eslint/issues/21173">#21173</a>)
(dongkyu lee)</li>
<li><a
href="151ba3f583"><code>151ba3f</code></a>
fix: false positives in <code>getter-return</code> and
<code>accessor-pairs</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21163">#21163</a>)
(Grit)</li>
<li><a
href="6898df9364"><code>6898df9</code></a>
fix: ignore meta-property names in <code>id-denylist</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21166">#21166</a>)
(Pixel)</li>
<li><a
href="4d7db6628e"><code>4d7db66</code></a>
fix: ignore meta-property names in <code>id-match</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21167">#21167</a>)
(Pixel)</li>
<li><a
href="677214e7ee"><code>677214e</code></a>
fix: handle ASI hazards in no-unused-vars removeVar suggestion (<a
href="https://redirect.github.com/eslint/eslint/issues/20935">#20935</a>)
(kuldeep kumar)</li>
</ul>
<h2>Documentation</h2>
<ul>
<li><a
href="7d0cbf81cf"><code>7d0cbf8</code></a>
docs: Update README (GitHub Actions Bot)</li>
<li><a
href="0a05812adb"><code>0a05812</code></a>
docs: add missing backticks to <code>no-duplicate-imports.js</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21183">#21183</a>)
(Lee Daeun)</li>
<li><a
href="678c90b55d"><code>678c90b</code></a>
docs: Update README (GitHub Actions Bot)</li>
<li><a
href="8a104242e8"><code>8a10424</code></a>
docs: Update README (GitHub Actions Bot)</li>
<li><a
href="69bb948061"><code>69bb948</code></a>
docs: Update README (GitHub Actions Bot)</li>
</ul>
<h2>Chores</h2>
<ul>
<li><a
href="0a14800505"><code>0a14800</code></a>
chore: update github/codeql-action action to v4.37.4 (<a
href="https://redirect.github.com/eslint/eslint/issues/21196">#21196</a>)
(renovate[bot])</li>
<li><a
href="05adcb1354"><code>05adcb1</code></a>
test: fix failing ecosystem test for <code>eslint-plugin-unicorn</code>
(<a
href="https://redirect.github.com/eslint/eslint/issues/21191">#21191</a>)
(Lazizbek Ergashev)</li>
<li><a
href="5611035665"><code>5611035</code></a>
test: add error locations info to <code>no-void</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21185">#21185</a>)
(Lee Daeun)</li>
<li><a
href="ee47333aa6"><code>ee47333</code></a>
ci: bump github/codeql-action from 4 to 4.37.3 (<a
href="https://redirect.github.com/eslint/eslint/issues/21176">#21176</a>)
(dependabot[bot])</li>
<li><a
href="f131c034ad"><code>f131c03</code></a>
chore: improve ecosystem test failure reporting (<a
href="https://redirect.github.com/eslint/eslint/issues/20937">#20937</a>)
(crimsonjay0)</li>
<li><a
href="1f6eddee60"><code>1f6edde</code></a>
chore: update ecosystem plugins (<a
href="https://redirect.github.com/eslint/eslint/issues/21182">#21182</a>)
(ESLint Bot)</li>
<li><a
href="d3266fb26c"><code>d3266fb</code></a>
chore: unpin <code>webpack</code> dependency (<a
href="https://redirect.github.com/eslint/eslint/issues/21172">#21172</a>)
(Francesco Trotta)</li>
<li><a
href="65a6519cc8"><code>65a6519</code></a>
chore: add allowScripts field to package.json (<a
href="https://redirect.github.com/eslint/eslint/issues/21092">#21092</a>)
(GiHoon Noh)</li>
<li><a
href="22e5256853"><code>22e5256</code></a>
ci: add <code>triage:no</code> label to Dependabot PRs (<a
href="https://redirect.github.com/eslint/eslint/issues/21141">#21141</a>)
(lumir)</li>
<li><a
href="55c9038836"><code>55c9038</code></a>
ci: bump actions/labeler from 6 to 7 (<a
href="https://redirect.github.com/eslint/eslint/issues/21159">#21159</a>)
(dependabot[bot])</li>
<li><a
href="7280e78183"><code>7280e78</code></a>
chore: update dependency prettier to v3.9.6 (<a
href="https://redirect.github.com/eslint/eslint/issues/21162">#21162</a>)
(renovate[bot])</li>
<li><a
href="eddbad6015"><code>eddbad6</code></a>
test: fix failing ecosystem test for <code>eslint-plugin-unicorn</code>
(<a
href="https://redirect.github.com/eslint/eslint/issues/21156">#21156</a>)
(Francesco Trotta)</li>
<li><a
href="60a178d5ef"><code>60a178d</code></a>
chore: update ecosystem plugins (<a
href="https://redirect.github.com/eslint/eslint/issues/21150">#21150</a>)
(ESLint Bot)</li>
<li><a
href="f9f61dc611"><code>f9f61dc</code></a>
test: add error locations to <code>no-unreachable</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21151">#21151</a>)
(JIYEON)</li>
<li><a
href="d08629382b"><code>d086293</code></a>
test: add error locations to <code>no-undef</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21147">#21147</a>)
(JIYEON)</li>
<li><a
href="cc01b67061"><code>cc01b67</code></a>
test: add error locations to <code>no-useless-catch</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21144">#21144</a>)
(devoil)</li>
<li><a
href="688e75ede0"><code>688e75e</code></a>
chore: add missing backticks in JSDoc (<a
href="https://redirect.github.com/eslint/eslint/issues/21143">#21143</a>)
(Bo Hyun Kim)</li>
<li><a
href="7c1e17543a"><code>7c1e175</code></a>
test: add error locations to <code>require-await</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21145">#21145</a>)
(Grit)</li>
<li><a
href="588a26ddce"><code>588a26d</code></a>
test: add error locations to <code>no-extra-label</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21139">#21139</a>)
(dongkyu lee)</li>
<li><a
href="059aa89574"><code>059aa89</code></a>
test: add error locations to <code>no-useless-concat</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21140">#21140</a>)
(dongkyu lee)</li>
<li><a
href="5a452a8ba5"><code>5a452a8</code></a>
test: add error locations to <code>no-const-assign</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21138">#21138</a>)
(dongkyu lee)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="c049dc3c42"><code>c049dc3</code></a>
10.8.1</li>
<li><a
href="a3f7826941"><code>a3f7826</code></a>
Build: changelog update for 10.8.1</li>
<li><a
href="18eb0a7e78"><code>18eb0a7</code></a>
fix: prevent ASI hazard in <code>no-unused-labels</code> autofix (<a
href="https://redirect.github.com/eslint/eslint/issues/21173">#21173</a>)</li>
<li><a
href="0a14800505"><code>0a14800</code></a>
chore: update github/codeql-action action to v4.37.4 (<a
href="https://redirect.github.com/eslint/eslint/issues/21196">#21196</a>)</li>
<li><a
href="7d0cbf81cf"><code>7d0cbf8</code></a>
docs: Update README</li>
<li><a
href="05adcb1354"><code>05adcb1</code></a>
test: fix failing ecosystem test for <code>eslint-plugin-unicorn</code>
(<a
href="https://redirect.github.com/eslint/eslint/issues/21191">#21191</a>)</li>
<li><a
href="5611035665"><code>5611035</code></a>
test: add error locations info to <code>no-void</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21185">#21185</a>)</li>
<li><a
href="ee47333aa6"><code>ee47333</code></a>
ci: bump github/codeql-action from 4 to 4.37.3 (<a
href="https://redirect.github.com/eslint/eslint/issues/21176">#21176</a>)</li>
<li><a
href="f131c034ad"><code>f131c03</code></a>
chore: improve ecosystem test failure reporting (<a
href="https://redirect.github.com/eslint/eslint/issues/20937">#20937</a>)</li>
<li><a
href="0a05812adb"><code>0a05812</code></a>
docs: add missing backticks to <code>no-duplicate-imports.js</code> (<a
href="https://redirect.github.com/eslint/eslint/issues/21183">#21183</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/eslint/eslint/compare/v10.8.0...v10.8.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `@anthropic-ai/claude-agent-sdk-darwin-arm64` from 0.3.220 to
0.3.226

Updates `@anthropic-ai/claude-agent-sdk-darwin-x64` from 0.3.220 to
0.3.226

Updates `@anthropic-ai/claude-agent-sdk-linux-arm64` from 0.3.220 to
0.3.226

Updates `@anthropic-ai/claude-agent-sdk-linux-arm64-musl` from 0.3.220
to 0.3.226

Updates `@anthropic-ai/claude-agent-sdk-linux-x64` from 0.3.220 to
0.3.226

Updates `@anthropic-ai/claude-agent-sdk-linux-x64-musl` from 0.3.220 to
0.3.226

Updates `@anthropic-ai/claude-agent-sdk-win32-arm64` from 0.3.220 to
0.3.226

Updates `@anthropic-ai/claude-agent-sdk-win32-x64` from 0.3.220 to
0.3.226

Updates `@typescript-eslint/project-service` from 8.65.0 to 8.66.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/project-service's
releases</a>.</em></p>
<blockquote>
<h2>v8.66.0</h2>
<h2>8.66.0 (2026-08-03)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>typescript-estree:</strong> handle import.defer() as
ImportExpression (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12609">#12609</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-conversion]
ignore shadowed built-ins (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12590">#12590</a>)</li>
<li><strong>eslint-plugin:</strong> [prefer-nullish-coalescing] handle
shadowed Boolean calls (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12591">#12591</a>)</li>
<li><strong>eslint-plugin:</strong> [no-useless-default-assignment]
don't report defaults used by other overloads (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12607">#12607</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-parameters]
check MappedType key remapping (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12588">#12588</a>)</li>
<li><strong>eslint-plugin:</strong> [class-literal-property-style]
preserve type annotations and don't drop decorators (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12617">#12617</a>)</li>
<li><strong>website:</strong> list
<code>onUnsupportedTypeScriptVersion</code> in parser options (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12585">#12585</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>Steffen Schroeder</li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.66.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/project-service/CHANGELOG.md">@​typescript-eslint/project-service's
changelog</a>.</em></p>
<blockquote>
<h2>8.66.0 (2026-08-03)</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.66.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="e51b11ba3a"><code>e51b11b</code></a>
chore(release): publish 8.66.0</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.66.0/packages/project-service">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/scope-manager` from 8.65.0 to 8.66.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/scope-manager's
releases</a>.</em></p>
<blockquote>
<h2>v8.66.0</h2>
<h2>8.66.0 (2026-08-03)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>typescript-estree:</strong> handle import.defer() as
ImportExpression (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12609">#12609</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-conversion]
ignore shadowed built-ins (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12590">#12590</a>)</li>
<li><strong>eslint-plugin:</strong> [prefer-nullish-coalescing] handle
shadowed Boolean calls (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12591">#12591</a>)</li>
<li><strong>eslint-plugin:</strong> [no-useless-default-assignment]
don't report defaults used by other overloads (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12607">#12607</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-parameters]
check MappedType key remapping (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12588">#12588</a>)</li>
<li><strong>eslint-plugin:</strong> [class-literal-property-style]
preserve type annotations and don't drop decorators (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12617">#12617</a>)</li>
<li><strong>website:</strong> list
<code>onUnsupportedTypeScriptVersion</code> in parser options (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12585">#12585</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>Steffen Schroeder</li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.66.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/scope-manager/CHANGELOG.md">@​typescript-eslint/scope-manager's
changelog</a>.</em></p>
<blockquote>
<h2>8.66.0 (2026-08-03)</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.66.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="e51b11ba3a"><code>e51b11b</code></a>
chore(release): publish 8.66.0</li>
<li><a
href="0c94e088e9"><code>0c94e08</code></a>
chore: add CI check to ensure snapshots dirs don't have stale files (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/scope-manager/issues/12623">#12623</a>)</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.66.0/packages/scope-manager">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/tsconfig-utils` from 8.65.0 to 8.66.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/tsconfig-utils's
releases</a>.</em></p>
<blockquote>
<h2>v8.66.0</h2>
<h2>8.66.0 (2026-08-03)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>typescript-estree:</strong> handle import.defer() as
ImportExpression (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12609">#12609</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-conversion]
ignore shadowed built-ins (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12590">#12590</a>)</li>
<li><strong>eslint-plugin:</strong> [prefer-nullish-coalescing] handle
shadowed Boolean calls (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12591">#12591</a>)</li>
<li><strong>eslint-plugin:</strong> [no-useless-default-assignment]
don't report defaults used by other overloads (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12607">#12607</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-parameters]
check MappedType key remapping (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12588">#12588</a>)</li>
<li><strong>eslint-plugin:</strong> [class-literal-property-style]
preserve type annotations and don't drop decorators (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12617">#12617</a>)</li>
<li><strong>website:</strong> list
<code>onUnsupportedTypeScriptVersion</code> in parser options (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12585">#12585</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>Steffen Schroeder</li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.66.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/tsconfig-utils/CHANGELOG.md">@​typescript-eslint/tsconfig-utils's
changelog</a>.</em></p>
<blockquote>
<h2>8.66.0 (2026-08-03)</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.66.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="e51b11ba3a"><code>e51b11b</code></a>
chore(release): publish 8.66.0</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.66.0/packages/tsconfig-utils">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/type-utils` from 8.65.0 to 8.66.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/type-utils's
releases</a>.</em></p>
<blockquote>
<h2>v8.66.0</h2>
<h2>8.66.0 (2026-08-03)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>typescript-estree:</strong> handle import.defer() as
ImportExpression (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12609">#12609</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-conversion]
ignore shadowed built-ins (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12590">#12590</a>)</li>
<li><strong>eslint-plugin:</strong> [prefer-nullish-coalescing] handle
shadowed Boolean calls (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12591">#12591</a>)</li>
<li><strong>eslint-plugin:</strong> [no-useless-default-assignment]
don't report defaults used by other overloads (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12607">#12607</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-parameters]
check MappedType key remapping (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12588">#12588</a>)</li>
<li><strong>eslint-plugin:</strong> [class-literal-property-style]
preserve type annotations and don't drop decorators (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12617">#12617</a>)</li>
<li><strong>website:</strong> list
<code>onUnsupportedTypeScriptVersion</code> in parser options (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12585">#12585</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>Steffen Schroeder</li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.66.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/type-utils/CHANGELOG.md">@​typescript-eslint/type-utils's
changelog</a>.</em></p>
<blockquote>
<h2>8.66.0 (2026-08-03)</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.66.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="e51b11ba3a"><code>e51b11b</code></a>
chore(release): publish 8.66.0</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.66.0/packages/type-utils">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/types` from 8.65.0 to 8.66.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/types's
releases</a>.</em></p>
<blockquote>
<h2>v8.66.0</h2>
<h2>8.66.0 (2026-08-03)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>typescript-estree:</strong> handle import.defer() as
ImportExpression (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12609">#12609</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-conversion]
ignore shadowed built-ins (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12590">#12590</a>)</li>
<li><strong>eslint-plugin:</strong> [prefer-nullish-coalescing] handle
shadowed Boolean calls (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12591">#12591</a>)</li>
<li><strong>eslint-plugin:</strong> [no-useless-default-assignment]
don't report defaults used by other overloads (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12607">#12607</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-parameters]
check MappedType key remapping (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12588">#12588</a>)</li>
<li><strong>eslint-plugin:</strong> [class-literal-property-style]
preserve type annotations and don't drop decorators (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12617">#12617</a>)</li>
<li><strong>website:</strong> list
<code>onUnsupportedTypeScriptVersion</code> in parser options (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12585">#12585</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>Steffen Schroeder</li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.66.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/types/CHANGELOG.md">@​typescript-eslint/types's
changelog</a>.</em></p>
<blockquote>
<h2>8.66.0 (2026-08-03)</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.66.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="e51b11ba3a"><code>e51b11b</code></a>
chore(release): publish 8.66.0</li>
<li><a
href="918411c1b6"><code>918411c</code></a>
chore(types): fix <code>build</code> nx task inputs (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/types/issues/12634">#12634</a>)</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.66.0/packages/types">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/typescript-estree` from 8.65.0 to 8.66.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/typescript-estree's
releases</a>.</em></p>
<blockquote>
<h2>v8.66.0</h2>
<h2>8.66.0 (2026-08-03)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>typescript-estree:</strong> handle import.defer() as
ImportExpression (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12609">#12609</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-conversion]
ignore shadowed built-ins (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12590">#12590</a>)</li>
<li><strong>eslint-plugin:</strong> [prefer-nullish-coalescing] handle
shadowed Boolean calls (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12591">#12591</a>)</li>
<li><strong>eslint-plugin:</strong> [no-useless-default-assignment]
don't report defaults used by other overloads (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12607">#12607</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-parameters]
check MappedType key remapping (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12588">#12588</a>)</li>
<li><strong>eslint-plugin:</strong> [class-literal-property-style]
preserve type annotations and don't drop decorators (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12617">#12617</a>)</li>
<li><strong>website:</strong> list
<code>onUnsupportedTypeScriptVersion</code> in parser options (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12585">#12585</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>Steffen Schroeder</li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.66.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-estree/CHANGELOG.md">@​typescript-eslint/typescript-estree's
changelog</a>.</em></p>
<blockquote>
<h2>8.66.0 (2026-08-03)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>typescript-estree:</strong> handle import.defer() as
ImportExpression (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12609">#12609</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.66.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="e51b11ba3a"><code>e51b11b</code></a>
chore(release): publish 8.66.0</li>
<li><a
href="e88c943f01"><code>e88c943</code></a>
feat(typescript-estree): handle import.defer() as ImportExpression (<a
href="https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-estree/issues/12609">#12609</a>)</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.66.0/packages/typescript-estree">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/utils` from 8.65.0 to 8.66.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/utils's
releases</a>.</em></p>
<blockquote>
<h2>v8.66.0</h2>
<h2>8.66.0 (2026-08-03)</h2>
<h3>🚀 Features</h3>
<ul>
<li><strong>typescript-estree:</strong> handle import.defer() as
ImportExpression (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12609">#12609</a>)</li>
</ul>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-conversion]
ignore shadowed built-ins (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12590">#12590</a>)</li>
<li><strong>eslint-plugin:</strong> [prefer-nullish-coalescing] handle
shadowed Boolean calls (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12591">#12591</a>)</li>
<li><strong>eslint-plugin:</strong> [no-useless-default-assignment]
don't report defaults used by other overloads (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12607">#12607</a>)</li>
<li><strong>eslint-plugin:</strong> [no-unnecessary-type-parameters]
check MappedType key remapping (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12588">#12588</a>)</li>
<li><strong>eslint-plugin:</strong> [class-literal-property-style]
preserve type annotations and don't drop decorators (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12617">#12617</a>)</li>
<li><strong>website:</strong> list
<code>onUnsupportedTypeScriptVersion</code> in parser options (<a
href="https://redirect.github.com/typescript-eslint/typescript-eslint/pull/12585">#12585</a>)</li>
</ul>
<h3>❤️ Thank You</h3>
<ul>
<li>Evyatar Daud <a
href="https://github.com/StyleShit"><code>@​StyleShit</code></a></li>
<li>Kirk Waiblinger <a
href="https://github.com/kirkwaiblinger"><code>@​kirkwaiblinger</code></a></li>
<li>Steffen Schroeder</li>
<li>송재욱</li>
</ul>
<p>See <a
href="https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.66.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/utils/CHANGELOG.md">@​typescript-eslint/utils's
changelog</a>.</em></p>
<blockquote>
<h2>8.66.0 (2026-08-03)</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.66.0">GitHub
Releases</a> for more information.</p>
<p>You can read about our <a
href="https://typescript-eslint.io/users/versioning">versioning
strategy</a> and <a
href="https://typescript-eslint.io/users/releases">releases</a> on our
website.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="e51b11ba3a"><code>e51b11b</code></a>
chore(release): publish 8.66.0</li>
<li>See full diff in <a
href="https://github.com/typescript-eslint/typescript-eslint/commits/v8.66.0/packages/utils">compare
view</a></li>
</ul>
</details>
<br />

Updates `@typescript-eslint/visitor-keys` from 8.65.0 to 8.66.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/typescript-eslint/typescript-eslint/releases">@​typescript-eslint/visitor-keys's
releases</a>.</em></p>
<blockquote>
<h2>v8.66.0</h2>
<h2>8.66.0 (2026-08-03)</h2>
<h3>🚀 Features</h3>
<ul>
<...

_Description has been truncated_

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
2026-08-14 02:39:32 +02:00
nikita-ashihmin
35aaddb3d5 feat: publish the model fallback as a warning advisory (#990)
## Problem

When the API refuses a turn, the SDK retries on the fallback model and
makes the swap persistent for the session. We flag that with a
transcript line:

```ts
text: `**Model fallback:** ${message.original_model} declined this request${category}; retried with ${message.fallback_model}. ${outcome}${explanation}`,
```

It goes out as an `agent_message_chunk`, so the client cannot tell a
session-level advisory from something the model actually said — the bold
label is the only severity signal available, and it is just characters
inside the assistant's own text.

The comment above that code already states the stakes: *"Without a
notice the user just sees regenerated output."* The notice matters; the
channel it uses does not fit it.

## Change

Routes it through the AIR typed session-failure extension instead, as a
new `advisory` category:

- `severity?: "error" | "warning"` on the record. **Optional, and absent
means `error`**, so a client that predates this keeps treating every
record it receives as a failure.
- The advisory carries its own wording (`safeMessage`) rather than the
canned per-category message, so the client renders the explanation
verbatim in a warning banner.
- **Clients that did not negotiate the `sessionFailure` capability keep
the exact transcript line they get today.**

### Advisories are not turn failures

Two existing behaviours would have mangled them, so both are now
guarded:

- `createSessionFailure` clears every other active record when a new one
is published. An advisory no longer does that, and is no longer cleared
by one — otherwise a model-fallback notice would erase an actionable
error, or vice versa.
- `clearFailuresFromEarlierTurns` clears everything whose `turnId` is
not the active turn. Advisories carry no `turnId`, so without the guard
every turn boundary would sweep them away.

They therefore live under their own `:notice:` id namespace,
session-scoped, one slot, newest wins.

## Scoped out

`**Fast mode turned off:**` (`syncFastMode…`) is the same pattern and
deserves the same treatment, but it sits in a class method while the
failure machinery is closure-scoped inside `prompt()`. Lifting that out
is a separate change rather than something to smuggle into this one.

## Tests

`npm run build`, `npm run check`, and `npm run test:run` (**766
passed**) are clean.

The existing `model_refusal_fallback` tests still assert the `**Model
fallback:**` transcript line and still pass — they never call
`initialize`, so they exercise the no-capability path, which is exactly
the backward-compatibility guarantee.

One test added for the typed path, asserting that the advisory is
published with `severity: "warning"`, that its `safeMessage` carries the
real model ids and refusal category, that it uses the `:notice:` id
namespace, that it contains no `**` markdown lead-in, and that the
transcript line is **not** also emitted.

Client side: IJAI-993.
2026-08-12 22:16:28 +04:00
nikita-ashihmin
1f09e9a3ca feat: surface Skill tool calls with name and kind in _meta (#986)
## Summary

- Add `skill?: string` to `ToolUpdateMeta.claudeCode`, populated from
`toolUse.input.skill` in `claudeCodeMetaFromToolUse`. Lets ACP clients
detect skill invocations from structured metadata rather than parsing
the title string.
- Add `case "Skill":` to `toolInfoFromToolUse`: sets `title` to `"Load
skill: <name>"` and returns empty content — readable fallback for
clients that don't read `_meta`.
- Add `case "Skill":` to `toolUpdateFromToolResult`: returns `{}` to
suppress the raw `"Launching skill: <name>"` result text, matching the
pattern used for `Edit`/`Write`.

## Motivation

Without this, the Skill tool falls through to the `default` case and
renders as a generic `kind: "other"` block with title `"Skill"` and an
expand control showing the raw result text. With this change, clients
that read `_meta.claudeCode.skill` can render the block with the
appropriate icon, the skill name, and no expand control.

## Test plan

- [ ] Invoke a skill (e.g. `/commits`) and confirm
`_meta.claudeCode.skill` is present in the emitted `tool_call` update
- [ ] Confirm title is `"Load skill: commits"` in the ACP event stream
- [ ] Confirm no content is emitted for the skill tool result
2026-08-12 14:51:31 +04:00
nikita-ashihmin
ec73cd8560 feat: surface resolved model name in default model option description (#982)
## Summary

- When the SDK's `"default"` model entry carries a `resolvedModel` (e.g.
`"claude-sonnet-5"`), `buildConfigOptions` now sets the option's
`description` to the human-readable `displayName` of the matching named
model (e.g. `"Claude Sonnet 5"`)
- Falls back to the raw `resolvedModel` string if no named entry shares
that resolved ID
- No behaviour change when `resolvedModel` is absent

This lets ACP clients show the actual model behind "Default" — e.g.
"Default (Claude Sonnet 5)" — instead of just "Default (recommended)".

## Test plan

- [ ] `buildConfigOptions model option resolved description > sets
description to the named model's displayName when resolvedModel matches`
- [ ] `buildConfigOptions model option resolved description > falls back
to resolvedModel itself when no named model shares it`
- [ ] `buildConfigOptions model option resolved description > leaves
description undefined when default model has no resolvedModel`
- [ ] All 753 existing tests still pass

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

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-11 13:21:42 +04:00
nikita-ashihmin
8157ee113e feat: expose typed session failures for AIR (#979)
## Summary

- Negotiate the JetBrains AIR `sessionFailure` extension when the client
advertises an integer version `>= 1` and the capability name.
- Return turn-terminal failures only on `PromptResponse._meta`; publish
idle and asynchronous session health failures through `session/update`.
- Preserve legacy ACP behavior for clients that do not negotiate the
extension.
- Emit bounded fixed safe messages without SDK/provider details, paths,
credentials, or stack traces.

## Recovery contract

Failures use `_meta.jetbrains.air.sessionFailure` with a stable id,
monotonic revision, phase, category, source, safe message, retryability,
and optional real turn attribution. Session-scoped ids include a
consumer epoch, so replayed tombstones from a dead consumer cannot
suppress failures after restart. Stale consumers cannot publish into a
replacement session.

AIR owns localized action presentation. Claude supplies typed category
and retryability facts. Billing maps to quota exhaustion, max-output
limits map to context exhaustion, and the irreversibly closed Claude
Query reports non-retryable `transport_lost` so AIR offers New Session.

A held-open turn keeps its already recorded successful `PromptResponse`,
while worker shutdown or transport death also emits a separate
session-scoped health failure without `turnId`; the next prompt is
proven to fail because the Query is closed. Worker-shutdown remains
armed through buffered trailing frames and is classified only when EOF
proves the live tail.

## Validation

- 750 passed, 25 skipped.
- Build passed.
- ESLint passed.
- Changed files pass Prettier; the repository-wide check is blocked only
by an unrelated untracked local proposal file.
- Tests cover negotiation, taxonomy, sanitization, terminal/asynchronous
carrier separation, stale consumers, worker replay/live-tail behavior,
held turns, process/transport failure, restart-safe ids, clear
revisions, and capability-off behavior.
2026-08-10 14:15:44 +04:00
nikita-ashihmin
3df1ede89f fix: show a pending title while Claude prepares a file (#978)
## Summary

- show `Preparing file…` instead of the bare `Write` title while
streamed input has no `file_path`
- refine the same tool call to `Write <display path>` as soon as the
path is available
- cover both partial-field refinement and
complete-input/consolidated-message delivery

## Tests

- `npm run lint`
- `npx prettier --check src/tools.ts src/tests/acp-agent.test.ts`
- `npm run build`
- `npm run test:run` — 716 passed, 25 skipped

YouTrack: https://youtrack.jetbrains.com/issue/IJAI-841
2026-08-09 22:07:58 +04:00
nikita-ashihmin
1afa940a2c fix: preserve task plans across prompts (#974)
## Summary

- republish unfinished Task* plan snapshots when a follow-up prompt
starts
- avoid resurfacing fully completed historical plans on unrelated
prompts
- clear stale task state when Claude starts a fresh conversation
- reconcile task state from authoritative TaskList results after resume
or history compaction
- consume structured Task* outputs during live sessions and parse Claude
Code textual output when replaying history
- reject logically failed TaskUpdate results instead of persisting the
requested mutation
- keep scanning structured tool output past unrelated JSON blocks
- use Claude activeForm for in-progress TodoWrite and Task* entries

This keeps ACP clients from losing an unfinished task list at prompt
boundaries, restores state when incremental history is unavailable, and
prevents plans from leaking across conversation resets.

## Tests

- npm run test:run (713 passed, 20 skipped)
- npm run check
- npm run build
- git diff --check
2026-08-08 14:23:28 +04:00
14 changed files with 4045 additions and 370 deletions

View File

@@ -1,3 +1,3 @@
{
".": "0.66.0"
".": "0.68.0"
}

View File

@@ -1,5 +1,29 @@
# Changelog
## [0.68.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.67.0...v0.68.0) (2026-08-14)
### Features
* align typed session failures with AIR protocol ([#992](https://github.com/agentclientprotocol/claude-agent-acp/issues/992)) ([0581b9c](https://github.com/agentclientprotocol/claude-agent-acp/commit/0581b9cf397ffd88f2830db721c2d8e3689045e4))
## [0.67.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.66.0...v0.67.0) (2026-08-14)
### Features
* **deps:** Update to @anthropic-ai/claude-agent-sdk v0.3.232 ([#993](https://github.com/agentclientprotocol/claude-agent-acp/issues/993)) ([de0d0e2](https://github.com/agentclientprotocol/claude-agent-acp/commit/de0d0e2b7d185c521c3d1ac7b86e4312c919abfe))
* expose typed session failures for AIR ([#979](https://github.com/agentclientprotocol/claude-agent-acp/issues/979)) ([8157ee1](https://github.com/agentclientprotocol/claude-agent-acp/commit/8157ee113e705750be4eb6cc787bdd12f1db84ff))
* publish the model fallback as a warning advisory ([#990](https://github.com/agentclientprotocol/claude-agent-acp/issues/990)) ([35aaddb](https://github.com/agentclientprotocol/claude-agent-acp/commit/35aaddb3d5b14eca316582ab285af8a660150dae))
* surface resolved model name in default model option description ([#982](https://github.com/agentclientprotocol/claude-agent-acp/issues/982)) ([ec73cd8](https://github.com/agentclientprotocol/claude-agent-acp/commit/ec73cd8560be7d5e8b9741e404d7d45e17336996))
* surface Skill tool calls with name and kind in _meta ([#986](https://github.com/agentclientprotocol/claude-agent-acp/issues/986)) ([1f09e9a](https://github.com/agentclientprotocol/claude-agent-acp/commit/1f09e9a3cae787b0effdfae0e7205ef7fe22b9dc))
### Bug Fixes
* preserve task plans across prompts ([#974](https://github.com/agentclientprotocol/claude-agent-acp/issues/974)) ([1afa940](https://github.com/agentclientprotocol/claude-agent-acp/commit/1afa940a2c8c0f4c610f4f64d30c0961642907b0))
* show a pending title while Claude prepares a file ([#978](https://github.com/agentclientprotocol/claude-agent-acp/issues/978)) ([3df1ede](https://github.com/agentclientprotocol/claude-agent-acp/commit/3df1ede89f217312bc237124dc1eccc10c860f99))
## [0.66.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.65.0...v0.66.0) (2026-08-07)

View File

@@ -17,6 +17,7 @@ This tool implements an ACP agent by using the official [Claude Agent SDK](https
- Custom [Slash commands](https://docs.anthropic.com/en/docs/claude-code/slash-commands)
- Client MCP servers
- Session-scoped long-running goals through the provider-neutral [goal extension](docs/goal-extension.md)
- Structured errors, recovery, and warnings through the opt-in [session failure extension](docs/session-failure-extension.md)
Learn more about the [Agent Client Protocol](https://agentclientprotocol.com/).

View File

@@ -0,0 +1,208 @@
# Session failure extension
This document defines the provider-neutral experimental `sessionFailure` extension implemented by
`claude-agent-acp`. Warnings and errors are durable transcript entries shown in order beside user,
agent, and tool messages. They are not assistant-authored text and not ephemeral chat banners.
## Capability negotiation
The extension is opt-in. A client advertises it in `initialize`:
```json
{
"clientCapabilities": {
"_meta": {
"jetbrains": {
"air": {
"version": 1,
"capabilities": ["sessionFailure"]
}
}
}
}
}
```
The adapter enables the extension only when `version` is a finite integer greater than or equal to
`1` and `capabilities` contains `sessionFailure`. Otherwise it preserves the legacy ACP behavior.
## Wire record
The record is placed under `_meta.jetbrains.air.sessionFailure`:
```json
{
"_meta": {
"jetbrains": {
"air": {
"version": 1,
"sessionFailure": {
"id": "prompt-uuid:error",
"revision": 1,
"category": "limit",
"severity": "error",
"title": "You've hit your individual spend limit · run /usage-credits to ask your admin for a higher limit",
"actions": []
}
}
}
}
}
```
| Field | Required | Type | Meaning |
| ---------- | -------: | -------------------- | ----------------------------------------------------------------------- |
| `id` | yes | non-empty string | Stable identity of one logical incident. |
| `revision` | yes | positive integer | Monotonically increasing version of that incident. |
| `category` | yes | category enum | Broad machine-readable visual group. |
| `severity` | yes | `warning` or `error` | Inline warning or error presentation. |
| `title` | yes | string | Complete normal user-facing presentation. |
| `details` | no | string | Long explanation used only when required text is too large for `title`. |
| `actions` | yes | ordered string array | Recovery actions recommended by the adapter. |
There are no `phase`, `source`, `safeMessage`, `retryable`, `retryAfterMs`, `turnId`, retry-counter,
or provider-code fields. Retry progress belongs in `title` when Claude supplies it. Transcript order
and persistence time come from the receiving ACP event.
## Identity, revisions, and history
`id` identifies one occurrence, not an error type.
- The first record for an incident creates one transcript entry at the current stream position.
- The same `id` with a higher `revision` updates that entry in place without moving it.
- The same or a lower revision is idempotently ignored by the client.
- A later independent occurrence uses a new `id`, even when its category and text are identical.
- Consecutive updates for the same notice may reuse one id and increment its revision.
- Different ids are never deduplicated by comparing title or category.
Live turn failures use `<turnId>:error`. Session-scoped incidents use an adapter-session epoch plus an
incident sequence. Notices have their own `:notice:` namespace. Replayed usage-limit failures recover
the persisted user-message UUID used as the live turn id, so the same occurrence keeps the same id.
Malformed history without a preceding user message falls back to
`<sessionId>:history-error:<messageUuid>`.
Resolved records remain in transcript history. Recovery removes only the adapter's internal active
state; it does not publish a clear tombstone and does not delete or rewrite the historical entry. A
producer may publish a higher revision only when it has a real new user-facing status to show.
## Delivery surfaces
A turn-terminal failure is attached to the successful ACP `PromptResponse` in `_meta`; the response
uses `stopReason: end_turn`. Session-scoped, replay-restored, warning, and background incidents use a
`session_info_update`. The schema is identical on both carriers.
A warning does not end a turn. An error record does not independently invent ACP lifecycle state;
the prompt response or transport lifecycle remains authoritative. If no capable client negotiated
the extension, existing JSON-RPC errors and transcript text remain unchanged.
## Categories
Categories are deliberately broad and drive only iconography and generic accessibility labels.
| Category | Covers |
| ------------ | --------------------------------------------------------------------- |
| `connection` | Lost transport, stopped or shutting-down worker, unavailable runtime. |
| `access` | Authentication required, rejected credentials, denied access. |
| `limit` | Rate, quota, token, context, turn, or configured budget limit. |
| `request` | Invalid input, unsupported model or operation, rejected request. |
| `service` | Provider overload, provider failure, adapter/internal failure. |
| `unknown` | A warning or failure that does not fit another group. |
Claude SDK conditions map to these groups:
| Claude condition | Category |
| ---------------------------------------------------------------------------------------- | ---------------------------------- |
| `authentication_failed`, `oauth_org_not_allowed`, synthetic login message | `access` |
| `billing_error`, `rate_limit`, `max_output_tokens`, usage/spend limit, budget/turn limit | `limit` |
| `invalid_request`, `model_not_found` | `request` |
| `overloaded`, `server_error`, unknown provider error, adapter internal error | `service` |
| API retry without an HTTP response, query transport loss, worker shutdown | `connection` |
| model fallback notice | `unknown` with `severity: warning` |
Unknown SDK error kinds degrade to `service`; they never become success. Category does not determine
message text or client behavior beyond presentation.
## Severity
- `warning` means the operation may still succeed and normally has no actions while recovery is in
progress. It does not terminate the turn. SDK `api_retry` progress reuses the active turn failure id;
a later terminal failure updates that record with a higher revision.
- `error` means the operation cannot continue without user action or another request.
Severity is always explicit.
## Title and details
`title` is the complete normal presentation: what happened, retry progress when present, and a short
next step. The adapter must not replace a real Claude message with category-specific canned wording.
For Claude SDK failures the title is copied from the user-facing text Claude already emitted:
- top-level assistant error text, including synthetic usage-limit and login messages;
- otherwise the terminal SDK result or error text;
- the exact model-fallback notice for warning records.
Adapter-authored fallback text is allowed only when no Claude user-facing message exists, such as a
query iterator throwing, worker-shutdown EOF, or an internal missing-result invariant. Raw thrown
exceptions, stack traces, transport URLs, tokens, headers, environment values, and private paths are
never used as titles.
`details` is reserved exclusively for required explanations too large to fit reasonably in `title`.
If the complete message fits in `title`, `details` must be omitted. It is not a place for status text,
retry counters, provider payloads, or diagnostics.
## Actions
Version 1 defines only:
| Action | Meaning |
| ------------- | -------------------------------------------------- |
| `retry` | Retry the operation associated with this incident. |
| `login` | Start authentication. |
| `new_session` | Start a fresh agent session/runtime. |
The adapter supplies action ordering. The client filters actions it cannot safely execute and ignores
unknown or duplicate values. The client must not infer actions from category.
Current Claude policies are:
- authentication: `login`;
- provider overload/error and recoverable internal error: `retry`;
- transport loss or worker shutdown: `new_session`;
- context or configured session-budget exhaustion: `new_session`;
- account quota and invalid request: no action.
## Internal recovery
Recovery policy is internal adapter state and is not serialized:
- restored quota remains active until a real model answer;
- authentication remains active until successful `auth_status`;
- transport loss and worker shutdown remain active until runtime replacement;
- other turn-scoped failures stop being active at a later confirmed attempt boundary;
- notices are not cleared merely by generic success.
Recovery never removes the transcript record. Publishing another incident is not evidence that an
older one recovered.
## History replay
`session/load` scans top-level assistant history for SDK synthetic usage-limit messages. It recognizes
only `<synthetic>` messages beginning with SDK-owned stable prefixes, not arbitrary model prose. Every
recognized occurrence is replayed as a typed record at its original transcript position. The latest
matching message stays active until a later real-model answer proves recovery; older and recovered
records remain historical typed entries.
For a capable client, replay suppresses duplicate assistant prose and restores typed errors using the
exact stored Claude text as `title`. Legacy clients receive the original transcript and no typed record.
## Verification
The executable contract lives primarily in `src/tests/acp-agent.test.ts`, under `usage-limit failure
replay`, `stop reason propagation`, and `model refusal fallback handling`.
```sh
npm run build
npm run check
npm run test:run
```

406
package-lock.json generated
View File

@@ -1,31 +1,31 @@
{
"name": "@agentclientprotocol/claude-agent-acp",
"version": "0.66.0",
"version": "0.68.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@agentclientprotocol/claude-agent-acp",
"version": "0.66.0",
"version": "0.68.0",
"license": "Apache-2.0",
"dependencies": {
"@agentclientprotocol/sdk": "1.3.0",
"@anthropic-ai/claude-agent-sdk": "0.3.220",
"@anthropic-ai/claude-agent-sdk": "0.3.232",
"zod": "^3.25.0 || ^4.0.0"
},
"bin": {
"claude-agent-acp": "dist/index.js"
},
"devDependencies": {
"@anthropic-ai/sdk": "0.115.0",
"@anthropic-ai/sdk": "0.117.1",
"@eslint/js": "10.0.1",
"@tsconfig/node22": "22.0.5",
"@types/node": "26.1.2",
"@typescript-eslint/eslint-plugin": "8.65.0",
"@typescript-eslint/parser": "8.65.0",
"eslint": "10.8.0",
"@types/node": "26.2.0",
"@typescript-eslint/eslint-plugin": "8.67.0",
"@typescript-eslint/parser": "8.67.0",
"eslint": "10.8.1",
"eslint-config-prettier": "10.1.8",
"globals": "17.9.0",
"globals": "17.11.0",
"prettier": "3.9.6",
"ts-node": "10.9.2",
"typescript": "6.0.3",
@@ -45,22 +45,22 @@
}
},
"node_modules/@anthropic-ai/claude-agent-sdk": {
"version": "0.3.220",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.220.tgz",
"integrity": "sha512-glc7SdwPkOkLw8oxwLo9PKTdLJGqW/PIR4urWXFoRtX9YllwozsEVc5Tc1+EvLSkfrsxPJqQWqOgpjUOQXf1oA==",
"version": "0.3.232",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.232.tgz",
"integrity": "sha512-8od7hJk9fZnF1/oYYiR9PvroGbZRQrpmNgKirjHNGoj5ur5YcAZLohI70XVUAUe3KvjB1msLxtkvmlAT9sqFAg==",
"license": "SEE LICENSE IN README.md",
"engines": {
"node": ">=18.0.0"
},
"optionalDependencies": {
"@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.220",
"@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.220",
"@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.220",
"@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.220",
"@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.220",
"@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.220",
"@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.220",
"@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.220"
"@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.232",
"@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.232",
"@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.232",
"@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.232",
"@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.232",
"@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.232",
"@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.232",
"@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.232"
},
"peerDependencies": {
"@anthropic-ai/sdk": ">=0.93.0",
@@ -69,9 +69,9 @@
}
},
"node_modules/@anthropic-ai/claude-agent-sdk-darwin-arm64": {
"version": "0.3.220",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.220.tgz",
"integrity": "sha512-7VxlbEosK7DODiOnsjoVd0DSJzbnaPrM2jelMHI0y8zx1UnLS3WC6EFUXbvy74F2sXqEznh2tzn7EKWInaRN6Q==",
"version": "0.3.232",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.232.tgz",
"integrity": "sha512-+/4PX+dwmQAjlOlooocwa3kClulZfMo133xQH3LYDlK7D5bzze16lwlDGPVAYGEarpvXg7G5JK8QjfWAJ2HYbg==",
"cpu": [
"arm64"
],
@@ -82,9 +82,9 @@
]
},
"node_modules/@anthropic-ai/claude-agent-sdk-darwin-x64": {
"version": "0.3.220",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.220.tgz",
"integrity": "sha512-X9RwDsSmbF6ultKZroaip+DL8WRgC64gHbrAwrRlAFSPNZV7zmJyP2ur8rW7KrxqmtuehdMMkw8+SAC/6hD2PA==",
"version": "0.3.232",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.232.tgz",
"integrity": "sha512-EHZ1Y3aGyZ2mFZ6QLR1bM3/HiIn2cLrPjU+k3/oCIW6omJFodfzf410aWjDPSnMj7CE4d6t7MSjBWekMUbcv0g==",
"cpu": [
"x64"
],
@@ -95,9 +95,9 @@
]
},
"node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64": {
"version": "0.3.220",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.220.tgz",
"integrity": "sha512-WkROPwWskqhKR9XgnmseHQ6rLi9zM9qt57IWoToIjL/eXOqDWipp7JXZ1L5ud+LrA42dunHPZfBwD/vXZ+A7LA==",
"version": "0.3.232",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.232.tgz",
"integrity": "sha512-wW2opwA5s7gLghjU6B2ADMAtoc7bAZMevUzi4g+1PXMJ8MGcPvbnx92EDYJrVDcZmAl1+fz19XyPsaShlisTzw==",
"cpu": [
"arm64"
],
@@ -111,9 +111,9 @@
]
},
"node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64-musl": {
"version": "0.3.220",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.220.tgz",
"integrity": "sha512-OHoZOZ8Cf2TBr6oXIXPwyvUxj9jrq2w8E4poA8dMpacXszcPSPiCQCMuuOh4aWJzfeJE1+TtWxhKMVb2csXyZQ==",
"version": "0.3.232",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.232.tgz",
"integrity": "sha512-XkLcb9UT/l42Rtw7KBApzgxUe/kwoWJ9KCPcVEnYojzIvVR+AwBCl/QReNU5+6c72w48dYBMmLebI64gQbN0tg==",
"cpu": [
"arm64"
],
@@ -127,9 +127,9 @@
]
},
"node_modules/@anthropic-ai/claude-agent-sdk-linux-x64": {
"version": "0.3.220",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.220.tgz",
"integrity": "sha512-tkTJFnpR9VifvWX2fmkCAPkT6+8Wk/gVu8B5jsVekKZPiZoWRHmMXO30BnZn+f0TZhgYP+82PSX3S8crH1kn+w==",
"version": "0.3.232",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.232.tgz",
"integrity": "sha512-6Px1xDiwQyLkSxwRQ34/kPA8WMXQ2rHYGkwockND7+9yMw+ShI3AfLkyi9G7JtJHObWFT6B82eSHjDS/Fy+9hQ==",
"cpu": [
"x64"
],
@@ -143,9 +143,9 @@
]
},
"node_modules/@anthropic-ai/claude-agent-sdk-linux-x64-musl": {
"version": "0.3.220",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.220.tgz",
"integrity": "sha512-K+FWj+LcGhC1Z7wqeWoLxm1iemcba5xKpLLFVwYm4V6HyMx3ruYd/2r2TiQtjT+JWeNFWIys0ScHiItR6vWAiA==",
"version": "0.3.232",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.232.tgz",
"integrity": "sha512-L1x2ge9NpXMLTczmT44TKPQ88PHE+gsCakQMVEOa8rXFvfBoqvcpxM0DT8wrqYWUHhQEYR8NXxQTP9a1NVRj8Q==",
"cpu": [
"x64"
],
@@ -159,9 +159,9 @@
]
},
"node_modules/@anthropic-ai/claude-agent-sdk-win32-arm64": {
"version": "0.3.220",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.220.tgz",
"integrity": "sha512-rIwgq0UwQExWl6KrHUyC4w5KwpL9l6nd95aUTx6RitexaAuEw//xtfTVLnuE4hDDQZFkzEwpdKc3nxDWoGcUbA==",
"version": "0.3.232",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.232.tgz",
"integrity": "sha512-fDiuwL5dm1elOy7fNp4Qmdor8so4R8npj2pUGQA1G/xKfJhaK236aTWezvZid3L/O7cRdFeN9IVofOAlWLQcsw==",
"cpu": [
"arm64"
],
@@ -172,9 +172,9 @@
]
},
"node_modules/@anthropic-ai/claude-agent-sdk-win32-x64": {
"version": "0.3.220",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.220.tgz",
"integrity": "sha512-MuOuXhbr66HlGaWXD2f3w0k2PsvmnbkwcUZ0dAe2poFLdl72GC2dapwwOBefxm9QmoNqk9+jmv/dSKGOVWyvLw==",
"version": "0.3.232",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.232.tgz",
"integrity": "sha512-Hc/9uy1BI9mqKVyB1b/zoUnm3MFgtVNzQY6p5zgaq9DaIjCKDpR4a4L1aDZM4lMqUcWFMZM+u7juOhh+m39NBQ==",
"cpu": [
"x64"
],
@@ -185,9 +185,9 @@
]
},
"node_modules/@anthropic-ai/sdk": {
"version": "0.115.0",
"resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.115.0.tgz",
"integrity": "sha512-BJrFIVyjNuU8lfDyIJTvlRYzgQg+zEl78BxE7fq8esULsGz9IRQvGtW5spq3tydmtjQb/GFdooKGdGsetpx+lQ==",
"version": "0.117.1",
"resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.117.1.tgz",
"integrity": "sha512-Yn2QlXfyCiKJ5YGCOOay7ZE78ISvII2XY621WMCiflmG8IYgwx59IBwPExxki3Xk9jKUtnD/Sj6UvplWr0rZxg==",
"license": "MIT",
"dependencies": {
"json-schema-to-ts": "^3.1.1",
@@ -491,9 +491,9 @@
}
},
"node_modules/@oxc-project/types": {
"version": "0.142.0",
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.142.0.tgz",
"integrity": "sha512-7W+2q5AKQVU36fkaryontrHn3YDt1RyUYXatw9i5H8ocYe2sPKSFB6eS8WNPeRKiN1qAWWZUPm7gwFzJGrccqQ==",
"version": "0.144.0",
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.144.0.tgz",
"integrity": "sha512-nuhZIOLuI6TFQ32I/WnUx+SCPY7SdSKwgnFHydAuoS1+Z4BRcaP+RRJmGzl9lw+0OFF7UmaESf7KQRXaNLHypg==",
"dev": true,
"license": "MIT",
"funding": {
@@ -501,9 +501,9 @@
}
},
"node_modules/@rolldown/binding-android-arm64": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.2.tgz",
"integrity": "sha512-l7x215OGvo1s52JWmR8U/DAVzEDWBCIbTm28aeJV/WDTSHgcKXaZTuBT0hJMs5NggilfJTW3clZVvd24yfKJxA==",
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.4.tgz",
"integrity": "sha512-jHC2cnyKz5xU2fhECtFl8OZ83cYNt13GZQD+0uMJ/X3o+ijmd56okHhTUwxVSHPx1IRVIJEZ1/1pPzeLCU6XKA==",
"cpu": [
"arm64"
],
@@ -518,9 +518,9 @@
}
},
"node_modules/@rolldown/binding-darwin-arm64": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.2.tgz",
"integrity": "sha512-9u9Xv6c1AJZT0FfwH5vrMG5Jjcwhc1MlyrPu0XfTqkzsmqfks2M6W/o5XwAJgVVN/jHpqqngC1WevHKKTIUtIA==",
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.4.tgz",
"integrity": "sha512-Dc5mPD8F5F/FS8i01syd7FTF6yB2fVthH/TRkjwJkzUK6EpoxHtqvZQP5Zwq80/5z19TWYHIg1KOHboCgVx/aQ==",
"cpu": [
"arm64"
],
@@ -535,9 +535,9 @@
}
},
"node_modules/@rolldown/binding-darwin-x64": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.2.tgz",
"integrity": "sha512-9W1mbGZAfW3oqd85bhBkmpyHCCzL1TeG/zFFP3vg7b0rlly8cxOcre5nXwz+LHazCwac2MNWgPdPCHndABjpWQ==",
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.4.tgz",
"integrity": "sha512-fpDm4oBo6SqLvWUYCmFhdde3U9KH2fRNNMeAnAPAIwxRL345xutL0EtEUcuoxsoazdJGv/MuDBQHlCDrtbvqOg==",
"cpu": [
"x64"
],
@@ -552,9 +552,9 @@
}
},
"node_modules/@rolldown/binding-freebsd-x64": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.2.tgz",
"integrity": "sha512-0p1lhiCSCyaerFwtrdZQUx7NqGk6LQnaRKWX7tFQqwQgvX0rjM15cIkm3pax1UpEakK14C4mOxx/jSqCBdBRqQ==",
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.4.tgz",
"integrity": "sha512-rSJoreDE/HoIzoaib6MTp5jQtCTdMHKIvItAKT/ImS6Y6Ww76oUaeMyp4Vc/fAgd/ehji068IxetHXAnqUwN9A==",
"cpu": [
"x64"
],
@@ -569,9 +569,9 @@
}
},
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.2.tgz",
"integrity": "sha512-e+cOJXrJ2L3zx6YzqPg+f6Wbk3V1cKB8bOhbaYdVYN3DdquzNdRAmrbETz1qnt5yp/c7JNlNjmITiA2cVneQ7w==",
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.4.tgz",
"integrity": "sha512-/jm8OGHgn7oGaJu3i/qZI9spUGcJ+y/lk43ttQ/iO1tOd9NissG6o97bighBCiL+BKRngmcDuR6ikfwYdJmVuQ==",
"cpu": [
"arm"
],
@@ -586,9 +586,9 @@
}
},
"node_modules/@rolldown/binding-linux-arm64-gnu": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.2.tgz",
"integrity": "sha512-JsSMsj6sNat/MuhG5fnBD7QgbtpHKVe30x5/bAVirDHdhoQRXJkF6xc0Jqk8O4fiCUQAzMOoH9wZi3m60c8wtg==",
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.4.tgz",
"integrity": "sha512-tIP06BeD9EqvECBrPZ+sqdPlYrT+aYaAiu1wYziVx5elRK/ftm33JxVDy2bXGbr6J0CrtirCkR87/X5a2euEng==",
"cpu": [
"arm64"
],
@@ -606,9 +606,9 @@
}
},
"node_modules/@rolldown/binding-linux-arm64-musl": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.2.tgz",
"integrity": "sha512-B5G/zJdHaoJn9vD50eGHWkiWfmq8Uhi3IiLPJTzmZTrAalk1bztUikSXo0qga18ibE0IXboyeMUnhPjhAJ45wQ==",
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.4.tgz",
"integrity": "sha512-Ql1Q0EQqVThvn9VAVlwNzsUvbSFtCMGjLpRRi4pk5i7NZZ4n5ISiLMjHYtus4VQ2PvkSw24zyaCVsiS+sXPj1w==",
"cpu": [
"arm64"
],
@@ -626,9 +626,9 @@
}
},
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.2.tgz",
"integrity": "sha512-6mC/awzKka8W6EoekjegpfGkjz8jXWDX63pqu/HYVpyKtZfu65Jsh4QAH3Kej3CAv/c1oGX7psTmFEbr0mDxLA==",
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.4.tgz",
"integrity": "sha512-GjbjXD4XXfN19D0LZNbmiCBUoDiRACsYHr0yaIbbn8aFsXjHZifcYqu/W5Er5X2X990WjHXFrxarn5chzItorQ==",
"cpu": [
"ppc64"
],
@@ -646,9 +646,9 @@
}
},
"node_modules/@rolldown/binding-linux-s390x-gnu": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.2.tgz",
"integrity": "sha512-412MX9fJLdA1IK28EZnc8jYv2HRTleOZgfLQumJ5zy7OeJLZlg/CETwFaXjNmGVxG51cFHpKLqb5LKvBC+HsHA==",
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.4.tgz",
"integrity": "sha512-p5WR0NOwaRmJ/B1b6IjEFLLivwEsf3PrdBIhRbhTCQisbo2SvHHpG4ELB/+FgQNnB88LTOF86upmJmbvZdQ2lw==",
"cpu": [
"s390x"
],
@@ -666,9 +666,9 @@
}
},
"node_modules/@rolldown/binding-linux-x64-gnu": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.2.tgz",
"integrity": "sha512-Q/+HI/ToJafZ1iCqGgVQXUEkIjufHCTF0gBQ2a5o3cg7GJ2h0qyq3nvvSmU+bGda2/7ygXpTY4TM6gO9OhQ0ZA==",
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.4.tgz",
"integrity": "sha512-4/GyVjmhR+Tc6HLJvwc1sOhPqAZtySiSMesOZyX6JQ5XBxoTDEMKQzvo07NIK6nTon/SivlZqvhzvuVBNQhObQ==",
"cpu": [
"x64"
],
@@ -686,9 +686,9 @@
}
},
"node_modules/@rolldown/binding-linux-x64-musl": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.2.tgz",
"integrity": "sha512-ZKp/w41n6wCvxzxQHtQSbuphfX3Y4cCvbjkKHusrLx4lh+JWLTU7StSltO/DKARISzbj368d+qUaCjI8K2wzXw==",
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.4.tgz",
"integrity": "sha512-l9eeLsCNvPpmSXUej0etw/J1eqV0Jj1D5G/xG6YTijmE6dkv6E2QezgWbTfQk63v952DPqrjOCoiqxq7Bw0YUQ==",
"cpu": [
"x64"
],
@@ -706,9 +706,9 @@
}
},
"node_modules/@rolldown/binding-openharmony-arm64": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.2.tgz",
"integrity": "sha512-pxE6xD4KS3eAROkKK5yrhB9/3+vhlhVGMvlQLbdpzrBGDbKrnzx3RLwPaHvasLo6jgaiBLn7e04Df9C4tYhjmA==",
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.4.tgz",
"integrity": "sha512-e0F355MSTMm3+UOqtV3L24gFUp2N5m1f8L/7d56deik6va+AXdrt9F8LbzGpeWGWRbZEDq4m8NVnJDeBtf9DZg==",
"cpu": [
"arm64"
],
@@ -723,9 +723,9 @@
}
},
"node_modules/@rolldown/binding-win32-arm64-msvc": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.2.tgz",
"integrity": "sha512-4MqEue5re+xIZzAWsB8sj0P1kqZySWqIuN4t6QaIO/YA6SFwySOLruvWQFzfmqk8LBK2P30KCSJwf6mJCZZ5/A==",
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.4.tgz",
"integrity": "sha512-AWLi0uBRYh6QlE7OKhiz+phZC0qwtij2QZmhmOdsLdFn64m7oMpooE9ICE3lhm9xMb4SpDo2WbHcxX1iFLFtqw==",
"cpu": [
"arm64"
],
@@ -740,9 +740,9 @@
}
},
"node_modules/@rolldown/binding-win32-x64-msvc": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.2.tgz",
"integrity": "sha512-NweNxxD0Nf9t8v7kodun45Ijp3EIwYY+uydPP6qBEYvfBqhIjN6dZMzlQja3tqX/aLs3F3Uz+AxDpKgRhpOZQg==",
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.4.tgz",
"integrity": "sha512-UwSDJOg3dqCAejWdxclJjCsh3Qq4vLYMDxmyHqo1btz3stK2VqgwNd3mm5tuIwzSlGIQ/1H9Hr+Zn09mrezNqQ==",
"cpu": [
"x64"
],
@@ -851,9 +851,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "26.1.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz",
"integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==",
"version": "26.2.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.2.0.tgz",
"integrity": "sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -861,17 +861,17 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.65.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.65.0.tgz",
"integrity": "sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA==",
"version": "8.67.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.67.0.tgz",
"integrity": "sha512-Un7Heoyj65NREbKAyIrFxeM143NZpExWmy1Nep4DLeQOeLlTeumPjoNKnBrU5D5moWXbPJgRa5Uwcdu0faVNGQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.12.2",
"@typescript-eslint/scope-manager": "8.65.0",
"@typescript-eslint/type-utils": "8.65.0",
"@typescript-eslint/utils": "8.65.0",
"@typescript-eslint/visitor-keys": "8.65.0",
"@typescript-eslint/scope-manager": "8.67.0",
"@typescript-eslint/type-utils": "8.67.0",
"@typescript-eslint/utils": "8.67.0",
"@typescript-eslint/visitor-keys": "8.67.0",
"ignore": "^7.0.5",
"natural-compare": "^1.4.0",
"ts-api-utils": "^2.5.0"
@@ -884,22 +884,22 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^8.65.0",
"@typescript-eslint/parser": "^8.67.0",
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.65.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.65.0.tgz",
"integrity": "sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==",
"version": "8.67.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.67.0.tgz",
"integrity": "sha512-fUBfTuuEulWqX6V8+O3PtScV01tzYYRUDTAirHFKoRAt7nOzoGiPt0M/bB47wWNy0coOOcgEwAMUtBpykMxl6w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.65.0",
"@typescript-eslint/types": "8.65.0",
"@typescript-eslint/typescript-estree": "8.65.0",
"@typescript-eslint/visitor-keys": "8.65.0",
"@typescript-eslint/scope-manager": "8.67.0",
"@typescript-eslint/types": "8.67.0",
"@typescript-eslint/typescript-estree": "8.67.0",
"@typescript-eslint/visitor-keys": "8.67.0",
"debug": "^4.4.3"
},
"engines": {
@@ -915,14 +915,14 @@
}
},
"node_modules/@typescript-eslint/project-service": {
"version": "8.65.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz",
"integrity": "sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==",
"version": "8.67.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.67.0.tgz",
"integrity": "sha512-cvE8c7ulYeXN9fYuszhCeCsbzyVEXuhrRCybnBre7TUmqb5nRmBfQAwCj0O3WJFDeyAZt4VYv51vMCC9LHSdYw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.65.0",
"@typescript-eslint/types": "^8.65.0",
"@typescript-eslint/tsconfig-utils": "^8.67.0",
"@typescript-eslint/types": "^8.67.0",
"debug": "^4.4.3"
},
"engines": {
@@ -937,14 +937,14 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "8.65.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz",
"integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==",
"version": "8.67.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.67.0.tgz",
"integrity": "sha512-EgvsleTwS4E+WzzSvem8fAUubLwatMNF1B5hHSLQxcvs7q2dtRhGyujHwLJSYlG41niJ7GP24Aha2+0mb1b2kg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.65.0",
"@typescript-eslint/visitor-keys": "8.65.0"
"@typescript-eslint/types": "8.67.0",
"@typescript-eslint/visitor-keys": "8.67.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -955,9 +955,9 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.65.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz",
"integrity": "sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==",
"version": "8.67.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.67.0.tgz",
"integrity": "sha512-vV+LUSv5njUWsknE71fqKTlXUva+R76SaeORd6Zojcunk/6DvKFXONU3BrAs2H49mbygUXt6gbYunzwqNwlhdg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -972,15 +972,15 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.65.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.65.0.tgz",
"integrity": "sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g==",
"version": "8.67.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.67.0.tgz",
"integrity": "sha512-aVWDXbRmdXO9siTfX4ditQI1T9+zVcNazT48EJCD0v40/9RIFoUgZ05CmGEq9H2gixRpjUn/iplwvlcvutJW/Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.65.0",
"@typescript-eslint/typescript-estree": "8.65.0",
"@typescript-eslint/utils": "8.65.0",
"@typescript-eslint/types": "8.67.0",
"@typescript-eslint/typescript-estree": "8.67.0",
"@typescript-eslint/utils": "8.67.0",
"debug": "^4.4.3",
"ts-api-utils": "^2.5.0"
},
@@ -997,9 +997,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "8.65.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz",
"integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==",
"version": "8.67.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.67.0.tgz",
"integrity": "sha512-sBtgslww8nsMYUjhdPBiSyUqSzT8uR6g93A2QXnQC8+cGdjz0CyaOdqHDRJb1AtORbZCNUJBBeFA/tNR2uQmww==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1011,16 +1011,16 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "8.65.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz",
"integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==",
"version": "8.67.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.67.0.tgz",
"integrity": "sha512-EKQBCE9yNlRJYm7jdTW5AhDacDUmSwQb0FAJAmK2EKYrNXIsa2vxcSZx6PvJ/dEdI6lS+Y9W+EXckLj0iPFGcw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.65.0",
"@typescript-eslint/tsconfig-utils": "8.65.0",
"@typescript-eslint/types": "8.65.0",
"@typescript-eslint/visitor-keys": "8.65.0",
"@typescript-eslint/project-service": "8.67.0",
"@typescript-eslint/tsconfig-utils": "8.67.0",
"@typescript-eslint/types": "8.67.0",
"@typescript-eslint/visitor-keys": "8.67.0",
"debug": "^4.4.3",
"minimatch": "^10.2.2",
"semver": "^7.7.3",
@@ -1039,16 +1039,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "8.65.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.65.0.tgz",
"integrity": "sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==",
"version": "8.67.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.67.0.tgz",
"integrity": "sha512-U9D1FdwEWBwok3hxxSdhclMb0twvt9QnjIQ0VfQ1AiX2epnpSgv2ubVDsayOFyY8K6FX+AQ7E0FKWVG3iKsj1A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.9.1",
"@typescript-eslint/scope-manager": "8.65.0",
"@typescript-eslint/types": "8.65.0",
"@typescript-eslint/typescript-estree": "8.65.0"
"@typescript-eslint/scope-manager": "8.67.0",
"@typescript-eslint/types": "8.67.0",
"@typescript-eslint/typescript-estree": "8.67.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1063,13 +1063,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.65.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz",
"integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==",
"version": "8.67.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.67.0.tgz",
"integrity": "sha512-fkv8dHRDqfGtTHuJeebdrQ7cX6Ad4WAS00rgHh9UGvMycF1mjBfsxry1XsLIFhWZ6Judlh6UdzK+TYlbpCXgnA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.65.0",
"@typescript-eslint/types": "8.67.0",
"eslint-visitor-keys": "^5.0.0"
},
"engines": {
@@ -1344,9 +1344,9 @@
}
},
"node_modules/body-parser/node_modules/content-type": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz",
"integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz",
"integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==",
"license": "MIT",
"peer": true,
"engines": {
@@ -1658,9 +1658,9 @@
}
},
"node_modules/eslint": {
"version": "10.8.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-10.8.0.tgz",
"integrity": "sha512-nuKKvN+oIBO0koN7Tm7dlkmnkc21mtt0QJLwAKzjLq14y6lRTdVG36MZHJ8eQHwdJMwZbQNMlPOYedMq/oVJvQ==",
"version": "10.8.1",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-10.8.1.tgz",
"integrity": "sha512-wqA7W2jbsC/BnV9Iv1UZpKVFkO1AdNoSmYW8NWG4HNOBbkAMvIqDZ27pI2f07dqn583NcIC44ckjAcOXDL1QbQ==",
"dev": true,
"license": "MIT",
"workspaces": [
@@ -1922,9 +1922,9 @@
}
},
"node_modules/eventsource-parser": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.0.tgz",
"integrity": "sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==",
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.1.tgz",
"integrity": "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==",
"license": "MIT",
"peer": true,
"engines": {
@@ -1986,9 +1986,9 @@
}
},
"node_modules/express-rate-limit": {
"version": "8.6.1",
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.6.1.tgz",
"integrity": "sha512-0D493aP61w0TJ2A0wy27riRsO7FMQ7FK+KUHOKCSfPvYo0R55aiC6emCVgFUeShH0fq0ICPVzNcgoS+BsbXQCA==",
"version": "8.6.2",
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.6.2.tgz",
"integrity": "sha512-YH4ru+eOJxQABscKFfRCy9R7x9QFGdezclVMwwgFFndzS2Xnm0uo6B0ABZsLhcpeptGv2qvuJVWlQr9gQZoC3A==",
"license": "MIT",
"peer": true,
"dependencies": {
@@ -2237,9 +2237,9 @@
}
},
"node_modules/globals": {
"version": "17.9.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-17.9.0.tgz",
"integrity": "sha512-m/MvAW61QVU5VDNF1Vj8axt016h8w7L5TU1e9zlab7XIttAT2YAlCwl75K1fOqvMM9apmD7lbCIRhpfkhmxhCg==",
"version": "17.11.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-17.11.0.tgz",
"integrity": "sha512-Z2I8hM+PbJDXQDq3Icgpzv+mPdwr68iZUU9d5WW4FuXfDUQfkZaZuvjMv42/5crNyw154+9+VWXbYrUgDXbxNw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2289,9 +2289,9 @@
}
},
"node_modules/hono": {
"version": "4.13.0",
"resolved": "https://registry.npmjs.org/hono/-/hono-4.13.0.tgz",
"integrity": "sha512-jhunvfHWxd7J5EFfSgH4xsYJzSe/lfqbUCxiyyeaQasUsXeEHXtzVid+7EOGByc5JnFa23SSFL3Y2RV/z1T+eQ==",
"version": "4.13.2",
"resolved": "https://registry.npmjs.org/hono/-/hono-4.13.2.tgz",
"integrity": "sha512-JydRilDRkYBQMt9qR9U92mXxmbGqsqSn/IKOrh4e7/gEbn+0zSr8igTu0obwJoNGN4sez28DIql7FBHWydoJpA==",
"license": "MIT",
"peer": true,
"engines": {
@@ -2364,9 +2364,9 @@
"peer": true
},
"node_modules/ip-address": {
"version": "10.4.0",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.4.0.tgz",
"integrity": "sha512-oSK96Grm3aP6OrS263xVxbNDGVL7rzBtYdpGqlDG8iQdoenDoTs/nkki+DflYbAEE8Xl6o5YxhxlrKvI3nqKXQ==",
"version": "10.5.0",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.5.0.tgz",
"integrity": "sha512-R5SnVLJmgYYvf2F2ZgwSBnelz5G4q5AxIC277GDfUaNbrZKNANcBC7RHqYYePlszf4kBolVkJauG0ZjHHFh55g==",
"license": "MIT",
"peer": true,
"engines": {
@@ -2887,9 +2887,9 @@
"license": "MIT"
},
"node_modules/nanoid": {
"version": "3.3.17",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.17.tgz",
"integrity": "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==",
"version": "3.3.18",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz",
"integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==",
"dev": true,
"funding": [
{
@@ -3110,9 +3110,9 @@
}
},
"node_modules/postcss": {
"version": "8.5.25",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz",
"integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==",
"version": "8.5.26",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz",
"integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==",
"dev": true,
"funding": [
{
@@ -3130,7 +3130,7 @@
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.16",
"nanoid": "^3.3.17",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
@@ -3246,13 +3246,13 @@
}
},
"node_modules/rolldown": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.2.tgz",
"integrity": "sha512-opwpo1tQBAcpSUJDt94B7hhLNGOKjCdE//XXjeLrnx9b83bjnw45tXdg1b09yEw/VLFBJGZpwRULMmOZo7ol+A==",
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.4.tgz",
"integrity": "sha512-rSr7irW0K7QRWzjdJXqZowkcRdDtjRduh43rBltnVKd0VFq839l1lJoDvGJb6gl7+4rTTCrPWu+YfujUL8Ug7w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@oxc-project/types": "=0.142.0",
"@oxc-project/types": "=0.144.0",
"@rolldown/pluginutils": "^1.0.0"
},
"bin": {
@@ -3262,20 +3262,20 @@
"node": "^20.19.0 || >=22.12.0"
},
"optionalDependencies": {
"@rolldown/binding-android-arm64": "1.2.2",
"@rolldown/binding-darwin-arm64": "1.2.2",
"@rolldown/binding-darwin-x64": "1.2.2",
"@rolldown/binding-freebsd-x64": "1.2.2",
"@rolldown/binding-linux-arm-gnueabihf": "1.2.2",
"@rolldown/binding-linux-arm64-gnu": "1.2.2",
"@rolldown/binding-linux-arm64-musl": "1.2.2",
"@rolldown/binding-linux-ppc64-gnu": "1.2.2",
"@rolldown/binding-linux-s390x-gnu": "1.2.2",
"@rolldown/binding-linux-x64-gnu": "1.2.2",
"@rolldown/binding-linux-x64-musl": "1.2.2",
"@rolldown/binding-openharmony-arm64": "1.2.2",
"@rolldown/binding-win32-arm64-msvc": "1.2.2",
"@rolldown/binding-win32-x64-msvc": "1.2.2"
"@rolldown/binding-android-arm64": "1.2.4",
"@rolldown/binding-darwin-arm64": "1.2.4",
"@rolldown/binding-darwin-x64": "1.2.4",
"@rolldown/binding-freebsd-x64": "1.2.4",
"@rolldown/binding-linux-arm-gnueabihf": "1.2.4",
"@rolldown/binding-linux-arm64-gnu": "1.2.4",
"@rolldown/binding-linux-arm64-musl": "1.2.4",
"@rolldown/binding-linux-ppc64-gnu": "1.2.4",
"@rolldown/binding-linux-s390x-gnu": "1.2.4",
"@rolldown/binding-linux-x64-gnu": "1.2.4",
"@rolldown/binding-linux-x64-musl": "1.2.4",
"@rolldown/binding-openharmony-arm64": "1.2.4",
"@rolldown/binding-win32-arm64-msvc": "1.2.4",
"@rolldown/binding-win32-x64-msvc": "1.2.4"
}
},
"node_modules/router": {
@@ -3667,9 +3667,9 @@
}
},
"node_modules/type-is/node_modules/content-type": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz",
"integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz",
"integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==",
"license": "MIT",
"peer": true,
"engines": {
@@ -3739,16 +3739,16 @@
}
},
"node_modules/vite": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/vite/-/vite-8.2.0.tgz",
"integrity": "sha512-pn+CFpM0lwDeKwmOq1ZaBK/9sjorZcgqxki6MbY/jPEVd9vichIlmlD4HmQ5wdP5EgqQCFRaACBxMC7uEGc6lQ==",
"version": "8.2.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-8.2.1.tgz",
"integrity": "sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==",
"dev": true,
"license": "MIT",
"dependencies": {
"lightningcss": "^1.33.0",
"picomatch": "^4.0.5",
"postcss": "^8.5.23",
"rolldown": "~1.2.0",
"postcss": "^8.5.25",
"rolldown": "~1.2.1",
"tinyglobby": "^0.2.17"
},
"bin": {

View File

@@ -3,7 +3,7 @@
"publishConfig": {
"access": "public"
},
"version": "0.66.0",
"version": "0.68.0",
"description": "An ACP-compatible coding agent powered by the Claude Agent SDK (TypeScript)",
"main": "dist/lib.js",
"types": "dist/lib.d.ts",
@@ -66,19 +66,19 @@
},
"dependencies": {
"@agentclientprotocol/sdk": "1.3.0",
"@anthropic-ai/claude-agent-sdk": "0.3.220",
"@anthropic-ai/claude-agent-sdk": "0.3.232",
"zod": "^3.25.0 || ^4.0.0"
},
"devDependencies": {
"@anthropic-ai/sdk": "0.115.0",
"@anthropic-ai/sdk": "0.117.1",
"@eslint/js": "10.0.1",
"@tsconfig/node22": "22.0.5",
"@types/node": "26.1.2",
"@typescript-eslint/eslint-plugin": "8.65.0",
"@typescript-eslint/parser": "8.65.0",
"eslint": "10.8.0",
"@types/node": "26.2.0",
"@typescript-eslint/eslint-plugin": "8.67.0",
"@typescript-eslint/parser": "8.67.0",
"eslint": "10.8.1",
"eslint-config-prettier": "10.1.8",
"globals": "17.9.0",
"globals": "17.11.0",
"prettier": "3.9.6",
"ts-node": "10.9.2",
"typescript": "6.0.3",

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,463 @@
import type { ClientCapabilities, SessionNotification } from "@agentclientprotocol/sdk";
import {
getSessionMessages,
type SDKAssistantMessageError,
USAGE_LIMIT_ERROR_PREFIXES,
} from "@anthropic-ai/claude-agent-sdk";
import { randomUUID } from "node:crypto";
const JETBRAINS_META_KEY = "jetbrains";
const AIR_META_KEY = "air";
const AIR_EXTENSION_VERSION_KEY = "version";
const AIR_EXTENSION_CAPABILITIES_KEY = "capabilities";
const AIR_SESSION_FAILURE_KEY = "sessionFailure";
const AIR_EXTENSION_VERSION = 1;
export function airSessionFailureCapabilityMeta() {
return {
[JETBRAINS_META_KEY]: {
[AIR_META_KEY]: {
[AIR_EXTENSION_VERSION_KEY]: AIR_EXTENSION_VERSION,
[AIR_EXTENSION_CAPABILITIES_KEY]: [AIR_SESSION_FAILURE_KEY],
},
},
};
}
export type ClaudeFailureKind =
| "advisory"
| "auth_required"
| "bad_request"
| "budget_exhausted"
| "context_exhausted"
| "internal_error"
| "overloaded"
| "provider_error"
| "quota_exhausted"
| "rate_limited"
| "transport_lost"
| "worker_shutdown";
type AirSessionFailureCategory =
"connection" | "access" | "limit" | "request" | "service" | "unknown";
type AirSessionFailureSeverity = "warning" | "error";
type AirSessionFailureAction = "retry" | "login" | "new_session";
type SessionFailureRecoveryPolicy =
"never" | "next_attempt" | "real_model_success" | "auth_status" | "runtime_rebind";
export type PublishedSessionFailure = {
id: string;
revision: number;
kind: ClaudeFailureKind;
category: AirSessionFailureCategory;
turnId?: string;
severity: AirSessionFailureSeverity;
title: string;
details?: string;
actions: AirSessionFailureAction[];
recoveryPolicy: SessionFailureRecoveryPolicy;
};
type SessionFailureOptions = {
turnId?: string;
sessionScoped?: boolean;
title?: string;
details?: string;
severity?: AirSessionFailureSeverity;
};
export type SessionFailureState = {
epoch: string;
revisions: Map<string, number>;
active: Map<string, PublishedSessionFailure>;
nextIncident?: number;
lastNotice?: { title: string; id: string };
};
type Logger = {
error: (...args: unknown[]) => void;
};
export function createSessionFailureState(): SessionFailureState {
return {
epoch: randomUUID(),
revisions: new Map(),
active: new Map(),
};
}
const AIR_FAILURE_POLICY: Record<
ClaudeFailureKind,
{
category: AirSessionFailureCategory;
actions: AirSessionFailureAction[];
fallbackTitle: string;
}
> = {
advisory: {
category: "unknown",
actions: [],
fallbackTitle: "Claude reported a notice.",
},
auth_required: {
category: "access",
actions: ["login"],
fallbackTitle: "Sign in to continue using Claude.",
},
bad_request: {
category: "request",
actions: [],
fallbackTitle: "Claude could not process this request.",
},
budget_exhausted: {
category: "limit",
actions: ["new_session"],
fallbackTitle: "This Claude session reached its configured budget.",
},
context_exhausted: {
category: "limit",
actions: ["new_session"],
fallbackTitle: "This Claude turn reached its configured limit.",
},
internal_error: {
category: "service",
actions: ["retry", "new_session"],
fallbackTitle: "Claude Agent encountered an internal error.",
},
overloaded: {
category: "service",
actions: ["retry"],
fallbackTitle: "Claude is temporarily overloaded.",
},
provider_error: {
category: "service",
actions: ["retry"],
fallbackTitle: "The model provider reported an error.",
},
quota_exhausted: {
category: "limit",
actions: [],
fallbackTitle: "The Claude account has no available quota.",
},
rate_limited: {
category: "limit",
actions: ["retry"],
fallbackTitle: "Claude is temporarily rate limited.",
},
transport_lost: {
category: "connection",
actions: ["new_session"],
fallbackTitle: "The connection to Claude was lost.",
},
worker_shutdown: {
category: "connection",
actions: ["new_session"],
fallbackTitle: "The Claude worker is shutting down.",
},
};
export function sessionFailureMeta(failure: PublishedSessionFailure) {
return {
[JETBRAINS_META_KEY]: {
[AIR_META_KEY]: {
[AIR_EXTENSION_VERSION_KEY]: AIR_EXTENSION_VERSION,
[AIR_SESSION_FAILURE_KEY]: {
id: failure.id,
revision: failure.revision,
category: failure.category,
severity: failure.severity,
title: failure.title,
...(failure.details ? { details: failure.details } : {}),
actions: failure.actions,
},
},
},
};
}
/** `getSessionMessages` deliberately exposes only the API message and strips
* transcript-level `error` / `isApiErrorMessage` fields. The SDK exports the
* exact stable prefixes used by its synthetic usage-limit errors, so replay
* can recover this one typed failure without matching arbitrary model prose. */
export function assistantMessageText(apiMessage: unknown): string | undefined {
if (!apiMessage || typeof apiMessage !== "object") return undefined;
const { content } = apiMessage as { content?: unknown };
if (typeof content === "string") return content || undefined;
if (!Array.isArray(content)) return undefined;
const text = content
.map((block) =>
block &&
typeof block === "object" &&
"type" in block &&
block.type === "text" &&
"text" in block &&
typeof block.text === "string"
? block.text
: "",
)
.join("");
return text || undefined;
}
export function isSyntheticUsageLimitMessage(apiMessage: unknown): boolean {
if (!apiMessage || typeof apiMessage !== "object") return false;
const { model } = apiMessage as { model?: unknown };
if (model !== "<synthetic>") return false;
const text = assistantMessageText(apiMessage);
if (!text) return false;
return USAGE_LIMIT_ERROR_PREFIXES.some((prefix) => text.startsWith(prefix));
}
export function activeUsageLimitMessage(
messages: Awaited<ReturnType<typeof getSessionMessages>>,
): { uuid: string; title: string } | undefined {
let active: { uuid: string; title: string } | undefined;
for (const message of messages) {
if (message.type !== "assistant" || message.parent_tool_use_id !== null) continue;
if (isSyntheticUsageLimitMessage(message.message)) {
const title = assistantMessageText(message.message);
if (title) active = { uuid: message.uuid, title };
continue;
}
const model =
message.message && typeof message.message === "object" && "model" in message.message
? message.message.model
: undefined;
// A later real model answer proves the account recovered. Synthetic
// local-command/interruption messages do not: they can be appended while
// the provider remains unavailable.
if (model !== undefined && model !== "<synthetic>") active = undefined;
}
return active;
}
export function supportsAirSessionFailures(capabilities?: ClientCapabilities): boolean {
const jetbrains = capabilities?._meta?.[JETBRAINS_META_KEY] as
Record<string, unknown> | undefined;
const air = jetbrains?.[AIR_META_KEY] as Record<string, unknown> | undefined;
const version = air?.[AIR_EXTENSION_VERSION_KEY];
const advertised = air?.[AIR_EXTENSION_CAPABILITIES_KEY];
return (
typeof version === "number" &&
Number.isFinite(version) &&
Number.isInteger(version) &&
version >= AIR_EXTENSION_VERSION &&
Array.isArray(advertised) &&
advertised.some((capability) => capability === AIR_SESSION_FAILURE_KEY)
);
}
function sessionFailureRecoveryPolicy(
kind: ClaudeFailureKind,
turnId?: string,
): SessionFailureRecoveryPolicy {
switch (kind) {
case "advisory":
return "never";
case "auth_required":
return "auth_status";
case "transport_lost":
case "worker_shutdown":
return "runtime_rebind";
case "quota_exhausted":
return "real_model_success";
default:
return turnId ? "next_attempt" : "real_model_success";
}
}
/** Owns the AIR failure lifecycle for both the live consumer and history replay.
* Published revisions become active only after delivery succeeds; recovery only
* removes internal active state because transcript records are historical. */
export class SessionFailureController {
private readonly sessionId: string;
private readonly state: SessionFailureState;
private readonly capabilities?: ClientCapabilities;
private readonly isCurrent: () => boolean;
private readonly sendUpdate: (notification: SessionNotification) => Promise<void>;
private readonly logger: Logger;
constructor(options: {
sessionId: string;
state: SessionFailureState;
capabilities?: ClientCapabilities;
isCurrent: () => boolean;
sendUpdate: (notification: SessionNotification) => Promise<void>;
logger: Logger;
}) {
this.sessionId = options.sessionId;
this.state = options.state;
this.capabilities = options.capabilities;
this.isCurrent = options.isCurrent;
this.sendUpdate = options.sendUpdate;
this.logger = options.logger;
}
private isSupported(): boolean {
return supportsAirSessionFailures(this.capabilities);
}
private async emit(failure: PublishedSessionFailure): Promise<boolean> {
if (!this.isCurrent()) {
this.logger.error(
`Session ${this.sessionId}: ignored AIR session failure from a stale consumer`,
);
return false;
}
try {
await this.sendUpdate({
sessionId: this.sessionId,
update: {
sessionUpdate: "session_info_update",
_meta: sessionFailureMeta(failure),
},
});
return true;
} catch (error) {
this.logger.error(
`Session ${this.sessionId}: failed to publish AIR session failure: ${error}`,
);
return false;
}
}
recordActive(failure: PublishedSessionFailure): void {
this.state.revisions.set(failure.id, failure.revision);
this.state.active.set(failure.id, failure);
this.state.lastNotice =
failure.kind === "advisory" ? { title: failure.title, id: failure.id } : undefined;
}
async clear(
shouldClear: (failure: PublishedSessionFailure) => boolean = () => true,
): Promise<boolean> {
if (!this.isSupported()) return true;
this.state.lastNotice = undefined;
for (const failure of [...this.state.active.values()]) {
if (!shouldClear(failure)) continue;
this.state.active.delete(failure.id);
}
return true;
}
async prepare(
kind: ClaudeFailureKind,
failureOptions: SessionFailureOptions = {},
): Promise<PublishedSessionFailure | undefined> {
if (!this.isSupported() || !this.isCurrent()) return undefined;
const policy = AIR_FAILURE_POLICY[kind];
const title = failureOptions.title || policy.fallbackTitle;
if (kind === "advisory") {
const id =
this.state.lastNotice?.title === title
? this.state.lastNotice.id
: `${this.sessionId}:notice:${this.state.epoch}:${this.state.nextIncident ?? 1}`;
if (this.state.lastNotice?.title !== title) {
this.state.nextIncident = (this.state.nextIncident ?? 1) + 1;
}
return {
id,
revision: (this.state.revisions.get(id) ?? 0) + 1,
kind,
category: policy.category,
severity: "warning",
title,
...(failureOptions.details ? { details: failureOptions.details } : {}),
actions: policy.actions,
recoveryPolicy: sessionFailureRecoveryPolicy(kind),
};
}
const id =
failureOptions.turnId && !failureOptions.sessionScoped
? `${failureOptions.turnId}:error`
: `${this.sessionId}:session-error:${this.state.epoch}:${this.state.nextIncident ?? 1}`;
if (!failureOptions.turnId || failureOptions.sessionScoped) {
this.state.nextIncident = (this.state.nextIncident ?? 1) + 1;
}
return {
id,
revision: (this.state.revisions.get(id) ?? 0) + 1,
kind,
category: policy.category,
severity: failureOptions.severity ?? "error",
title,
...(failureOptions.details ? { details: failureOptions.details } : {}),
actions: failureOptions.severity === "warning" ? [] : policy.actions,
recoveryPolicy: sessionFailureRecoveryPolicy(
kind,
failureOptions.turnId && !failureOptions.sessionScoped ? failureOptions.turnId : undefined,
),
...(failureOptions.turnId && !failureOptions.sessionScoped
? { turnId: failureOptions.turnId }
: {}),
};
}
async publish(
kind: ClaudeFailureKind,
failureOptions: SessionFailureOptions = {},
): Promise<void> {
const failure = await this.prepare(kind, failureOptions);
if (!failure) return;
if (await this.emit(failure)) this.recordActive(failure);
}
async restore(
id: string,
kind: ClaudeFailureKind,
title: string,
active = true,
): Promise<boolean> {
if (!this.isSupported() || !this.isCurrent()) return false;
const policy = AIR_FAILURE_POLICY[kind];
const failure: PublishedSessionFailure = {
id,
revision: (this.state.revisions.get(id) ?? 0) + 1,
kind,
category: policy.category,
severity: "error",
title,
actions: policy.actions,
recoveryPolicy: sessionFailureRecoveryPolicy(kind),
};
if (!(await this.emit(failure))) return false;
this.state.revisions.set(failure.id, failure.revision);
if (active) {
this.state.active.set(failure.id, failure);
} else {
this.state.active.delete(failure.id);
}
return true;
}
}
export function providerFailureCategory(
errorKind?: SDKAssistantMessageError,
isUsageLimit = false,
): ClaudeFailureKind {
if (isUsageLimit) return "quota_exhausted";
switch (errorKind) {
case "authentication_failed":
case "oauth_org_not_allowed":
return "auth_required";
case "billing_error":
return "quota_exhausted";
case "rate_limit":
return "rate_limited";
case "overloaded":
return "overloaded";
case "invalid_request":
case "model_not_found":
return "bad_request";
case "max_output_tokens":
return "context_exhausted";
case "server_error":
case "unknown":
case undefined:
return "provider_error";
default:
return "provider_error";
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -795,7 +795,7 @@ describe("createSession options merging", () => {
return { onUserDialog: capturedOptions!.onUserDialog!, createElicitation };
}
const signal = () => ({ signal: new AbortController().signal });
const signal = () => ({ signal: new AbortController().signal, requestId: "1" });
it("renders the prompt as a form elicitation and maps the retry choice", async () => {
const { onUserDialog, createElicitation } = await setupDialog();

View File

@@ -0,0 +1,3 @@
{"type":"user","uuid":"11111111-1111-4111-8111-111111111111","parentUuid":null,"sessionId":"aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa","timestamp":"2026-01-01T00:00:00.000Z","message":{"role":"user","content":[{"type":"text","text":"Create a short HTML page"}]}}
{"type":"assistant","uuid":"22222222-2222-4222-8222-222222222222","parentUuid":"11111111-1111-4111-8111-111111111111","sessionId":"aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa","timestamp":"2026-01-01T00:00:01.000Z","error":"rate_limit","isApiErrorMessage":true,"message":{"id":"msg_usage_limit","type":"message","role":"assistant","model":"<synthetic>","content":[{"type":"text","text":"You've hit your individual spend limit · run /usage-credits to ask your admin for a higher limit"}],"stop_reason":"stop_sequence","stop_sequence":null,"usage":{"input_tokens":0,"output_tokens":0,"cache_read_input_tokens":0,"cache_creation_input_tokens":0}}}
{"type":"assistant","uuid":"33333333-3333-4333-8333-333333333333","parentUuid":"22222222-2222-4222-8222-222222222222","sessionId":"aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa","timestamp":"2026-01-01T00:00:02.000Z","message":{"id":"msg_local_command","type":"message","role":"assistant","model":"<synthetic>","content":[{"type":"text","text":"No response requested."}],"stop_reason":"stop_sequence","stop_sequence":null,"usage":{"input_tokens":0,"output_tokens":0,"cache_read_input_tokens":0,"cache_creation_input_tokens":0}}}

View File

@@ -59,7 +59,7 @@ describe("providers", () => {
expect(response.agentCapabilities?.providers).toEqual({});
});
it("lists a single optional 'main' provider, unconfigured by default", async () => {
it("lists one mutually exclusive provider slot with native routing", async () => {
const [agent] = await createAgentMock();
const response = await agent.unstable_listProviders({});
expect(response.providers).toEqual([
@@ -67,7 +67,7 @@ describe("providers", () => {
providerId: "main",
supported: ["anthropic", "bedrock", "vertex"],
required: false,
current: null,
current: { apiType: "anthropic", baseUrl: "https://api.anthropic.com" },
},
]);
});
@@ -126,7 +126,7 @@ describe("providers", () => {
).rejects.toMatchObject({ code: -32602 });
});
it("disables the 'main' provider by clearing config and reporting current: null", async () => {
it("restores native routing after the last override is disabled", async () => {
const [agent] = await createAgentMock();
await agent.unstable_setProvider({
providerId: "main",
@@ -137,7 +137,36 @@ describe("providers", () => {
await expect(agent.unstable_disableProvider({ providerId: "main" })).resolves.toEqual({});
expect((await agent.unstable_listProviders({})).providers[0].current).toBeNull();
expect((await agent.unstable_listProviders({})).providers[0].current).toEqual({
apiType: "anthropic",
baseUrl: "https://api.anthropic.com",
});
});
it("replaces the active api type when the single slot is set again", async () => {
const [agent] = await createAgentMock();
await agent.unstable_setProvider({
providerId: "main",
apiType: "anthropic",
baseUrl: "https://anthropic.example",
});
await agent.unstable_setProvider({
providerId: "main",
apiType: "bedrock",
baseUrl: "https://bedrock.example",
});
const overridden = await agent.unstable_listProviders({});
expect(overridden.providers[0].current).toEqual({
apiType: "bedrock",
baseUrl: "https://bedrock.example",
});
await agent.unstable_disableProvider({ providerId: "main" });
expect((await agent.unstable_listProviders({})).providers[0].current).toEqual({
apiType: "anthropic",
baseUrl: "https://api.anthropic.com",
});
});
it("treats disabling an unknown provider as an idempotent no-op", async () => {
@@ -298,6 +327,9 @@ describe("providers", () => {
await agent.logout({});
expect((await agent.unstable_listProviders({})).providers[0].current).toBeNull();
expect((await agent.unstable_listProviders({})).providers[0].current).toEqual({
apiType: "anthropic",
baseUrl: "https://api.anthropic.com",
});
});
});

View File

@@ -1,4 +1,7 @@
import { describe, it, expect, beforeEach, vi } from "vitest";
import { describe, it, expect, afterEach, beforeEach, vi } from "vitest";
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import * as path from "node:path";
import { ClientCapabilities } from "@agentclientprotocol/sdk";
import { ImageBlockParam, ToolResultBlockParam } from "@anthropic-ai/sdk/resources";
import {
@@ -18,8 +21,10 @@ import {
toolInfoFromToolUse,
planEntries,
applyTaskCreate,
applyTaskList,
applyTaskUpdate,
parseTaskCreateOutput,
parseTaskListOutput,
taskStateToPlanEntries,
TaskState,
} from "../tools.js";
@@ -1817,6 +1822,14 @@ describe("planEntries - undefined input regression", () => {
{ content: "Task 2", status: "completed", priority: "medium" },
]);
});
it("uses activeForm while a todo is in progress", () => {
expect(
planEntries({
todos: [{ content: "Run tests", status: "in_progress", activeForm: "Running tests" }],
}),
).toEqual([{ content: "Running tests", status: "in_progress", priority: "medium" }]);
});
});
describe("toAcpNotifications - TodoWrite with undefined input regression", () => {
@@ -1884,6 +1897,20 @@ describe("parseTaskCreateOutput", () => {
expect(parsed).toEqual({ task: { id: "2", subject: "Y" } });
});
it("continues past unrelated JSON blocks", () => {
const parsed = parseTaskCreateOutput([
{ type: "text", text: JSON.stringify({ metadata: "unrelated" }) },
{ type: "text", text: JSON.stringify({ task: { id: "2", subject: "Y" } }) },
]);
expect(parsed).toEqual({ task: { id: "2", subject: "Y" } });
});
it("parses the human-readable TaskCreate format used in session history", () => {
expect(parseTaskCreateOutput("Task #42 created successfully: Run tests")).toEqual({
task: { id: "42", subject: "Run tests" },
});
});
it("returns undefined for non-JSON content", () => {
expect(parseTaskCreateOutput("not json")).toBeUndefined();
expect(parseTaskCreateOutput([{ type: "text", text: "not json" }])).toBeUndefined();
@@ -1918,11 +1945,15 @@ describe("applyTaskCreate / applyTaskUpdate", () => {
it("updates fields by task ID and keeps insertion order in plan entries", () => {
const state: TaskState = new Map();
applyTaskCreate(state, { subject: "A", description: "" }, { task: { id: "1", subject: "A" } });
applyTaskCreate(
state,
{ subject: "A", description: "", activeForm: "Working on A" },
{ task: { id: "1", subject: "A" } },
);
applyTaskCreate(state, { subject: "B", description: "" }, { task: { id: "2", subject: "B" } });
applyTaskUpdate(state, { taskId: "1", status: "in_progress" });
expect(taskStateToPlanEntries(state)).toEqual([
{ content: "A", status: "in_progress", priority: "medium" },
{ content: "Working on A", status: "in_progress", priority: "medium" },
{ content: "B", status: "pending", priority: "medium" },
]);
});
@@ -1952,6 +1983,84 @@ describe("applyTaskCreate / applyTaskUpdate", () => {
// update is dropped instead of synthesizing a blank placeholder.
expect(state.has("5")).toBe(false);
});
it("rebuilds task state from TaskList while preserving richer local fields", () => {
const state: TaskState = new Map([
[
"1",
{
subject: "Old subject",
status: "pending",
activeForm: "Running the task",
description: "Details",
},
],
["deleted", { subject: "Stale", status: "pending" }],
]);
const output = parseTaskListOutput(
JSON.stringify({
tasks: [
{ id: "1", subject: "Current subject", status: "in_progress", blockedBy: [] },
{ id: "2", subject: "New task", status: "pending", blockedBy: [] },
],
}),
);
expect(output).toBeDefined();
applyTaskList(state, output!);
expect([...state.entries()]).toEqual([
[
"1",
{
subject: "Current subject",
status: "in_progress",
activeForm: "Running the task",
description: "Details",
},
],
[
"2",
{
subject: "New task",
status: "pending",
activeForm: undefined,
description: undefined,
},
],
]);
});
it("finds a TaskList snapshot after an unrelated JSON block", () => {
expect(
parseTaskListOutput([
{ type: "text", text: JSON.stringify({ metadata: "unrelated" }) },
{
type: "text",
text: JSON.stringify({
tasks: [{ id: "1", subject: "Recovered", status: "pending", blockedBy: [] }],
}),
},
]),
).toEqual({
tasks: [{ id: "1", subject: "Recovered", status: "pending", blockedBy: [] }],
});
});
it("parses the human-readable TaskList format used in session history", () => {
expect(
parseTaskListOutput(
"#1 [in_progress] Run tests\n#2 [pending] Write release notes [blocked by #1]",
),
).toEqual({
tasks: [
{ id: "1", subject: "Run tests", status: "in_progress", blockedBy: [] },
{ id: "2", subject: "Write release notes", status: "pending", blockedBy: ["1"] },
],
});
expect(parseTaskListOutput("No tasks found")).toEqual({ tasks: [] });
});
});
describe("toAcpNotifications - Task* tools", () => {
@@ -2112,10 +2221,9 @@ describe("toAcpNotifications - Task* tools", () => {
});
});
it("suppresses TaskList and TaskGet tool_result without touching task state", () => {
it("uses the structured TaskList result as an authoritative plan snapshot", () => {
const toolUseCache: ToolUseCache = {
"list-1": { type: "tool_use", id: "list-1", name: "TaskList", input: {} },
"get-1": { type: "tool_use", id: "get-1", name: "TaskGet", input: { taskId: "1" } },
};
const taskState: TaskState = new Map([
["1", { subject: "Existing", status: "in_progress" as const }],
@@ -2123,8 +2231,93 @@ describe("toAcpNotifications - Task* tools", () => {
const notifications = toAcpNotifications(
[
{ type: "tool_result", tool_use_id: "list-1", content: "...", is_error: false },
{ type: "tool_result", tool_use_id: "get-1", content: "...", is_error: false },
{
type: "tool_result",
tool_use_id: "list-1",
content: "#2 [pending] Recovered",
is_error: false,
},
] as any,
"user",
"test-session",
toolUseCache,
mockClient,
mockLogger,
{
taskState,
toolUseResult: {
tasks: [{ id: "2", subject: "Recovered", status: "pending", blockedBy: [] }],
},
},
);
expect(notifications).toHaveLength(1);
expect(notifications[0].update).toMatchObject({
sessionUpdate: "plan",
entries: [{ content: "Recovered", status: "pending", priority: "medium" }],
});
expect([...taskState.keys()]).toEqual(["2"]);
});
it("does not apply a logically failed TaskUpdate", () => {
const toolUseCache: ToolUseCache = {
"update-1": {
type: "tool_use",
id: "update-1",
name: "TaskUpdate",
input: { taskId: "1", status: "completed" },
},
};
const taskState: TaskState = new Map([["1", { subject: "Existing", status: "pending" }]]);
const notifications = toAcpNotifications(
[
{
type: "tool_result",
tool_use_id: "update-1",
content: "Task #1 not found",
is_error: false,
},
] as any,
"user",
"test-session",
toolUseCache,
mockClient,
mockLogger,
{
taskState,
toolUseResult: {
success: false,
taskId: "1",
updatedFields: [],
error: "Task not found",
},
},
);
expect(notifications).toHaveLength(0);
expect(taskState.get("1")).toEqual({ subject: "Existing", status: "pending" });
});
it("does not apply a replayed TaskUpdate failure without structured output", () => {
const toolUseCache: ToolUseCache = {
"update-1": {
type: "tool_use",
id: "update-1",
name: "TaskUpdate",
input: { taskId: "1", status: "completed" },
},
};
const taskState: TaskState = new Map([["1", { subject: "Existing", status: "pending" }]]);
const notifications = toAcpNotifications(
[
{
type: "tool_result",
tool_use_id: "update-1",
content: "Task #1 not found",
is_error: false,
},
] as any,
"user",
"test-session",
@@ -2135,7 +2328,7 @@ describe("toAcpNotifications - Task* tools", () => {
);
expect(notifications).toHaveLength(0);
expect(taskState.get("1")).toEqual({ subject: "Existing", status: "in_progress" });
expect(taskState.get("1")).toEqual({ subject: "Existing", status: "pending" });
});
it("does not apply TaskCreate/TaskUpdate when the tool_result reports an error", () => {
@@ -2984,3 +3177,147 @@ describe("structured tool_use_result rendering (Read/Bash/WebSearch)", () => {
});
});
});
describe("Skill tool rendering", () => {
const mockLogger: Logger = { log: () => {}, error: () => {} };
describe("toolInfoFromToolUse", () => {
it("sets title to 'Load skill: <name>' and returns empty content", () => {
const info = toolInfoFromToolUse(
{ name: "Skill", id: "toolu_1", input: { skill: "commits" } },
false,
);
expect(info.title).toBe("Load skill: commits");
expect(info.kind).toBe("other");
expect(info.content).toEqual([]);
});
it("falls back to 'Load skill' when skill name is absent", () => {
const info = toolInfoFromToolUse({ name: "Skill", id: "toolu_2", input: {} }, false);
expect(info.title).toBe("Load skill");
expect(info.content).toEqual([]);
});
it("does not throw when input is undefined", () => {
const info = toolInfoFromToolUse({ name: "Skill", id: "toolu_3", input: undefined }, false);
expect(info.title).toBe("Load skill");
expect(info.content).toEqual([]);
});
});
describe("toolUpdateFromToolResult", () => {
it("suppresses the raw 'Launching skill' result text", () => {
const toolUse = {
type: "tool_use",
id: "toolu_4",
name: "Skill",
input: { skill: "commits" },
};
const toolResult = {
type: "tool_result" as const,
tool_use_id: "toolu_4",
content: "Launching skill: commits",
is_error: false,
};
const update = toolUpdateFromToolResult(toolResult, toolUse, false);
expect(update).toEqual({});
});
});
describe("_meta.claudeCode.skill in tool_call notification", () => {
it("includes skill name in _meta.claudeCode when Skill tool is invoked", () => {
const notifications = toAcpNotifications(
[
{ type: "tool_use", id: "toolu_5", name: "Skill", input: { skill: "commits", args: "" } },
] as any,
"assistant",
"test-session",
{},
{} as AcpClient,
mockLogger,
);
expect(notifications[0]?.update).toMatchObject({
sessionUpdate: "tool_call",
_meta: { claudeCode: { toolName: "Skill", skill: "commits" } },
});
});
it("omits skill from _meta.claudeCode when skill name is missing", () => {
const notifications = toAcpNotifications(
[{ type: "tool_use", id: "toolu_6", name: "Skill", input: {} }] as any,
"assistant",
"test-session",
{},
{} as AcpClient,
mockLogger,
);
const meta = (notifications[0]?.update as any)?._meta?.claudeCode;
expect(meta).toBeDefined();
expect(meta.skill).toBeUndefined();
});
});
describe("_meta.claudeCode.skillPath", () => {
const skillMeta = (skill: string, cwd?: string) =>
(
toAcpNotifications(
[{ type: "tool_use", id: "toolu_skill_path", name: "Skill", input: { skill } }] as any,
"assistant",
"test-session",
{},
{} as AcpClient,
mockLogger,
cwd ? { cwd } : undefined,
)[0]?.update as any
)?._meta?.claudeCode;
let root: string;
beforeEach(() => {
root = mkdtempSync(path.join(tmpdir(), "acp-skill-path-"));
});
afterEach(() => {
rmSync(root, { recursive: true, force: true });
});
const writeSkill = (relativeDir: string) => {
const dir = path.join(root, relativeDir);
mkdirSync(dir, { recursive: true });
const file = path.join(dir, "SKILL.md");
writeFileSync(file, "# skill\n");
return file;
};
it("resolves a project-level .claude/skills skill", () => {
const file = writeSkill(".claude/skills/commits");
expect(skillMeta("commits", root).skillPath).toBe(file);
});
it("resolves a project-level .agents/skills skill", () => {
const file = writeSkill(".agents/skills/commits");
expect(skillMeta("commits", root).skillPath).toBe(file);
});
it("resolves a directory-scoped skill spelled prefix:name", () => {
const file = writeSkill("apps/web/.claude/skills/deploy");
expect(skillMeta("apps/web:deploy", root).skillPath).toBe(file);
});
it("resolves a plugin skill spelled plugin:name", () => {
const file = writeSkill(".claude/plugins/reviewer/skills/audit");
expect(skillMeta("reviewer:audit", root).skillPath).toBe(file);
});
it("omits skillPath when no known layout holds the skill", () => {
const meta = skillMeta("nonexistent", root);
expect(meta.skill).toBe("nonexistent");
expect(meta.skillPath).toBeUndefined();
});
it("omits skillPath when the session has no cwd", () => {
writeSkill(".claude/skills/commits");
expect(skillMeta("commits").skillPath).toBeUndefined();
});
});
});

View File

@@ -21,7 +21,9 @@ import {
ReportFindingsInput,
TaskCreateInput,
TaskCreateOutput,
TaskListOutput,
TaskUpdateInput,
TaskUpdateOutput,
TodoWriteInput,
WebFetchInput,
WebSearchInput,
@@ -216,7 +218,7 @@ export function toolInfoFromToolUse(
}
const displayPath = input?.file_path ? toDisplayPath(input.file_path, cwd) : undefined;
return {
title: displayPath ? `Write ${displayPath}` : "Write",
title: displayPath ? `Write ${displayPath}` : "Preparing file…",
kind: "edit",
content,
locations: input?.file_path ? [{ path: input.file_path }] : [],
@@ -439,6 +441,16 @@ export function toolInfoFromToolUse(
};
}
case "Skill": {
const input = toolUse.input as { skill?: string; args?: string } | undefined;
const skillName = input?.skill;
return {
title: skillName ? `Load skill: ${skillName}` : "Load skill",
kind: "other",
content: [],
};
}
case "AskUserQuestion": {
const input = toolUse.input as Partial<AskUserQuestionInput> | undefined;
const questions = Array.isArray(input?.questions) ? input.questions : [];
@@ -861,6 +873,10 @@ export function toolUpdateFromToolResult(
);
}
case "Skill": {
return {};
}
case "Edit": // Edit is handled in hooks
case "Write": {
return {};
@@ -1030,7 +1046,7 @@ export type ClaudePlanEntry = {
export function planEntries(input: { todos: ClaudePlanEntry[] } | undefined): PlanEntry[] {
return (input?.todos ?? []).map((todo) => ({
content: todo.content,
content: todo.status === "in_progress" && todo.activeForm ? todo.activeForm : todo.content,
status: todo.status,
priority: "medium",
}));
@@ -1052,31 +1068,29 @@ export type TaskEntry = {
export type TaskState = Map<string, TaskEntry>;
/**
* Best-effort parse of a TaskCreate tool_result content into the structured
* TaskCreateOutput. The SDK delivers tool outputs either as a string or as
* an array of TextBlockParam-like blocks containing JSON text; try both.
* Best-effort parse of a structured Task* tool_result. The SDK delivers tool
* outputs either as a string or as an array of TextBlockParam-like blocks
* containing JSON text; try both.
*/
export function parseTaskCreateOutput(content: unknown): TaskCreateOutput | undefined {
const tryParse = (text: string): TaskCreateOutput | undefined => {
function parseJsonToolOutput<T>(
content: unknown,
isExpectedOutput: (value: unknown) => value is T,
): T | undefined {
const tryParse = (text: string): T | undefined => {
try {
const parsed = JSON.parse(text);
if (
parsed &&
typeof parsed === "object" &&
parsed.task &&
typeof parsed.task.id === "string"
) {
return parsed as TaskCreateOutput;
}
const parsed: unknown = JSON.parse(text);
return isExpectedOutput(parsed) ? parsed : undefined;
} catch {
// ignore
return undefined;
}
return undefined;
};
if (typeof content === "string") {
return tryParse(content);
}
if (content && typeof content === "object" && !Array.isArray(content)) {
return isExpectedOutput(content) ? content : undefined;
}
if (Array.isArray(content)) {
for (const block of content) {
if (block && typeof block === "object" && "type" in block && block.type === "text") {
@@ -1091,6 +1105,119 @@ export function parseTaskCreateOutput(content: unknown): TaskCreateOutput | unde
return undefined;
}
function toolOutputTexts(content: unknown): string[] {
if (typeof content === "string") return [content];
if (!Array.isArray(content)) return [];
return content.flatMap((block) =>
block &&
typeof block === "object" &&
"type" in block &&
block.type === "text" &&
"text" in block &&
typeof block.text === "string"
? [block.text]
: [],
);
}
export function parseTaskCreateOutput(content: unknown): TaskCreateOutput | undefined {
const structured = parseJsonToolOutput(content, (parsed): parsed is TaskCreateOutput =>
Boolean(
parsed &&
typeof parsed === "object" &&
"task" in parsed &&
parsed.task &&
typeof parsed.task === "object" &&
"id" in parsed.task &&
typeof parsed.task.id === "string",
),
);
if (structured) return structured;
for (const text of toolOutputTexts(content)) {
const match = /^Task #(\S+) created successfully: (.+)$/.exec(text.trim());
if (match) return { task: { id: match[1], subject: match[2] } };
}
return undefined;
}
export function parseTaskListOutput(content: unknown): TaskListOutput | undefined {
const validStatuses = new Set(["pending", "in_progress", "completed"]);
const structured = parseJsonToolOutput(content, (parsed): parsed is TaskListOutput =>
Boolean(
parsed &&
typeof parsed === "object" &&
"tasks" in parsed &&
Array.isArray(parsed.tasks) &&
parsed.tasks.every(
(task) =>
task &&
typeof task === "object" &&
typeof task.id === "string" &&
typeof task.subject === "string" &&
typeof task.status === "string" &&
validStatuses.has(task.status),
),
),
);
if (structured) return structured;
for (const text of toolOutputTexts(content)) {
if (text.trim() === "No tasks found") return { tasks: [] };
const tasks: TaskListOutput["tasks"] = [];
const lines = text.trim().split("\n");
for (const line of lines) {
const match =
/^#(\S+) \[(pending|in_progress|completed)\] (.+?)(?: \(([^()]*)\))?(?: \[blocked by ((?:#[^,\]]+(?:, )?)+)\])?$/.exec(
line,
);
if (!match) {
tasks.length = 0;
break;
}
tasks.push({
id: match[1],
subject: match[3],
status: match[2] as TaskListOutput["tasks"][number]["status"],
...(match[4] ? { owner: match[4] } : {}),
blockedBy: match[5] ? match[5].split(", ").map((id) => id.slice(1)) : [],
});
}
if (tasks.length > 0) return { tasks };
}
return undefined;
}
export function parseTaskUpdateOutput(
content: unknown,
expectedTaskId?: string,
): TaskUpdateOutput | undefined {
const structured = parseJsonToolOutput(content, (parsed): parsed is TaskUpdateOutput =>
Boolean(
parsed &&
typeof parsed === "object" &&
"success" in parsed &&
typeof parsed.success === "boolean" &&
"taskId" in parsed &&
typeof parsed.taskId === "string" &&
"updatedFields" in parsed &&
Array.isArray(parsed.updatedFields) &&
parsed.updatedFields.every((field) => typeof field === "string"),
),
);
if (structured) return structured;
for (const text of toolOutputTexts(content)) {
const notFound = /^Task #(\S+) not found$/.exec(text.trim());
const taskId = notFound?.[1] ?? expectedTaskId;
if (taskId && (notFound || text.trim() === "Failed to delete task")) {
return { success: false, taskId, updatedFields: [], error: text.trim() };
}
}
return undefined;
}
export function applyTaskCreate(
state: TaskState,
input: TaskCreateInput | undefined,
@@ -1125,9 +1252,23 @@ export function applyTaskUpdate(state: TaskState, input: TaskUpdateInput | undef
});
}
export function applyTaskList(state: TaskState, output: TaskListOutput): void {
const previous = new Map(state);
state.clear();
for (const task of output.tasks) {
const existing = previous.get(task.id);
state.set(task.id, {
subject: task.subject,
status: task.status,
activeForm: existing?.activeForm,
description: existing?.description,
});
}
}
export function taskStateToPlanEntries(state: TaskState): PlanEntry[] {
return Array.from(state.values()).map((task) => ({
content: task.subject,
content: task.status === "in_progress" && task.activeForm ? task.activeForm : task.subject,
status: task.status,
priority: "medium",
}));