Compare commits

..

9 Commits

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


##
[0.85.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.84.0...v0.85.0)
(2026-10-01)


### Features

* **deps:** Bump @anthropic-ai/claude-agent-sdk to 0.3.286
([#1211](https://github.com/agentclientprotocol/claude-agent-acp/issues/1211))
([f53b0d7](f53b0d7f40))


### Bug Fixes

* Adapt to SDK 0.3.284 replay and ultracode changes
([#1208](https://github.com/agentclientprotocol/claude-agent-acp/issues/1208))
([4af8079](4af807968c))
* send the legacy custom answer key to AIR
([#1210](https://github.com/agentclientprotocol/claude-agent-acp/issues/1210))
([af0b43d](af0b43de94))

---
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-10-01 13:38:29 +02:00
Kirill Alekhnovich
af0b43de94 fix: send the legacy custom answer key to AIR (#1210)
Released AIR versions, such as 262.air/release-09, read the custom
answer companion only from the root key
_meta._askUserQuestionCustomAnswer. The adapter sent only
_meta.jetbrains.air.customAnswer, so these versions showed the companion
as a separate question.

For an AIR client the companion now carries both keys with the same
value. Other clients get no key, as before. Remove the root key when the
ACP registry no longer serves AIR versions that read only that key.

IJAI-2892
2026-10-01 13:25:01 +02:00
Ben Brandt
f53b0d7f40 feat(deps): Bump @anthropic-ai/claude-agent-sdk to 0.3.286 (#1211)
## Summary

- Bumps `@anthropic-ai/claude-agent-sdk` to 0.3.286 (Claude Code
2.1.286). No compile or test fixes were needed.
- Updates the steer comments for a CLI behavior change. A `priority:
'now'` steer that lands during a foreground tool call (Bash, Agent, MCP)
now moves the tool to the background instead of stopping the turn.
Depending on timing, the CLI then either:
- adds the steer to the running cycle, which ends in **one** result
stamped with both the prompt's and the steer's uuids, or
  - ends the cycle early and runs the steer as a second cycle.

  During plain text output it still stops the turn, as before.
- Adds a test for the case where the steer joins the running cycle, with
and without an echo of the steer. The case where the steer runs as a
second cycle was already covered.

I checked all of this against the live CLI 2.1.286. In each case the
result that answered the steer carried the steer's uuid, so the existing
steer settlement logic handles it without code changes.

## Review notes for 0.3.285 and 0.3.286

- **Raw XML in subagent replay:** `getSubagentMessages()` now returns
delivered `<task-notification>` rows as plain user messages (`origin:
{kind: "task-notification"}`, `isQueuedCommand: true`, no `is_meta`).
They reach child replay and show up as raw XML user messages. #1205
already hides this exact shape, so it fixes the subagent case once
merged.
- **Omitted `permissionMode`:** the SDK now leaves it to the CLI. The
adapter always passes an explicit mode, so this has no effect.
- **No changes needed:** the `fallback_credit` type, Artifact pin/unpin,
`provider_not_allowed`, the SDK MCP manifest fields, the background Bash
timeout (its stop notice goes through the existing task-stopped path),
and the `setModel` output-limit fix.

## Test plan

- [x] `npm run build`
- [x] `npm run test:run` (2095 passed)
- [x] `npm run check`

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-10-01 13:23:51 +02:00
Ben Brandt
4af807968c fix: Adapt to SDK 0.3.284 replay and ultracode changes (#1208)
Follow-ups to the SDK 0.3.284 / CLI 2.1.284 bump (#1189), which landed
with only package changes.

## Replay showed messages from other agents and sessions as user text

Since 0.3.284, `getSessionMessages()` returns `is_meta` user messages
from other agents, sessions and channels (`origin.kind` peer, channel,
observer, observer-activity, slack-ping). Their content is the full
harness framing: the `<cross-session-message>` envelope plus the "not
typed by your user" preamble. `replayMessage` sent them as
`user_message_chunk`, while the live prompt loop never shows them.
Replay now skips them. Other `is_meta` messages (compact summaries)
still replay.

Verified against a live 0.3.284 transcript: the enveloped peer turn is
gone, and the user's own prompts still replay.

Fixes #1203. Complements #1205, which hides `task-notification` records
the same way. The two cover different origin kinds and merge with only a
trivial test-file conflict.

## Effort applies turned ultracode off

CLI 2.1.284 turns ultracode off when an `applyFlagSettings` call changes
`effortLevel` without an `ultracode` key. The adapter applies effort at
session start (recommended-value clients), after a model switch and from
the effort picker, so a settings-enabled `ultracode: true` was silently
cleared. The CLI's own `/effort` control now keeps ultracode on at any
level, so all three applies send `ultracode: true` when the settings
request it (`effortFlagSettings`).

## Checked, no change needed

Elicitation-hook `decision: 'block'`, the background-agent stdin fix
(the adapter never hit it; keep
`CLAUDE_CODE_EMIT_SESSION_STATE_EVENTS=1`), MCP startup waits, Sonnet
5.5 (natively 1M, already covered by the background context-window
refresh), malformed-image handling (now done by the CLI), and notice
rewording (the adapter matches none of the changed strings).

## Testing

- New tests for both helpers, replay of the probe's peer-message shape,
and a session test checking that the startup and picker applies keep
ultracode.
- `npm run build`, `npm run check` and `npm run test:run` (2093 passed)
all pass.

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

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 10:43:11 +00:00
acp-release-bot[bot]
bdb50ad984 chore(main): release 0.84.0 (#1190)
🤖 I have created a release *beep* *boop*
---


##
[0.84.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.83.0...v0.84.0)
(2026-09-28)


### Features

* **deps:** update Claude SDK
([#1189](https://github.com/agentclientprotocol/claude-agent-acp/issues/1189))
([d5e6f33](d5e6f33046))

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

Co-authored-by: acp-release-bot[bot] <246668977+acp-release-bot[bot]@users.noreply.github.com>
2026-09-28 21:15:22 +02:00
Andrey Bragin
d5e6f33046 feat(deps): update Claude SDK (#1189) 2026-09-28 21:12:52 +02:00
acp-release-bot[bot]
691328a919 chore(main): release 0.83.0 (#1188)
🤖 I have created a release *beep* *boop*
---


##
[0.83.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.82.0...v0.83.0)
(2026-09-28)


### Features

* **deps:** Bump @anthropic-ai/claude-agent-sdk to 0.3.283
([#1186](https://github.com/agentclientprotocol/claude-agent-acp/issues/1186))
([49858b5](49858b5273))

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

Co-authored-by: acp-release-bot[bot] <246668977+acp-release-bot[bot]@users.noreply.github.com>
2026-09-28 17:55:23 +02:00
dependabot[bot]
49858b5273 feat(deps): Bump @anthropic-ai/claude-agent-sdk to 0.3.283 (#1186)
Bumps the minor group with 38 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
|
[@anthropic-ai/claude-agent-sdk](https://github.com/anthropics/claude-agent-sdk-typescript)
| `0.3.280` | `0.3.282` |
|
[@anthropic-ai/sdk](https://github.com/anthropics/anthropic-sdk-typescript)
| `0.127.0` | `0.128.0` |
| [prettier](https://github.com/prettier/prettier) | `3.9.8` | `3.9.9` |
|
[vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest)
| `5.0.1` | `5.0.2` |
| @anthropic-ai/claude-agent-sdk-darwin-arm64 | `0.3.280` | `0.3.282` |
| @anthropic-ai/claude-agent-sdk-darwin-x64 | `0.3.280` | `0.3.282` |
| @anthropic-ai/claude-agent-sdk-linux-arm64 | `0.3.280` | `0.3.282` |
| @anthropic-ai/claude-agent-sdk-linux-arm64-musl | `0.3.280` |
`0.3.282` |
| @anthropic-ai/claude-agent-sdk-linux-x64 | `0.3.280` | `0.3.282` |
| @anthropic-ai/claude-agent-sdk-linux-x64-musl | `0.3.280` | `0.3.282`
|
| @anthropic-ai/claude-agent-sdk-win32-arm64 | `0.3.280` | `0.3.282` |
| @anthropic-ai/claude-agent-sdk-win32-x64 | `0.3.280` | `0.3.282` |
|
[@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk)
| `1.30.0` | `1.30.1` |
|
[@oxc-project/types](https://github.com/oxc-project/oxc/tree/HEAD/npm/oxc-types)
| `0.150.0` | `0.151.0` |
|
[@rolldown/binding-android-arm-eabi](https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown)
| `1.2.9` | `1.2.11` |
|
[@rolldown/binding-android-arm64](https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown)
| `1.2.9` | `1.2.11` |
|
[@rolldown/binding-darwin-arm64](https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown)
| `1.2.9` | `1.2.11` |
|
[@rolldown/binding-darwin-x64](https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown)
| `1.2.9` | `1.2.11` |
|
[@rolldown/binding-freebsd-x64](https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown)
| `1.2.9` | `1.2.11` |
|
[@rolldown/binding-linux-arm-gnueabihf](https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown)
| `1.2.9` | `1.2.11` |
|
[@rolldown/binding-linux-arm64-gnu](https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown)
| `1.2.9` | `1.2.11` |
|
[@rolldown/binding-linux-arm64-musl](https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown)
| `1.2.9` | `1.2.11` |
|
[@rolldown/binding-linux-ppc64-gnu](https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown)
| `1.2.9` | `1.2.11` |
|
[@rolldown/binding-linux-s390x-gnu](https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown)
| `1.2.9` | `1.2.11` |
|
[@rolldown/binding-linux-x64-gnu](https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown)
| `1.2.9` | `1.2.11` |
|
[@rolldown/binding-linux-x64-musl](https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown)
| `1.2.9` | `1.2.11` |
|
[@rolldown/binding-openharmony-arm64](https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown)
| `1.2.9` | `1.2.11` |
|
[@rolldown/binding-win32-arm64-msvc](https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown)
| `1.2.9` | `1.2.11` |
|
[@rolldown/binding-win32-x64-msvc](https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown)
| `1.2.9` | `1.2.11` |
|
[@vitest/mocker](https://github.com/vitest-dev/vitest/tree/HEAD/packages/mocker)
| `5.0.1` | `5.0.2` |
|
[@vitest/spy](https://github.com/vitest-dev/vitest/tree/HEAD/packages/spy)
| `5.0.1` | `5.0.2` |
| [hono](https://github.com/honojs/hono) | `4.13.8` | `4.13.10` |
| [magic-string](https://github.com/Rich-Harris/magic-string) | `1.4.1`
| `1.4.2` |
|
[rolldown](https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown)
| `1.2.9` | `1.2.11` |
| [tinybench](https://github.com/tinylibs/tinybench) | `6.1.4` | `6.2.0`
|
| [tinyexec](https://github.com/tinylibs/tinyexec) | `1.3.0` | `1.3.1` |
| [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) |
`8.3.0` | `8.3.1` |
|
[why-is-node-running](https://github.com/mafintosh/why-is-node-running)
| `2.3.0` | `3.2.2` |


Updates `@anthropic-ai/claude-agent-sdk` from 0.3.280 to 0.3.282
<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.282</h2>
<h2>What's changed</h2>
<ul>
<li>Added support for <code>strictKnownMarketplaces</code> and
<code>blockedMarketplaces</code> in host-supplied
<code>managedSettings</code>: the allowlist applies only where admin
policy sets none; the blocklist adds to the admin's</li>
<li>Added <code>@anthropic-ai/claude-agent-sdk/core</code>, a smaller
entry point for apps that bundle the SDK (query, MCP tool helpers,
session mutations, <code>resolveSettings</code>); it uses your installed
zod and MCP SDK</li>
<li>Added <code>prewarm()</code> and <code>SpareProcess.claim()</code>
(alpha): start a Claude Code process before its session is known and
bind it to a folder and its per-session options later</li>
<li>Fixed <code>readMcpResource()</code> relaying content
<code>_meta</code> keys under the CLI-reserved
<code>com.anthropic/</code> prefix; they are now dropped, as for tool
results</li>
<li>Updated to parity with Claude Code v2.1.282</li>
</ul>
<h2>Update</h2>
<pre lang="sh"><code>npm install @anthropic-ai/claude-agent-sdk@0.3.282
# or
yarn add @anthropic-ai/claude-agent-sdk@0.3.282
# or
pnpm add @anthropic-ai/claude-agent-sdk@0.3.282
# or
bun add @anthropic-ai/claude-agent-sdk@0.3.282
</code></pre>
<h2>v0.3.281</h2>
<h2>What's changed</h2>
<ul>
<li>Added optional <code>trigger</code>, <code>user_message_uuid</code>
and <code>timestamp</code> fields to the <code>conversation_reset</code>
message so clients can tell what reset the conversation, match a /clear
to its message, and show when the reset happened</li>
<li>Fixed permission and dialog callbacks still being invoked for
requests that arrived after <code>close()</code></li>
<li>Fixed control requests issued after a query closed hanging or
leaking, and permission prompts that could not be cancelled after a
cancelled request was redelivered</li>
<li>Fixed <code>session_state_changed</code> staying at
<code>requires_action</code> after an overlapping permission prompt and
sandbox network-access prompt were both answered</li>
<li>Improved SDK package size and load time: <code>sdk.mjs</code> no
longer bundles unused dependencies (1.47 MB → 0.97 MB)</li>
<li>Improved startup time for <code>query()</code> sessions with
in-process MCP servers (<code>createSdkMcpServer</code>) by running
their handshake inside the SDK; <code>initialize</code> may now wait up
to 250 ms for them</li>
<li>Improved SDK session start-up: the CLI now answers the host's
initialize request before starting its background start-up work</li>
<li>Changed the <code>Settings</code> type's <code>attribution</code>
field to <code>boolean | {...}</code>; TypeScript code that reads
<code>attribution.commit</code> from returned settings needs a type
narrow</li>
<li>Updated to parity with Claude Code v2.1.281</li>
</ul>
<h2>Update</h2>
<pre lang="sh"><code>npm install @anthropic-ai/claude-agent-sdk@0.3.281
# or
yarn add @anthropic-ai/claude-agent-sdk@0.3.281
# or
pnpm add @anthropic-ai/claude-agent-sdk@0.3.281
# or
bun add @anthropic-ai/claude-agent-sdk@0.3.281
</code></pre>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/anthropics/claude-agent-sdk-typescript/blob/main/CHANGELOG.md">@​anthropic-ai/claude-agent-sdk's
changelog</a>.</em></p>
<blockquote>
<h2>0.3.282</h2>
<ul>
<li>Added support for <code>strictKnownMarketplaces</code> and
<code>blockedMarketplaces</code> in host-supplied
<code>managedSettings</code>: the allowlist applies only where admin
policy sets none; the blocklist adds to the admin's</li>
<li>Added <code>@anthropic-ai/claude-agent-sdk/core</code>, a smaller
entry point for apps that bundle the SDK (query, MCP tool helpers,
session mutations, <code>resolveSettings</code>); it uses your installed
zod and MCP SDK</li>
<li>Added <code>prewarm()</code> and <code>SpareProcess.claim()</code>
(alpha): start a Claude Code process before its session is known and
bind it to a folder and its per-session options later</li>
<li>Fixed <code>readMcpResource()</code> relaying content
<code>_meta</code> keys under the CLI-reserved
<code>com.anthropic/</code> prefix; they are now dropped, as for tool
results</li>
<li>Updated to parity with Claude Code v2.1.282</li>
</ul>
<h2>0.3.281</h2>
<ul>
<li>Added optional <code>trigger</code>, <code>user_message_uuid</code>
and <code>timestamp</code> fields to the <code>conversation_reset</code>
message so clients can tell what reset the conversation, match a /clear
to its message, and show when the reset happened</li>
<li>Fixed permission and dialog callbacks still being invoked for
requests that arrived after <code>close()</code></li>
<li>Fixed control requests issued after a query closed hanging or
leaking, and permission prompts that could not be cancelled after a
cancelled request was redelivered</li>
<li>Fixed <code>session_state_changed</code> staying at
<code>requires_action</code> after an overlapping permission prompt and
sandbox network-access prompt were both answered</li>
<li>Improved SDK package size and load time: <code>sdk.mjs</code> no
longer bundles unused dependencies (1.47 MB → 0.97 MB)</li>
<li>Improved startup time for <code>query()</code> sessions with
in-process MCP servers (<code>createSdkMcpServer</code>) by running
their handshake inside the SDK; <code>initialize</code> may now wait up
to 250 ms for them</li>
<li>Improved SDK session start-up: the CLI now answers the host's
initialize request before starting its background start-up work</li>
<li>Changed the <code>Settings</code> type's <code>attribution</code>
field to <code>boolean | {...}</code>; TypeScript code that reads
<code>attribution.commit</code> from returned settings needs a type
narrow</li>
<li>Updated to parity with Claude Code v2.1.281</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="9e477a178c"><code>9e477a1</code></a>
chore: Update CHANGELOG.md</li>
<li><a
href="fb04f66354"><code>fb04f66</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.280...v0.3.282">compare
view</a></li>
</ul>
</details>
<br />

Updates `@anthropic-ai/sdk` from 0.127.0 to 0.128.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.128.0</h2>
<h2>0.128.0 (2026-09-22)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.127.0...sdk-v0.128.0">sdk-v0.127.0...sdk-v0.128.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> add support for claude-opus-5-5, inline tool
definitions and MCP tool-list pinning (beta) (<a
href="eff27483d2">eff2748</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>api:</strong> share one evaluated_permission enum across
Managed Agents events (<a
href="0c1cc0fdf8">0c1cc0f</a>)</li>
<li><strong>tools:</strong> addTools() takes effect straight away (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/773">#773</a>)
(<a
href="1ccdad0d1b">1ccdad0</a>)</li>
<li><strong>tools:</strong> leave reply-only params off the tool
runner's compaction request (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/769">#769</a>)
(<a
href="8afc3364dc">8afc336</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><strong>docs:</strong> add descriptions to the Dreams API reference
(<a
href="ea4ddd1ee3">ea4ddd1</a>)</li>
<li><strong>docs:</strong> add descriptions to the User Profiles API
reference (<a
href="ea4ddd1ee3">ea4ddd1</a>)</li>
<li><strong>docs:</strong> add memory store descriptions to the Managed
Agents API reference (<a
href="ea4ddd1ee3">ea4ddd1</a>)</li>
<li><strong>docs:</strong> improve descriptions in the Dreams API
reference (<a
href="971c54a5dd">971c54a</a>)</li>
<li><strong>docs:</strong> simplify the session thread agent type
description (<a
href="ccf71a27bd">ccf71a2</a>)</li>
<li><strong>docs:</strong> update diagnostics field descriptions on beta
messages (<a
href="c0b8746679">c0b8746</a>)</li>
<li><strong>internal:</strong> add property-based tests for partial JSON
parsing (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/776">#776</a>)
(<a
href="3ffac0927f">3ffac09</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.128.0 (2026-09-22)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.127.0...sdk-v0.128.0">sdk-v0.127.0...sdk-v0.128.0</a></p>
<h3>Features</h3>
<ul>
<li><strong>api:</strong> add support for claude-opus-5-5, inline tool
definitions and MCP tool-list pinning (beta) (<a
href="eff27483d2">eff2748</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>api:</strong> share one evaluated_permission enum across
Managed Agents events (<a
href="0c1cc0fdf8">0c1cc0f</a>)</li>
<li><strong>tools:</strong> addTools() takes effect straight away (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/773">#773</a>)
(<a
href="1ccdad0d1b">1ccdad0</a>)</li>
<li><strong>tools:</strong> leave reply-only params off the tool
runner's compaction request (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/769">#769</a>)
(<a
href="8afc3364dc">8afc336</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><strong>docs:</strong> add descriptions to the Dreams API reference
(<a
href="ea4ddd1ee3">ea4ddd1</a>)</li>
<li><strong>docs:</strong> add descriptions to the User Profiles API
reference (<a
href="ea4ddd1ee3">ea4ddd1</a>)</li>
<li><strong>docs:</strong> add memory store descriptions to the Managed
Agents API reference (<a
href="ea4ddd1ee3">ea4ddd1</a>)</li>
<li><strong>docs:</strong> improve descriptions in the Dreams API
reference (<a
href="971c54a5dd">971c54a</a>)</li>
<li><strong>docs:</strong> simplify the session thread agent type
description (<a
href="ccf71a27bd">ccf71a2</a>)</li>
<li><strong>docs:</strong> update diagnostics field descriptions on beta
messages (<a
href="c0b8746679">c0b8746</a>)</li>
<li><strong>internal:</strong> add property-based tests for partial JSON
parsing (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/776">#776</a>)
(<a
href="3ffac0927f">3ffac09</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="1926adb4d2"><code>1926adb</code></a>
Merge pull request <a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/1208">#1208</a>
from anthropics/release-please--branches--main--chan...</li>
<li><a
href="162c8b125d"><code>162c8b1</code></a>
chore: release main</li>
<li><a
href="3ffac0927f"><code>3ffac09</code></a>
chore(internal): add property-based tests for partial JSON parsing (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/776">#776</a>)</li>
<li><a
href="e40ce277c2"><code>e40ce27</code></a>
test(ecosystem): check that unused imports tree-shake to nothing</li>
<li><a
href="1ccdad0d1b"><code>1ccdad0</code></a>
fix(tools): addTools() takes effect straight away (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/773">#773</a>)</li>
<li><a
href="8afc3364dc"><code>8afc336</code></a>
fix(tools): leave reply-only params off the tool runner's compaction
request ...</li>
<li><a
href="eff27483d2"><code>eff2748</code></a>
feat(api): add support for claude-opus-5-5, inline tool definitions and
MCP t...</li>
<li><a
href="06e5f9b5d1"><code>06e5f9b</code></a>
codegen metadata</li>
<li><a
href="0c1cc0fdf8"><code>0c1cc0f</code></a>
fix(api): share one evaluated_permission enum across Managed Agents
events</li>
<li><a
href="c0b8746679"><code>c0b8746</code></a>
chore(docs): update diagnostics field descriptions on beta messages</li>
<li>Additional commits viewable in <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.127.0...sdk-v0.128.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `prettier` from 3.9.8 to 3.9.9
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/prettier/prettier/releases">prettier's
releases</a>.</em></p>
<blockquote>
<h2>3.9.9</h2>
<ul>
<li>Markdown: Fix text with <code>$</code> been incorrectly parsed as
math syntax (<a
href="https://redirect.github.com/prettier/prettier/pull/20140">#20140</a>
by <a href="https://github.com/fisker"><code>@​fisker</code></a>)</li>
</ul>
<p>🔗 <a
href="https://github.com/prettier/prettier/blob/3.9.9/CHANGELOG.md#399">Changelog</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/prettier/prettier/blob/main/CHANGELOG.md">prettier's
changelog</a>.</em></p>
<blockquote>
<h1>3.9.9</h1>
<p><a
href="https://github.com/prettier/prettier/compare/3.9.8...3.9.9">diff</a></p>
<h4>Markdown: Fix text with <code>$</code> been incorrectly parsed as
math syntax (<a
href="https://redirect.github.com/prettier/prettier/pull/20140">#20140</a>
by <a href="https://github.com/fisker"><code>@​fisker</code></a>)</h4>
<!-- raw HTML omitted -->
<pre lang="md"><code>&lt;!-- Input --&gt;
**Uses $FOO** from `a.sh` and `b.sh`, plus `$BAR` from `c.sh`, before
anything else runs here.
<p>&lt;!-- Prettier 3.9.8 --&gt;<br />
<strong>Uses $FOO</strong> from <code>a.sh</code> and <code>b.sh</code>,
plus <code>$BAR</code>from<code>c.sh</code>, before anything else runs
here.</p>
<p>&lt;!-- Prettier 3.9.9 --&gt;<br />
<strong>Uses $FOO</strong> from <code>a.sh</code> and <code>b.sh</code>,
plus <code>$BAR</code> from <code>c.sh</code>, before anything else runs
here.<br />
</code></pre></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="cdd17f2288"><code>cdd17f2</code></a>
Release 3.9.9</li>
<li><a
href="dc7b8968e6"><code>dc7b896</code></a>
Disable <code>singleDollarTextMath</code> in
<code>mdast-util-math</code> (<a
href="https://redirect.github.com/prettier/prettier/issues/20140">#20140</a>)</li>
<li><a
href="f9be58fb8e"><code>f9be58f</code></a>
Git blame ignore 3.9.8</li>
<li><a
href="88470cb79c"><code>88470cb</code></a>
Bump Prettier dependency to 3.9.8</li>
<li><a
href="9559cab39e"><code>9559cab</code></a>
Clean changelog_unreleased</li>
<li><a
href="4fc8ee8746"><code>4fc8ee8</code></a>
Update dependents count</li>
<li>See full diff in <a
href="https://github.com/prettier/prettier/compare/3.9.8...3.9.9">compare
view</a></li>
</ul>
</details>
<br />

Updates `vitest` from 5.0.1 to 5.0.2
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/vitest-dev/vitest/releases">vitest's
releases</a>.</em></p>
<blockquote>
<h2>v5.0.2</h2>
<h3>   🐞 Bug Fixes</h3>
<ul>
<li>Bind <code>process</code> in case global is overwritten  -  by <a
href="https://github.com/AriPerkkio"><code>@​AriPerkkio</code></a> in <a
href="https://redirect.github.com/vitest-dev/vitest/issues/11343">vitest-dev/vitest#11343</a>
<a href="https://github.com/vitest-dev/vitest/commit/0b79231ad"><!-- raw
HTML omitted -->(0b792)<!-- raw HTML omitted --></a></li>
<li><strong>detect-async-leaks</strong>:
<ul>
<li>Ignore <code>process.stdio</code> handles  -  by <a
href="https://github.com/AriPerkkio"><code>@​AriPerkkio</code></a> in <a
href="https://redirect.github.com/vitest-dev/vitest/issues/11333">vitest-dev/vitest#11333</a>
<a href="https://github.com/vitest-dev/vitest/commit/0fd6b9790"><!-- raw
HTML omitted -->(0fd6b)<!-- raw HTML omitted --></a></li>
</ul>
</li>
<li><strong>expect</strong>:
<ul>
<li>Fix <code>toMatchObject</code> with asymmetric matchers  -  by <a
href="https://github.com/ShreeBohara"><code>@​ShreeBohara</code></a>,
<strong>Claude Opus 5</strong>, <a
href="https://github.com/hi-ogawa"><code>@​hi-ogawa</code></a>,
<strong>Hiroshi Ogawa</strong> and <strong>Codex (GPT-5)</strong> in <a
href="https://redirect.github.com/vitest-dev/vitest/issues/11100">vitest-dev/vitest#11100</a>
<a href="https://github.com/vitest-dev/vitest/commit/42523289e"><!-- raw
HTML omitted -->(42523)<!-- raw HTML omitted --></a></li>
</ul>
</li>
<li><strong>jsdom</strong>:
<ul>
<li>Fix <code>Request</code> with <code>Blob</code> body on jsdom 28+
 -  by <a
href="https://github.com/harshit-d3v"><code>@​harshit-d3v</code></a> in
<a
href="https://redirect.github.com/vitest-dev/vitest/issues/11295">vitest-dev/vitest#11295</a>
<a href="https://github.com/vitest-dev/vitest/commit/d1c3ecc93"><!-- raw
HTML omitted -->(d1c3e)<!-- raw HTML omitted --></a></li>
</ul>
</li>
<li><strong>reporter</strong>:
<ul>
<li><code>agent</code> to respect <code>--silent</code>  -  by <a
href="https://github.com/Raj4478"><code>@​Raj4478</code></a> and <a
href="https://github.com/AriPerkkio"><code>@​AriPerkkio</code></a> in <a
href="https://redirect.github.com/vitest-dev/vitest/issues/11271">vitest-dev/vitest#11271</a>
<a href="https://github.com/vitest-dev/vitest/commit/5b95efb6d"><!-- raw
HTML omitted -->(5b95e)<!-- raw HTML omitted --></a></li>
</ul>
</li>
<li><strong>reporters</strong>:
<ul>
<li>Handle concurrent <code>createReport</code> calls  -  by <a
href="https://github.com/7rulnik"><code>@​7rulnik</code></a> in <a
href="https://redirect.github.com/vitest-dev/vitest/issues/11278">vitest-dev/vitest#11278</a>
<a href="https://github.com/vitest-dev/vitest/commit/e8e556ff7"><!-- raw
HTML omitted -->(e8e55)<!-- raw HTML omitted --></a></li>
<li><code>hanging-process</code> to use ESM entrypoint  -  by <a
href="https://github.com/AriPerkkio"><code>@​AriPerkkio</code></a> in <a
href="https://redirect.github.com/vitest-dev/vitest/issues/11316">vitest-dev/vitest#11316</a>
<a href="https://github.com/vitest-dev/vitest/commit/4e91e5668"><!-- raw
HTML omitted -->(4e91e)<!-- raw HTML omitted --></a></li>
</ul>
</li>
<li><strong>spy</strong>:
<ul>
<li>Fix stack overflow when spying <code>Set.prototype.add</code>  -  by
<a href="https://github.com/fengmk2"><code>@​fengmk2</code></a> in <a
href="https://redirect.github.com/vitest-dev/vitest/issues/11299">vitest-dev/vitest#11299</a>
<a href="https://github.com/vitest-dev/vitest/commit/a0a939653"><!-- raw
HTML omitted -->(a0a93)<!-- raw HTML omitted --></a></li>
</ul>
</li>
<li><strong>ui</strong>:
<ul>
<li>Persist authentication cookie beyond current browser session  -  by
<a href="https://github.com/hi-ogawa"><code>@​hi-ogawa</code></a>,
<strong>Hiroshi Ogawa</strong> and <strong>OpenCode
(gpt-5.6-sol)</strong> in <a
href="https://redirect.github.com/vitest-dev/vitest/issues/11066">vitest-dev/vitest#11066</a>
<a href="https://github.com/vitest-dev/vitest/commit/f88195e16"><!-- raw
HTML omitted -->(f8819)<!-- raw HTML omitted --></a></li>
</ul>
</li>
</ul>
<h5>    <a
href="https://github.com/vitest-dev/vitest/compare/v5.0.1...v5.0.2">View
changes on GitHub</a></h5>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="428e2e5043"><code>428e2e5</code></a>
chore: release v5.0.2 (<a
href="https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest/issues/11357">#11357</a>)</li>
<li><a
href="0b79231ad7"><code>0b79231</code></a>
fix: bind <code>process</code> in case global is overwritten (<a
href="https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest/issues/11343">#11343</a>)</li>
<li><a
href="597df569ab"><code>597df56</code></a>
docs: correct stale config defaults (<a
href="https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest/issues/11354">#11354</a>)</li>
<li><a
href="ea1c44fb58"><code>ea1c44f</code></a>
docs: <code>sequence.setupFiles</code> default is <code>'list'</code>
(<a
href="https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest/issues/11351">#11351</a>)</li>
<li><a
href="0fd6b97902"><code>0fd6b97</code></a>
fix(detect-async-leaks): ignore <code>process.stdio</code> handles (<a
href="https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest/issues/11333">#11333</a>)</li>
<li><a
href="4e91e56687"><code>4e91e56</code></a>
fix(reporters): <code>hanging-process</code> to use ESM entrypoint (<a
href="https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest/issues/11316">#11316</a>)</li>
<li><a
href="1a57929a49"><code>1a57929</code></a>
docs(browser): fix locators.exact default (<a
href="https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest/issues/11338">#11338</a>)</li>
<li><a
href="5b95efb6df"><code>5b95efb</code></a>
fix(reporter): <code>agent</code> to respect <code>--silent</code> (<a
href="https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest/issues/11271">#11271</a>)</li>
<li><a
href="f58a209779"><code>f58a209</code></a>
docs: correct benchmark.exclude and watch defaults (<a
href="https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest/issues/11268">#11268</a>)</li>
<li><a
href="d1c3ecc932"><code>d1c3ecc</code></a>
fix(jsdom): fix <code>Request</code> with <code>Blob</code> body on
jsdom 28+ (<a
href="https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest/issues/11295">#11295</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/vitest-dev/vitest/commits/v5.0.2/packages/vitest">compare
view</a></li>
</ul>
</details>
<br />

Updates `@anthropic-ai/claude-agent-sdk-darwin-arm64` from 0.3.280 to
0.3.282

Updates `@anthropic-ai/claude-agent-sdk-darwin-x64` from 0.3.280 to
0.3.282

Updates `@anthropic-ai/claude-agent-sdk-linux-arm64` from 0.3.280 to
0.3.282

Updates `@anthropic-ai/claude-agent-sdk-linux-arm64-musl` from 0.3.280
to 0.3.282

Updates `@anthropic-ai/claude-agent-sdk-linux-x64` from 0.3.280 to
0.3.282

Updates `@anthropic-ai/claude-agent-sdk-linux-x64-musl` from 0.3.280 to
0.3.282

Updates `@anthropic-ai/claude-agent-sdk-win32-arm64` from 0.3.280 to
0.3.282

Updates `@anthropic-ai/claude-agent-sdk-win32-x64` from 0.3.280 to
0.3.282

Updates `@modelcontextprotocol/sdk` from 1.30.0 to 1.30.1
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/modelcontextprotocol/typescript-sdk/releases">@​modelcontextprotocol/sdk's
releases</a>.</em></p>
<blockquote>
<h2>1.30.1</h2>
<h2>What's Changed</h2>
<ul>
<li>[v1.x] fix(server): read HTTP request bodies with a size limit and
bound JSON-RPC batch length by <a
href="https://github.com/maxisbey"><code>@​maxisbey</code></a> in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/2717">modelcontextprotocol/typescript-sdk#2717</a></li>
<li>fix(auth): preserve resource URI without trailing slash (<a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/issues/1968">#1968</a>)
by <a
href="https://github.com/MukundaKatta"><code>@​MukundaKatta</code></a>
in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/1972">modelcontextprotocol/typescript-sdk#1972</a></li>
<li>chore: bump version to 1.30.1 by <a
href="https://github.com/claude"><code>@​claude</code></a>[bot] in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/2848">modelcontextprotocol/typescript-sdk#2848</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/MukundaKatta"><code>@​MukundaKatta</code></a>
made their first contribution in <a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/pull/1972">modelcontextprotocol/typescript-sdk#1972</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/modelcontextprotocol/typescript-sdk/compare/1.30.0...1.30.1">https://github.com/modelcontextprotocol/typescript-sdk/compare/1.30.0...1.30.1</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="289ac2c3af"><code>289ac2c</code></a>
chore: bump version to 1.30.1 (<a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/issues/2848">#2848</a>)</li>
<li><a
href="12b425678a"><code>12b4256</code></a>
fix(auth): preserve resource URI without trailing slash (<a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/issues/1968">#1968</a>)
(<a
href="https://redirect.github.com/modelcontextprotocol/typescript-sdk/issues/1972">#1972</a>)</li>
<li><a
href="a9f6eb709b"><code>a9f6eb7</code></a>
[v1.x] fix(server): read HTTP request bodies with a size limit and bound
JSON...</li>
<li>See full diff in <a
href="https://github.com/modelcontextprotocol/typescript-sdk/compare/1.30.0...1.30.1">compare
view</a></li>
</ul>
</details>
<br />

Updates `@oxc-project/types` from 0.150.0 to 0.151.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/oxc-project/oxc/releases">@​oxc-project/types's
releases</a>.</em></p>
<blockquote>
<h2>oxc crates_v0.151.0</h2>
<h3>🚀 Features</h3>
<ul>
<li>1fac655 coverage: Support new TypeScript fixture layout (<a
href="https://github.com/oxc-project/oxc/tree/HEAD/npm/oxc-types/issues/26226">#26226</a>)
(camc314)</li>
</ul>
<h3>🐛 Bug Fixes</h3>
<ul>
<li>cd7228f minifier: Add missing negation for boolean literal (<a
href="https://github.com/oxc-project/oxc/tree/HEAD/npm/oxc-types/issues/26847">#26847</a>)
(Armano)</li>
<li>32305e2 transformer: Skip enum reverse mappings for syntactic string
initializers (<a
href="https://github.com/oxc-project/oxc/tree/HEAD/npm/oxc-types/issues/26724">#26724</a>)
(Dunqing)</li>
<li>7811f0a parser: Preserve reparsed <code>await</code> tokens (<a
href="https://github.com/oxc-project/oxc/tree/HEAD/npm/oxc-types/issues/26619">#26619</a>)
(camc314)</li>
<li>9220158 transformer: Skip recovered using loop patterns (<a
href="https://github.com/oxc-project/oxc/tree/HEAD/npm/oxc-types/issues/26722">#26722</a>)
(camc314)</li>
<li>3130405 parser: Recover await using object binding patterns (<a
href="https://github.com/oxc-project/oxc/tree/HEAD/npm/oxc-types/issues/26721">#26721</a>)
(camc314)</li>
<li>827fdbd parser: Reject invalid modifiers on import aliases (<a
href="https://github.com/oxc-project/oxc/tree/HEAD/npm/oxc-types/issues/26622">#26622</a>)
(camc314)</li>
</ul>
<h3>⚡ Performance</h3>
<ul>
<li>3f1e3aa minifier: Preallocate stmts vec size to avoid excesive
reallocs (<a
href="https://github.com/oxc-project/oxc/tree/HEAD/npm/oxc-types/issues/26856">#26856</a>)
(Armano)</li>
<li>bd3f69e semantic: Reuse identifier hashes in enum member lookups (<a
href="https://github.com/oxc-project/oxc/tree/HEAD/npm/oxc-types/issues/26773">#26773</a>)
(camc314)</li>
<li>5f72954 minifier: Reduce allocations when processing if statements
(<a
href="https://github.com/oxc-project/oxc/tree/HEAD/npm/oxc-types/issues/26666">#26666</a>)
(Armano)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="bec650f15b"><code>bec650f</code></a>
release(crates): oxc v0.151.0 (<a
href="https://github.com/oxc-project/oxc/tree/HEAD/npm/oxc-types/issues/26875">#26875</a>)</li>
<li>See full diff in <a
href="https://github.com/oxc-project/oxc/commits/crates_v0.151.0/npm/oxc-types">compare
view</a></li>
</ul>
</details>
<br />

Updates `@rolldown/binding-android-arm-eabi` from 1.2.9 to 1.2.11
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/rolldown/rolldown/releases">@​rolldown/binding-android-arm-eabi's
releases</a>.</em></p>
<blockquote>
<h2>v1.2.11</h2>
<h2>[1.2.11] - 2026-09-24</h2>
<h3>🚀 Features</h3>
<ul>
<li>watch: report only file events (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10974">#10974</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
</ul>
<h3>🐛 Bug Fixes</h3>
<ul>
<li>sourcemap: preserve names through <code>collapse_sourcemaps</code>
(<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/9486">#9486</a>)
by <a href="https://github.com/eoin"><code>@​eoin</code></a></li>
<li>watch: map metadata events by kind and missing paths as deleted (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10973">#10973</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
</ul>
<h3>🚜 Refactor</h3>
<ul>
<li>rolldown_fs_watcher: hand the handler file events instead of notify
results (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10980">#10980</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
<li>rolldown_fs_watcher: share one event handler adapter between the
notify watchers (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10979">#10979</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
<li>rolldown_fs_watcher: register every path with one watch mode (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10978">#10978</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
<li>rolldown_fs_watcher: tweak the backend traits and the event mapping
(<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10977">#10977</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
<li>watch: translate notify events inside FsWatcher (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10972">#10972</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
</ul>
<h3>📚 Documentation</h3>
<ul>
<li>watch: update the fs watcher layout and the dev coordinator message
(<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10981">#10981</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
<li>watch: document the event translation in FsWatcher (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10975">#10975</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
</ul>
<h3>◀️ Revert</h3>
<ul>
<li>&quot;perf(dev/lazy): fetch a lazy route in one request&quot; (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10789">#10789</a>)
(<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10982">#10982</a>)
by <a href="https://github.com/h-a-n-a"><code>@​h-a-n-a</code></a></li>
</ul>
<h3>❤️ New Contributors</h3>
<ul>
<li><a href="https://github.com/eoin"><code>@​eoin</code></a> made their
first contribution in <a
href="https://redirect.github.com/rolldown/rolldown/pull/9486">#9486</a></li>
</ul>
<p>Co-authored-by: shulaoda <a
href="mailto:165626830+shulaoda@users.noreply.github.com">165626830+shulaoda@users.noreply.github.com</a></p>
<h2>v1.2.10</h2>
<h2>[1.2.10] - 2026-09-23</h2>
<h3>🚀 Features</h3>
<ul>
<li>rolldown_plugin_esm_external_require: prefer 'module.exports' over a
namespace spread (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10901">#10901</a>)
by <a href="https://github.com/isker"><code>@​isker</code></a></li>
<li>watch: support directories in addWatchFile (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10944">#10944</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
<li>label code-splitting timing rows (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10896">#10896</a>)
by <a
href="https://github.com/IWANABETHATGUY"><code>@​IWANABETHATGUY</code></a></li>
<li>separate code-splitting group timing rows (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10892">#10892</a>)
by <a
href="https://github.com/IWANABETHATGUY"><code>@​IWANABETHATGUY</code></a></li>
<li>clarify callback owners in the timing report (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10890">#10890</a>)
by <a
href="https://github.com/IWANABETHATGUY"><code>@​IWANABETHATGUY</code></a></li>
<li>add checks.bundlerTimings with deprecated pluginTimings alias (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10883">#10883</a>)
by <a
href="https://github.com/IWANABETHATGUY"><code>@​IWANABETHATGUY</code></a></li>
</ul>
<h3>🐛 Bug Fixes</h3>
<ul>
<li>code-splitting: initialize leaves behind a re-exported namespace
import (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10694">#10694</a>)
by <a href="https://github.com/hyfdev"><code>@​hyfdev</code></a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/rolldown/rolldown/blob/main/CHANGELOG.md">@​rolldown/binding-android-arm-eabi's
changelog</a>.</em></p>
<blockquote>
<h2>[1.2.11] - 2026-09-24</h2>
<h3>🚀 Features</h3>
<ul>
<li>watch: report only file events (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10974">#10974</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
</ul>
<h3>🐛 Bug Fixes</h3>
<ul>
<li>sourcemap: preserve names through <code>collapse_sourcemaps</code>
(<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/9486">#9486</a>)
by <a href="https://github.com/eoin"><code>@​eoin</code></a></li>
<li>watch: map metadata events by kind and missing paths as deleted (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10973">#10973</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
</ul>
<h3>🚜 Refactor</h3>
<ul>
<li>rolldown_fs_watcher: hand the handler file events instead of notify
results (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10980">#10980</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
<li>rolldown_fs_watcher: share one event handler adapter between the
notify watchers (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10979">#10979</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
<li>rolldown_fs_watcher: register every path with one watch mode (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10978">#10978</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
<li>rolldown_fs_watcher: tweak the backend traits and the event mapping
(<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10977">#10977</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
<li>watch: translate notify events inside FsWatcher (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10972">#10972</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
</ul>
<h3>📚 Documentation</h3>
<ul>
<li>watch: update the fs watcher layout and the dev coordinator message
(<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10981">#10981</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
<li>watch: document the event translation in FsWatcher (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10975">#10975</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
</ul>
<h3>◀️ Revert</h3>
<ul>
<li>&quot;perf(dev/lazy): fetch a lazy route in one request&quot; (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10789">#10789</a>)
(<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10982">#10982</a>)
by <a href="https://github.com/h-a-n-a"><code>@​h-a-n-a</code></a></li>
</ul>
<h3>❤️ New Contributors</h3>
<ul>
<li><a href="https://github.com/eoin"><code>@​eoin</code></a> made their
first contribution in <a
href="https://redirect.github.com/rolldown/rolldown/pull/9486">#9486</a></li>
</ul>
<h2>[1.2.10] - 2026-09-23</h2>
<h3>🚀 Features</h3>
<ul>
<li>rolldown_plugin_esm_external_require: prefer 'module.exports' over a
namespace spread (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10901">#10901</a>)
by <a href="https://github.com/isker"><code>@​isker</code></a></li>
<li>watch: support directories in addWatchFile (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10944">#10944</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
<li>label code-splitting timing rows (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10896">#10896</a>)
by <a
href="https://github.com/IWANABETHATGUY"><code>@​IWANABETHATGUY</code></a></li>
<li>separate code-splitting group timing rows (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10892">#10892</a>)
by <a
href="https://github.com/IWANABETHATGUY"><code>@​IWANABETHATGUY</code></a></li>
<li>clarify callback owners in the timing report (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10890">#10890</a>)
by <a
href="https://github.com/IWANABETHATGUY"><code>@​IWANABETHATGUY</code></a></li>
<li>add checks.bundlerTimings with deprecated pluginTimings alias (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10883">#10883</a>)
by <a
href="https://github.com/IWANABETHATGUY"><code>@​IWANABETHATGUY</code></a></li>
</ul>
<h3>🐛 Bug Fixes</h3>
<ul>
<li>code-splitting: initialize leaves behind a re-exported namespace
import (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10694">#10694</a>)
by <a href="https://github.com/hyfdev"><code>@​hyfdev</code></a></li>
<li>node: record the WASI target on the WebContainer binding fallback
(<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10952">#10952</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
<li>dev: force incremental build on in DevEngine::new (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10958">#10958</a>)
by <a
href="https://github.com/o-alexandrov"><code>@​o-alexandrov</code></a></li>
<li>tree-shaking: propagate side effects through cycles (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10893">#10893</a>)
by <a
href="https://github.com/IWANABETHATGUY"><code>@​IWANABETHATGUY</code></a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="8df4219851"><code>8df4219</code></a>
release: v1.2.11 (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10987">#10987</a>)</li>
<li><a
href="6f85455383"><code>6f85455</code></a>
revert: &quot;perf(dev/lazy): fetch a lazy route in one request&quot;
(<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10789">#10789</a>)
(<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10982">#10982</a>)</li>
<li><a
href="78699253af"><code>7869925</code></a>
feat(watch): report only file events (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10974">#10974</a>)</li>
<li><a
href="3b8e06e99b"><code>3b8e06e</code></a>
release: v1.2.10 (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10969">#10969</a>)</li>
<li><a
href="ec5bab8832"><code>ec5bab8</code></a>
feat(rolldown_plugin_esm_external_require): prefer 'module.exports' over
a na...</li>
<li><a
href="bb1db15db2"><code>bb1db15</code></a>
chore(deps): update napi (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10959">#10959</a>)</li>
<li><a
href="374639ef58"><code>374639e</code></a>
fix(node): record the WASI target on the WebContainer binding fallback
(<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10952">#10952</a>)</li>
<li><a
href="cfc9d19df0"><code>cfc9d19</code></a>
refactor(watch): let FsWatcher own the watched paths (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10948">#10948</a>)</li>
<li><a
href="1551cf8a09"><code>1551cf8</code></a>
feat(watch): support directories in addWatchFile (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10944">#10944</a>)</li>
<li><a
href="b12f077c66"><code>b12f077</code></a>
test: handle Windows paths in sourcemap callback tests (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10942">#10942</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/rolldown/rolldown/commits/v1.2.11/packages/rolldown">compare
view</a></li>
</ul>
</details>
<br />

Updates `@rolldown/binding-android-arm64` from 1.2.9 to 1.2.11
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/rolldown/rolldown/releases">@​rolldown/binding-android-arm64's
releases</a>.</em></p>
<blockquote>
<h2>v1.2.11</h2>
<h2>[1.2.11] - 2026-09-24</h2>
<h3>🚀 Features</h3>
<ul>
<li>watch: report only file events (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10974">#10974</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
</ul>
<h3>🐛 Bug Fixes</h3>
<ul>
<li>sourcemap: preserve names through <code>collapse_sourcemaps</code>
(<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/9486">#9486</a>)
by <a href="https://github.com/eoin"><code>@​eoin</code></a></li>
<li>watch: map metadata events by kind and missing paths as deleted (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10973">#10973</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
</ul>
<h3>🚜 Refactor</h3>
<ul>
<li>rolldown_fs_watcher: hand the handler file events instead of notify
results (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10980">#10980</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
<li>rolldown_fs_watcher: share one event handler adapter between the
notify watchers (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10979">#10979</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
<li>rolldown_fs_watcher: register every path with one watch mode (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10978">#10978</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
<li>rolldown_fs_watcher: tweak the backend traits and the event mapping
(<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10977">#10977</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
<li>watch: translate notify events inside FsWatcher (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10972">#10972</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
</ul>
<h3>📚 Documentation</h3>
<ul>
<li>watch: update the fs watcher layout and the dev coordinator message
(<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10981">#10981</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
<li>watch: document the event translation in FsWatcher (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10975">#10975</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
</ul>
<h3>◀️ Revert</h3>
<ul>
<li>&quot;perf(dev/lazy): fetch a lazy route in one request&quot; (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10789">#10789</a>)
(<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10982">#10982</a>)
by <a href="https://github.com/h-a-n-a"><code>@​h-a-n-a</code></a></li>
</ul>
<h3>❤️ New Contributors</h3>
<ul>
<li><a href="https://github.com/eoin"><code>@​eoin</code></a> made their
first contribution in <a
href="https://redirect.github.com/rolldown/rolldown/pull/9486">#9486</a></li>
</ul>
<p>Co-authored-by: shulaoda <a
href="mailto:165626830+shulaoda@users.noreply.github.com">165626830+shulaoda@users.noreply.github.com</a></p>
<h2>v1.2.10</h2>
<h2>[1.2.10] - 2026-09-23</h2>
<h3>🚀 Features</h3>
<ul>
<li>rolldown_plugin_esm_external_require: prefer 'module.exports' over a
namespace spread (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10901">#10901</a>)
by <a href="https://github.com/isker"><code>@​isker</code></a></li>
<li>watch: support directories in addWatchFile (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10944">#10944</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
<li>label code-splitting timing rows (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10896">#10896</a>)
by <a
href="https://github.com/IWANABETHATGUY"><code>@​IWANABETHATGUY</code></a></li>
<li>separate code-splitting group timing rows (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10892">#10892</a>)
by <a
href="https://github.com/IWANABETHATGUY"><code>@​IWANABETHATGUY</code></a></li>
<li>clarify callback owners in the timing report (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10890">#10890</a>)
by <a
href="https://github.com/IWANABETHATGUY"><code>@​IWANABETHATGUY</code></a></li>
<li>add checks.bundlerTimings with deprecated pluginTimings alias (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10883">#10883</a>)
by <a
href="https://github.com/IWANABETHATGUY"><code>@​IWANABETHATGUY</code></a></li>
</ul>
<h3>🐛 Bug Fixes</h3>
<ul>
<li>code-splitting: initialize leaves behind a re-exported namespace
import (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10694">#10694</a>)
by <a href="https://github.com/hyfdev"><code>@​hyfdev</code></a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/rolldown/rolldown/blob/main/CHANGELOG.md">@​rolldown/binding-android-arm64's
changelog</a>.</em></p>
<blockquote>
<h2>[1.2.11] - 2026-09-24</h2>
<h3>🚀 Features</h3>
<ul>
<li>watch: report only file events (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10974">#10974</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
</ul>
<h3>🐛 Bug Fixes</h3>
<ul>
<li>sourcemap: preserve names through <code>collapse_sourcemaps</code>
(<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/9486">#9486</a>)
by <a href="https://github.com/eoin"><code>@​eoin</code></a></li>
<li>watch: map metadata events by kind and missing paths as deleted (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10973">#10973</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
</ul>
<h3>🚜 Refactor</h3>
<ul>
<li>rolldown_fs_watcher: hand the handler file events instead of notify
results (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10980">#10980</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
<li>rolldown_fs_watcher: share one event handler adapter between the
notify watchers (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10979">#10979</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
<li>rolldown_fs_watcher: register every path with one watch mode (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10978">#10978</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
<li>rolldown_fs_watcher: tweak the backend traits and the event mapping
(<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10977">#10977</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
<li>watch: translate notify events inside FsWatcher (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10972">#10972</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
</ul>
<h3>📚 Documentation</h3>
<ul>
<li>watch: update the fs watcher layout and the dev coordinator message
(<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10981">#10981</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
<li>watch: document the event translation in FsWatcher (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10975">#10975</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
</ul>
<h3>◀️ Revert</h3>
<ul>
<li>&quot;perf(dev/lazy): fetch a lazy route in one request&quot; (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10789">#10789</a>)
(<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10982">#10982</a>)
by <a href="https://github.com/h-a-n-a"><code>@​h-a-n-a</code></a></li>
</ul>
<h3>❤️ New Contributors</h3>
<ul>
<li><a href="https://github.com/eoin"><code>@​eoin</code></a> made their
first contribution in <a
href="https://redirect.github.com/rolldown/rolldown/pull/9486">#9486</a></li>
</ul>
<h2>[1.2.10] - 2026-09-23</h2>
<h3>🚀 Features</h3>
<ul>
<li>rolldown_plugin_esm_external_require: prefer 'module.exports' over a
namespace spread (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10901">#10901</a>)
by <a href="https://github.com/isker"><code>@​isker</code></a></li>
<li>watch: support directories in addWatchFile (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10944">#10944</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
<li>label code-splitting timing rows (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10896">#10896</a>)
by <a
href="https://github.com/IWANABETHATGUY"><code>@​IWANABETHATGUY</code></a></li>
<li>separate code-splitting group timing rows (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10892">#10892</a>)
by <a
href="https://github.com/IWANABETHATGUY"><code>@​IWANABETHATGUY</code></a></li>
<li>clarify callback owners in the timing report (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10890">#10890</a>)
by <a
href="https://github.com/IWANABETHATGUY"><code>@​IWANABETHATGUY</code></a></li>
<li>add checks.bundlerTimings with deprecated pluginTimings alias (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10883">#10883</a>)
by <a
href="https://github.com/IWANABETHATGUY"><code>@​IWANABETHATGUY</code></a></li>
</ul>
<h3>🐛 Bug Fixes</h3>
<ul>
<li>code-splitting: initialize leaves behind a re-exported namespace
import (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10694">#10694</a>)
by <a href="https://github.com/hyfdev"><code>@​hyfdev</code></a></li>
<li>node: record the WASI target on the WebContainer binding fallback
(<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10952">#10952</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
<li>dev: force incremental build on in DevEngine::new (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10958">#10958</a>)
by <a
href="https://github.com/o-alexandrov"><code>@​o-alexandrov</code></a></li>
<li>tree-shaking: propagate side effects through cycles (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10893">#10893</a>)
by <a
href="https://github.com/IWANABETHATGUY"><code>@​IWANABETHATGUY</code></a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="8df4219851"><code>8df4219</code></a>
release: v1.2.11 (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10987">#10987</a>)</li>
<li><a
href="6f85455383"><code>6f85455</code></a>
revert: &quot;perf(dev/lazy): fetch a lazy route in one request&quot;
(<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10789">#10789</a>)
(<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10982">#10982</a>)</li>
<li><a
href="78699253af"><code>7869925</code></a>
feat(watch): report only file events (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10974">#10974</a>)</li>
<li><a
href="3b8e06e99b"><code>3b8e06e</code></a>
release: v1.2.10 (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10969">#10969</a>)</li>
<li><a
href="ec5bab8832"><code>ec5bab8</code></a>
feat(rolldown_plugin_esm_external_require): prefer 'module.exports' over
a na...</li>
<li><a
href="bb1db15db2"><code>bb1db15</code></a>
chore(deps): update napi (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10959">#10959</a>)</li>
<li><a
href="374639ef58"><code>374639e</code></a>
fix(node): record the WASI target on the WebContainer binding fallback
(<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10952">#10952</a>)</li>
<li><a
href="cfc9d19df0"><code>cfc9d19</code></a>
refactor(watch): let FsWatcher own the watched paths (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10948">#10948</a>)</li>
<li><a
href="1551cf8a09"><code>1551cf8</code></a>
feat(watch): support directories in addWatchFile (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10944">#10944</a>)</li>
<li><a
href="b12f077c66"><code>b12f077</code></a>
test: handle Windows paths in sourcemap callback tests (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10942">#10942</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/rolldown/rolldown/commits/v1.2.11/packages/rolldown">compare
view</a></li>
</ul>
</details>
<br />

Updates `@rolldown/binding-darwin-arm64` from 1.2.9 to 1.2.11
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/rolldown/rolldown/releases">@​rolldown/binding-darwin-arm64's
releases</a>.</em></p>
<blockquote>
<h2>v1.2.11</h2>
<h2>[1.2.11] - 2026-09-24</h2>
<h3>🚀 Features</h3>
<ul>
<li>watch: report only file events (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/10974">#10974</a>)
by <a
href="https://github.com/shulaoda"><code>@​shulaoda</code></a></li>
</ul>
<h3>🐛 Bug Fixes</h3>
<ul>
<li>sourcemap: preserve names through <code>collapse_sourcemaps</code>
(<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown/issues/9486">#9486</a>)
by <a href="https://github.com/eoin"><code>@​eoin</code></a></li>
<li>watch: map metadata events by kind and missing paths as deleted (<a
href="https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown...

_Description has been truncated_

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
2026-09-28 17:37:12 +02:00
dependabot[bot]
77ceab0339 chore(deps): Bump diff from 8.0.4 to 9.0.0 (#1187)
Bumps [diff](https://github.com/kpdecker/jsdiff) from 8.0.4 to 9.0.0.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/kpdecker/jsdiff/blob/master/release-notes.md">diff's
changelog</a>.</em></p>
<blockquote>
<h2>9.0.0</h2>
<p>(All changes part of PR <a
href="https://redirect.github.com/kpdecker/jsdiff/pull/672">#672</a>.)</p>
<ul>
<li>
<p><strong>ES5 support is dropped</strong>. <code>parsePatch</code> now
uses <code>TextDecoder</code> and <code>Uint8Array</code>, which are not
available in ES5, and TypeScript is now compiled with the
&quot;es6&quot; <code>target</code>. From now on, I intend to freely use
any features that are deemed &quot;Widely available&quot; by <a
href="https://web.dev/baseline">Baseline</a>. Users who need ES5 support
should stick to version 8.</p>
</li>
<li>
<p><strong>C-style quoted strings in filename headers are now properly
supported</strong>.</p>
<p>When the name of either the old or new file in a patch contains
&quot;special characters&quot;, both GNU <code>diff</code> and Git quote
the filename in the patch's headers and escape special characters using
the same escape sequences that are used in string literals in C,
including octal escapes for all non-ASCII characters. Previously, jsdiff
had very little support for this; <code>parsePatch</code> would remove
the quotes, and unescape any escaped backslashes, but would not unescape
other escape sequences. <code>formatPatch</code>, meanwhile, did not
quote or escape special characters at all.</p>
<p>Now, <code>parsePatch</code> parses all the possible escape sequences
that GNU diff (or Git) ever output, and <code>formatPatch</code> quotes
and escapes filenames containing special characters in the same way GNU
diff does.</p>
</li>
<li>
<p><strong><code>formatPatch</code> now omits file headers when
<code>oldFileName</code> or <code>newFileName</code> in the provided
patch object are <code>undefined</code></strong>, regardless of the
<code>headerOptions</code> parameter. (Previously, it would treat the
absence of <code>oldFileName</code> or <code>newFileName</code> as
indicating the filename was the word &quot;undefined&quot; and emit
headers <code>--- undefined</code> / <code>+++ undefined</code>.)</p>
</li>
<li>
<p><strong><code>formatPatch</code> no longer outputs trailing tab
characters at the end of <code>---</code>/<code>+++</code>
headers.</strong></p>
<p>Previously, if <code>formatPatch</code> was passed a patch object to
serialize that had empty strings for the <code>oldHeader</code> or
<code>newHeader</code> property, it would include a trailing tab
character after the filename in the <code>---</code> and/or
<code>+++</code> file header. Now, this scenario is treated the same as
when <code>oldHeader</code>/<code>newHeader</code> is
<code>undefined</code> - i.e. the trailing tab is omitted.</p>
</li>
<li>
<p><strong><code>formatPatch</code> no longer mutates its input</strong>
when serializing a patch containing a hunk where either the old or new
content contained zero lines. (Such a hunk occurs only when the hunk has
no context lines and represents a pure insertion or pure deletion, which
for instance will occur whenever one of the two files being diffed is
completely empty.) Previously <code>formatPatch</code> would provide the
correct output but also mutate the <code>oldLines</code> or
<code>newLines</code> property on the hunk, changing the meaning of the
underlying patch.</p>
</li>
<li>
<p><strong>Git-style patches are now supported by
<code>parsePatch</code>, <code>formatPatch</code>, and
<code>reversePatch</code></strong>.</p>
<p>Patches output by <code>git diff</code> can include some features
that are unlike those output by GNU <code>diff</code>, and therefore not
handled by an ordinary unified diff format parser. An ordinary diff
simply describes the differences between the <em>content</em> of two
files, but Git diffs can also indicate, via &quot;extended
headers&quot;, the creation or deletion of (potentially empty) files,
indicate that a file was renamed, and contain information about file
mode changes. Furthermore, when these changes appear in a diff in the
absence of a content change (e.g. when an empty file is created, or a
file is renamed without content changes), the patch will contain no
associated <code>---</code>/<code>+++</code> file headers nor any
hunks.</p>
<p>jsdiff previously did not support parsing Git's extended headers, nor
hunkless patches. Now <code>parsePatch</code> parses some of the
extended headers, parses hunkless Git patches, and can determine
filenames (e.g. from the extended headers) when parsing a patch that
includes no <code>---</code> or <code>+++</code> file headers. The
additional information conveyed by the extended headers we support is
recorded on new fields on the result object returned by
<code>parsePatch</code>. See <code>isGit</code> and subsequent
properties in the docs in the README.md file.</p>
<p><code>formatPatch</code> now outputs extended headers based on these
new Git-specific properties, and <code>reversePatch</code> respects them
as far as possible (with one unavoidable caveat noted in the README.md
file).</p>
</li>
<li>
<p><strong>Unpaired file headers now cause <code>parsePatch</code> to
throw</strong>.</p>
<p>It remains acceptable to have a patch with no file headers whatsoever
(e.g. one that begins with a <code>@@</code> hunk header on the very
first line), but a patch with <em>only</em> a <code>---</code> header or
only a <code>+++</code> header is now considered an error.</p>
</li>
<li>
<p><strong><code>parsePatch</code> is now more tolerant of
&quot;trailing garbage&quot;</strong></p>
<p>That is: after a patch, or between files/indexes in a patch, it is
now acceptable to have arbitrary lines of &quot;garbage&quot; (so long
as they unambiguously have no syntactic meaning - e.g. trailing garbage
that leads with a <code>+</code>, <code>-</code>, or <code> </code> and
thus is interpretable as part of a hunk still triggers a throw).</p>
<p>This means we no longer reject patches output by tools that include
extra data in &quot;garbage&quot; lines not understood by generic
unified diff parsers. (For example, SVN patches can include
&quot;Property changes on:&quot; lines that generic unified diff parsers
should discard as garbage; jsdiff previously threw errors when
encountering them.)</p>
<p>This change brings jsdiff's behaviour more in line with GNU
<code>patch</code>, which is highly permissive of
&quot;garbage&quot;.</p>
</li>
<li>
<p><strong>The <code>oldFileName</code> and <code>newFileName</code>
fields of <code>StructuredPatch</code> are now typed as <code>string |
undefined</code> instead of <code>string</code></strong>. This type
change reflects the (pre-existing) reality that <code>parsePatch</code>
can produce patches without filenames (e.g. when parsing a patch that
simply contains hunks with no file headers).</p>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="ed13aca03a"><code>ed13aca</code></a>
Update version in package.json and in release notes (<a
href="https://redirect.github.com/kpdecker/jsdiff/issues/683">#683</a>)</li>
<li><a
href="7a49317b50"><code>7a49317</code></a>
Bump dependencies again (<a
href="https://redirect.github.com/kpdecker/jsdiff/issues/682">#682</a>)</li>
<li><a
href="afe5aecad1"><code>afe5aec</code></a>
Add Git support, and otherwise variously improve &amp; fix parsePatch
(and other ...</li>
<li><a
href="2e46779d88"><code>2e46779</code></a>
Fix a typo (<a
href="https://redirect.github.com/kpdecker/jsdiff/issues/679">#679</a>)</li>
<li>See full diff in <a
href="https://github.com/kpdecker/jsdiff/compare/8.0.4...v9.0.0">compare
view</a></li>
</ul>
</details>
<br />


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

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

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

---

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

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


</details>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 16:56:18 +02:00
20 changed files with 973 additions and 590 deletions

View File

@@ -1,3 +1,3 @@
{
".": "0.82.0"
".": "0.85.0"
}

View File

@@ -1,5 +1,32 @@
# Changelog
## [0.85.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.84.0...v0.85.0) (2026-10-01)
### Features
* **deps:** Bump @anthropic-ai/claude-agent-sdk to 0.3.286 ([#1211](https://github.com/agentclientprotocol/claude-agent-acp/issues/1211)) ([f53b0d7](https://github.com/agentclientprotocol/claude-agent-acp/commit/f53b0d7f40bcd0e9bddace85f123ff4851afcb1f))
### Bug Fixes
* Adapt to SDK 0.3.284 replay and ultracode changes ([#1208](https://github.com/agentclientprotocol/claude-agent-acp/issues/1208)) ([4af8079](https://github.com/agentclientprotocol/claude-agent-acp/commit/4af807968c7f0813bcb20fe2bcab2370320e9074))
* send the legacy custom answer key to AIR ([#1210](https://github.com/agentclientprotocol/claude-agent-acp/issues/1210)) ([af0b43d](https://github.com/agentclientprotocol/claude-agent-acp/commit/af0b43de944e36f81343f40b7aa1a96cd48c3218))
## [0.84.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.83.0...v0.84.0) (2026-09-28)
### Features
* **deps:** update Claude SDK ([#1189](https://github.com/agentclientprotocol/claude-agent-acp/issues/1189)) ([d5e6f33](https://github.com/agentclientprotocol/claude-agent-acp/commit/d5e6f33046614fca4798d49f0c77edba951a6771))
## [0.83.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.82.0...v0.83.0) (2026-09-28)
### Features
* **deps:** Bump @anthropic-ai/claude-agent-sdk to 0.3.283 ([#1186](https://github.com/agentclientprotocol/claude-agent-acp/issues/1186)) ([49858b5](https://github.com/agentclientprotocol/claude-agent-acp/commit/49858b527397a08d343fe2c148a5fe9e3eb84547))
## [0.82.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.81.2...v0.82.0) (2026-09-28)

View File

@@ -1204,6 +1204,10 @@ For AIR, the companion field carries `_meta.jetbrains.air.customAnswer`:
`questionId` names the select field of the same question.
A client can render the companion as the free-text choice of that question.
For AIR, the companion field also carries the same value under the root key `_meta._askUserQuestionCustomAnswer`.
Released AIR versions read only that key.
A client that is not AIR gets neither key.
## Session fork point
AIR can fork a session at one agent message.
@@ -1255,12 +1259,11 @@ The adapter sends the key only to an AIR client.
These keys moved into the AIR namespace. The adapter sends the new key only to AIR, and the old key to no client.
| Old key | New key |
| --------------------------------------------------------- | ---------------------------------------------- |
| `initialize._meta.goal`, `session_info_update._meta.goal` | `_meta.jetbrains.air.goal`, same shape |
| mode `_meta.kind`, config option value `_meta.kind` | `_meta.jetbrains.air.kind` |
| elicitation property `_meta._askUserQuestionCustomAnswer` | `_meta.jetbrains.air.customAnswer`, same value |
| tool call `_meta.contextCompaction` | `_meta.jetbrains.air.contextCompaction` |
| Old key | New key |
| --------------------------------------------------------- | --------------------------------------- |
| `initialize._meta.goal`, `session_info_update._meta.goal` | `_meta.jetbrains.air.goal`, same shape |
| mode `_meta.kind`, config option value `_meta.kind` | `_meta.jetbrains.air.kind` |
| tool call `_meta.contextCompaction` | `_meta.jetbrains.air.contextCompaction` |
The adapter does not send `_meta.claudeCode.title`, `claudeCode.subagent`, `claudeCode.skill`, or `claudeCode.skillPath` to any client.
AIR reads `_meta.jetbrains.air.commandTitle`, `subagent`, and `skill` instead.

828
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@
"publishConfig": {
"access": "public"
},
"version": "0.82.0",
"version": "0.85.0",
"description": "An ACP-compatible coding agent powered by the Claude Agent SDK (TypeScript)",
"main": "dist/lib.js",
"types": "dist/lib.d.ts",
@@ -65,26 +65,26 @@
"node": ">=22"
},
"dependencies": {
"@agentclientprotocol/sdk": "1.5.0",
"@anthropic-ai/claude-agent-sdk": "0.3.280",
"diff": "^8.0.3",
"@agentclientprotocol/sdk": "1.5.1",
"@anthropic-ai/claude-agent-sdk": "0.3.286",
"diff": "9.0.0",
"zod": "4.6.5"
},
"devDependencies": {
"@anthropic-ai/sdk": "0.127.0",
"@anthropic-ai/sdk": "0.131.0",
"@eslint/js": "10.0.1",
"@tsconfig/node22": "22.0.6",
"@types/node": "26.6.2",
"@typescript-eslint/eslint-plugin": "8.70.1",
"@typescript-eslint/parser": "8.70.1",
"@types/node": "26.6.3",
"@typescript-eslint/eslint-plugin": "8.71.0",
"@typescript-eslint/parser": "8.71.0",
"ajv": "8.20.0",
"ajv-formats": "3.0.1",
"eslint": "10.11.0",
"eslint-config-prettier": "10.1.8",
"globals": "17.12.0",
"prettier": "3.9.8",
"globals": "17.13.0",
"prettier": "3.9.9",
"ts-node": "10.9.2",
"typescript": "6.0.3",
"vitest": "5.0.1"
"vitest": "5.0.3"
}
}

View File

@@ -271,9 +271,9 @@ import {
import {
buildEffortConfigOption,
EFFORT_CONFIG_ID,
effortFlagSettings,
mergeEffortSettings,
settingsEffortForModel,
toSdkEffortLevel,
} from "./session-effort.js";
export { EFFORT_CONFIG_ID, settingsEffortForModel } from "./session-effort.js";
@@ -706,10 +706,14 @@ type Turn = {
deferredSettle?: PromptResponse;
/** Uuids of `steer()`-injected messages the SDK has not replayed back yet.
*
* A steer is normally delivered at priority `now`, so the CLI ABORTS
* the running cycle: it emits its own human-origin `result` —
* indistinguishable from a turn's terminal one — and the steered message runs
* as a SECOND cycle. Settling at that result would answer `session/prompt`
* A steer is normally delivered at priority `now`. When it lands during
* generation the CLI ABORTS the running cycle: it emits its own human-origin
* `result` — indistinguishable from a turn's terminal one — and the steered
* message runs as a SECOND cycle. When it lands during a foreground tool call
* (CLI 2.1.286) the CLI moves the tool to the background instead and either
* joins the steer into the running cycle (ONE result stamped with both
* uuids) or still ends that cycle early and runs the steer as a second one.
* Settling at an interrupted cycle's result would answer `session/prompt`
* mid-work, so a steered turn's results only RECORD their outcome
* (`steeredSettle`) until one provably answers the steer.
*
@@ -833,6 +837,9 @@ export type Session = {
* terminal (e.g. /doctor, /color). ACP clients aren't that terminal, so
* these are filtered out of `available_commands_update` payloads. */
terminalSlashCommands?: string[];
/** Serialized `system`/init `plugin_errors` last logged, so the per-turn
* init re-emit logs a plugin load failure once, not every turn. */
loggedPluginErrors?: string;
/** The long-lived consumer task. Lazily started on the first `prompt()` and
* kept alive for the session so between-turn/background messages are still
* drained and forwarded. */
@@ -928,16 +935,16 @@ export type Session = {
* prompts so mid-stream usage_update notifications report a correct `size`
* before the turn's first result message arrives. Seeded synchronously at
* session creation and on model switches from the per-model cache or the
* text heuristic (DEFAULT_CONTEXT_WINDOW when both miss), then confirmed —
* and the cache populated — by each result's modelUsage. No extra
* `getContextUsage` IPC is on these paths: before the first turn it can add
* tens of seconds to both fresh and resumed sessions (see the seeding call
* sites and `contextWindowCache`). */
* text heuristic (DEFAULT_CONTEXT_WINDOW when both miss), refined by a
* background `getContextUsage` when that seed was a guess (see
* `refreshContextWindowInBackground`), then confirmed — and the cache
* populated — by each result's modelUsage. No awaited IPC is on these
* paths (see the seeding call sites and `contextWindowCache`). */
contextWindowSize: number;
contextUsedTokens?: number;
/** Whether `contextWindowSize` came from an authoritative source (the
* cross-session cache or a `result.modelUsage`) rather than the text
* heuristic / default. Guards the
* cross-session cache, a `result.modelUsage`, or the background
* `getContextUsage`) rather than the text heuristic / default. Guards the
* mid-stream `message_start` heuristic upgrade: an authoritative window that
* happens to equal DEFAULT_CONTEXT_WINDOW must not be mistaken for "unseeded"
* and clobbered by a "1m" text match. */
@@ -1940,6 +1947,41 @@ export function isSyntheticLoginMessage(apiMessage: unknown): boolean {
);
}
/** Origin kinds of the meta user messages `getSessionMessages` returns since
* SDK 0.3.284 (messages from other agents, sessions and channels). */
const REPLAY_HIDDEN_META_ORIGIN_KINDS = new Set([
"peer",
"channel",
"observer",
"observer-activity",
"slack-ping",
]);
/**
* True for a transcript message delivered to the model from another agent,
* session or channel. Since SDK 0.3.284 `getSessionMessages` returns these as
* `is_meta` user messages whose content is the full harness framing (envelope
* XML plus the "not typed by your user" preamble). The live prompt loop never
* renders them, so replay skips them too rather than presenting them as text
* the user typed. Other `is_meta` messages (compact summaries) are kept.
*
* `is_meta` and `origin` are runtime fields not declared on `SessionMessage`.
*/
export function isReplayHiddenMetaMessage(message: unknown): boolean {
if (!message || typeof message !== "object") return false;
const { type, is_meta, origin } = message as {
type?: unknown;
is_meta?: unknown;
origin?: { kind?: unknown } | null;
};
return (
type === "user" &&
is_meta === true &&
typeof origin?.kind === "string" &&
REPLAY_HIDDEN_META_ORIGIN_KINDS.has(origin.kind)
);
}
/**
* Client-facing surface the agent calls back into. This is the subset of ACP
* client methods the agent actually uses, expressed as a narrow interface so
@@ -3376,9 +3418,12 @@ export class ClaudeAcpAgent {
* prompt (IJAI-1191). The steered message's own output streams via
* `session/update`, not this response.
*
* Pre-empting means ABORTING: the interrupted cycle emits a `result` of its
* own and the steered message runs as a second one, so the turn is marked
* (`Turn.steeredEchoes`) to settle only once a result answers the steer.
* Pre-empting generation means ABORTING: the interrupted cycle emits a
* `result` of its own and the steered message runs as a second one. A steer
* during a foreground tool call moves that tool (Bash, Agent, MCP) to the
* background instead, and may join the running cycle (CLI 2.1.286). Either
* way the turn is marked (`Turn.steeredEchoes`) to settle only once a result
* answers the steer.
*
* When the session is idle, the opt-in path returns `promptRequired` WITHOUT
* calling `prompt()`, pushing SDK input, or mutating `turnQueue`: the content
@@ -4591,6 +4636,20 @@ export class ClaudeAcpAgent {
this.logger.error(`Failed to re-advertise slash commands: ${error}`);
}
}
// Plugin load failures (CLI 2.1.283+) have no ACP surface;
// log them so a missing plugin isn't silent.
if (message.plugin_errors?.length) {
const pluginErrors = JSON.stringify(message.plugin_errors);
if (pluginErrors !== session.loggedPluginErrors) {
session.loggedPluginErrors = pluginErrors;
for (const error of message.plugin_errors) {
this.logger.error(
`Plugin ${error.plugin} failed to load (${error.type})` +
`${error.path ? ` from ${error.path}` : ""}: ${error.message}`,
);
}
}
}
break;
case "status": {
if (message.status === "compacting") {
@@ -7275,6 +7334,9 @@ export class ClaudeAcpAgent {
: { ids: new Map<string, string>() };
const replayedSubagents = new Set<string>();
const replayMessage = async (message: SessionMessage): Promise<void> => {
if (isReplayHiddenMetaMessage(message)) {
return;
}
if (
message.type === "user" &&
message.parent_tool_use_id === null &&
@@ -7983,6 +8045,41 @@ export class ClaudeAcpAgent {
});
}
/**
* Replace a heuristic context window with `getContextUsage().rawMaxTokens`
* without blocking the caller. The text heuristic misses natively-1M models
* whose picker rows carry no "1m" token (`sonnet`, and since CLI 2.1.283
* `opus`/`default`), which would otherwise report 200k until the first
* result's modelUsage. Never awaited: SDK control requests are serialized,
* so an awaited call would delay session/new or a model switch (before the
* first turn it took ~15s on older CLIs, issues #886/#880; ~0.5s on 2.1.283).
* Not written to `contextWindowCache` — that stays keyed to the
* `result.modelUsage` spellings — and a result still overwrites it.
*/
private refreshContextWindowInBackground(sessionId: string, session: Session): void {
if (session.contextWindowAuthoritative) return;
const { query } = session;
const modelId = session.models.currentModelId;
const stillCurrent = () =>
this.sessions[sessionId] === session &&
session.query === query &&
session.models.currentModelId === modelId;
// A synchronous throw must not fail the caller either.
Promise.resolve()
.then(() => query.getContextUsage())
.then(
(usage) => {
if (!stillCurrent() || session.contextWindowAuthoritative) return;
if (!(usage.rawMaxTokens > 0)) return;
session.contextWindowSize = usage.rawMaxTokens;
session.contextWindowAuthoritative = true;
},
(error) => {
if (stillCurrent()) this.logger.error("Failed to read the context window:", error);
},
);
}
private async applyConfigOptionValue(
sessionId: string,
session: Session,
@@ -7997,19 +8094,16 @@ export class ClaudeAcpAgent {
// context window for semantic aliases (e.g. `default`) whose ID alone
// carries no "1m" token.
const newModelInfo = session.modelInfos.find((m) => m.value === value);
if (session.models.currentModelId !== value) {
// Seed the new model's context window WITHOUT any IPC on the switch
// path: cached authoritative value if we've already learned it (from a
// prior turn's `result.modelUsage`), else the text heuristic, else the
// default. We deliberately do NOT call `getContextUsage` here — before
// a fresh session's first prompt turn that control request is not
// serviced (~15s stall, issues #886/#880), and (because SDK control
// requests are serialized over one channel) it would drag the awaited
// `setModel` down with it. The authoritative window arrives on the
// first `result.modelUsage` for the model and is cached from there;
// until then a switched-to alias that has never run a turn shows the
// heuristic/default window, which self-corrects on its first response
// (matches pre-0.59.0 behavior).
const modelChanged = session.models.currentModelId !== value;
if (modelChanged) {
// Seed the new model's context window WITHOUT awaited IPC on the
// switch path: cached authoritative value if we've already learned it
// (from a prior turn's `result.modelUsage`), else the text heuristic,
// else the default. SDK control requests are serialized over one
// channel, so an awaited `getContextUsage` here would delay the rest
// of the switch (issues #886/#880); a guessed seed is instead refined
// in the background once the switch is done
// (`refreshContextWindowInBackground`).
const seeded = immediateContextWindow(session.providerCacheKey, value, newModelInfo);
session.contextWindowSize = seeded.size;
session.contextWindowAuthoritative = seeded.authoritative;
@@ -8095,14 +8189,20 @@ export class ClaudeAcpAgent {
const newEffort =
typeof newEffortOpt?.currentValue === "string" ? newEffortOpt.currentValue : undefined;
try {
await session.query.applyFlagSettings({
await session.query.applyFlagSettings(
// A legacy client's unpinned effort is display-only: the CLI
// resolves the persisted value for the new model. When an old
// user pin is no longer supported, clear the flag layer instead
// of replacing it with that displayed value. Opted-in clients
// deliberately apply their concrete displayed effort.
effortLevel: useRecommendedValue ? toSdkEffortLevel(newEffort) : null,
});
effortFlagSettings(
useRecommendedValue ? newEffort : undefined,
mergeEffortSettings(
session.settingsManager.getSettings(),
session.effortSettingsOverride,
),
),
);
session.effortPinnedLevel = effortPinnedForNewModel ? pinnedEffort : undefined;
session.appliedEffortLevel =
useRecommendedValue && newEffort !== "default" ? newEffort : undefined;
@@ -8151,12 +8251,20 @@ export class ClaudeAcpAgent {
if (modeDowngraded) {
await this.sessionModes.publishFallbackState(sessionId, session);
}
// Last, so the switch's own control requests don't queue behind it.
if (modelChanged) this.refreshContextWindowInBackground(sessionId, session);
} else if (configId === EFFORT_CONFIG_ID) {
// Apply first so a rejected control request cannot leave the displayed
// value ahead of the SDK flag layer.
await session.query.applyFlagSettings({
effortLevel: toSdkEffortLevel(value),
});
await session.query.applyFlagSettings(
effortFlagSettings(
value,
mergeEffortSettings(
session.settingsManager.getSettings(),
session.effortSettingsOverride,
),
),
);
session.configOptions = session.configOptions.map((o) =>
o.id === configId && typeof o.currentValue === "string" ? { ...o, currentValue: value } : o,
);
@@ -8873,10 +8981,12 @@ export class ClaudeAcpAgent {
}
// Apply user's `availableModels` allowlist from settings.json before any
// downstream model handling. The SDK only enforces this allowlist in its
// own UI, not in `initializationResult.models`, so we filter here to keep
// configOptions, the current-model resolver, and the stored modelInfos
// consistent with what the user configured.
// downstream model handling. `initializationResult.models` is already
// policy-filtered by the CLI; we rebuild the picker from the allowlist
// so the user's exact spellings (and `modelOverrides` targets) are the
// values passed to `setModel`, keeping configOptions, the current-model
// resolver, and the stored modelInfos consistent with what the user
// configured. Managed `deniedModels` drops entries the CLI would refuse.
const settingsAvailableModels = settingsManager.getSettings().availableModels;
const settingsModelOverrides = settingsManager.getSettings().modelOverrides;
const allowedModels = Array.isArray(settingsAvailableModels)
@@ -8884,6 +8994,7 @@ export class ClaudeAcpAgent {
initializationResult.models,
settingsAvailableModels,
settingsModelOverrides,
settingsManager.getManagedDeniedModels(),
)
: initializationResult.models;
@@ -8969,15 +9080,15 @@ export class ClaudeAcpAgent {
// and persisted settings; automatic values re-seed on every model switch.
// Legacy clients still leave effort resolution entirely to the CLI.
const useRecommendedValue = clientSupportsRecommendedConfigValue(this.clientCapabilities);
const effortSettings = mergeEffortSettings(
settingsManager.getSettings(),
configuredSettingsObject,
);
const configOptions = buildConfigOptions(
modes,
models,
modelInfos,
userProvidedOptions?.effort ??
settingsEffortForModel(
mergeEffortSettings(settingsManager.getSettings(), configuredSettingsObject),
currentModelInfo,
),
userProvidedOptions?.effort ?? settingsEffortForModel(effortSettings, currentModelInfo),
fastMode,
{
useRecommendedValue,
@@ -8985,23 +9096,19 @@ export class ClaudeAcpAgent {
);
const initialEffort = configOptions.find((option) => option.id === EFFORT_CONFIG_ID);
if (useRecommendedValue && typeof initialEffort?.currentValue === "string") {
await q.applyFlagSettings({ effortLevel: toSdkEffortLevel(initialEffort.currentValue) });
await q.applyFlagSettings(effortFlagSettings(initialEffort.currentValue, effortSettings));
}
// Seed the context window without extra IPC. The cached authoritative
// Seed the context window without awaited IPC. The cached authoritative
// window from a prior turn wins (`result.modelUsage`, cross-session),
// then the text heuristic, then the default. We deliberately do NOT issue
// a getContextUsage call here: before the first prompt turn that control
// request can take tens of seconds, on resumed as well as fresh sessions.
// The authoritative window arrives on the first `result.modelUsage` and
// is cached from there.
// then the text heuristic, then the default. A guessed seed is refined
// by a background getContextUsage once the session is registered
// (`refreshContextWindowInBackground`); the authoritative window then
// arrives on the first `result.modelUsage` and is cached from there.
//
// Text inference alone misses aliases that resolve to extended-context
// models with no "1m" token anywhere in their id or description (e.g.
// `sonnet` → claude-sonnet-5, natively ~1M): those stream
// `usage_update.size: 200000` until the first result's modelUsage corrects
// it — but the cache means only the FIRST session to ever run a turn on such
// a model eats that window, not every fresh session after a process
// restart (issue #596).
// `sonnet` → claude-sonnet-5, and `opus`/`default` since CLI 2.1.283,
// all natively ~1M) — the background refresh covers those (issue #596).
//
// The inference fallback is deliberately keyed to the allowlisted entry: a
// fallback-resolved sibling's resolvedModel/displayName/description can
@@ -9077,6 +9184,7 @@ export class ClaudeAcpAgent {
fileChangeReporter,
};
timing.phase("register");
this.refreshContextWindowInBackground(sessionId, this.sessions[sessionId]);
return {
sessionId,
@@ -10310,6 +10418,7 @@ export function toAcpNotifications(
case "compaction_delta":
case "advisor_tool_result":
case "fallback":
case "mcp_tool_listing":
break;
default:
@@ -10582,10 +10691,9 @@ function commonPrefixLength(a: string, b: string) {
* "claude-opus-4-8[1m]", "Opus 4.7 (1M context)"), so callers pass those too.
* This text scan can't catch every model — some resolve to extended-context
* models with no "1m" anywhere (e.g. `sonnet` → claude-sonnet-5, natively
* ~1M). Such a miss falls back to the default window and is corrected by
* `result.modelUsage` (and cached) within one turn. We do NOT consult the
* SDK's `getContextUsage` to close that gap: before the first prompt turn it
* can take tens of seconds (issues #886/#880, see `contextWindowCache`). */
* ~1M). Such a miss falls back to the default window, is refined by the
* background `getContextUsage` (`refreshContextWindowInBackground`), and is
* corrected by `result.modelUsage` (and cached) within one turn. */
function inferContextWindowFromModel(...texts: Array<string | undefined>): number | null {
if (texts.some((text) => text != null && /\b1m\b/i.test(text))) return 1_000_000;
return null;
@@ -10606,11 +10714,10 @@ function inferContextWindowFromModel(...texts: Array<string | undefined>): numbe
* verbatim live id (rows without `resolvedModel`) can hit too.
*
* Populated authoritatively by each `result.modelUsage` a turn confirms (see
* the consumer's result handler). We deliberately never populate it from a
* fresh session's `getContextUsage`: before that session's first prompt turn
* has run the control request is not serviced (it stalls ~15s, and serializes
* ahead of an awaited `setModel` — issues #886/#880, regressed in 0.59.0), so
* it can neither beat the first `result` nor be issued cheaply before one.
* the consumer's result handler). We deliberately never populate it from
* `getContextUsage`: its `model` spelling and `rawMaxTokens` can differ from
* the `result.modelUsage` key and window (e.g. 967000 vs 1000000 for sonnet),
* so it only refines the live session (`refreshContextWindowInBackground`).
* Cleared on `logout`: 1M-context entitlement can differ per account/tier, so
* windows learned under one login must not seed sessions under the next. */
const contextWindowCache = new Map<string, number>();

View File

@@ -20,6 +20,12 @@ export const AIR_GOAL_KEY = "goal";
export const AIR_KIND_KEY = "kind";
export const AIR_PERMISSION_KEY = "permission";
export const AIR_CUSTOM_ANSWER_KEY = "customAnswer";
/**
* The root `_meta` spelling of {@link AIR_CUSTOM_ANSWER_KEY} that released
* AIR versions read. Remove it when the ACP registry no longer serves AIR
* versions that read only this spelling.
*/
export const LEGACY_AIR_CUSTOM_ANSWER_KEY = "_askUserQuestionCustomAnswer";
const JETBRAINS_META_KEY = "jetbrains";
const AIR_META_KEY = "air";

View File

@@ -12,7 +12,11 @@ import type {
AskUserQuestionInput,
AskUserQuestionOutput,
} from "@anthropic-ai/claude-agent-sdk/sdk-tools.js";
import { AIR_CUSTOM_ANSWER_KEY, withAirMeta } from "./air-extension.js";
import {
AIR_CUSTOM_ANSWER_KEY,
LEGACY_AIR_CUSTOM_ANSWER_KEY,
withAirMeta,
} from "./air-extension.js";
/**
* Bridges between the Claude Agent SDK's elicitation/dialog callbacks and ACP's
@@ -204,6 +208,7 @@ export function askUserQuestionsToCreateRequest(
? { type: "array", title, description, items: { anyOf: options } }
: { type: "string", title, description, oneOf: options };
const customAnswer = { questionId: questionFieldKey(index), isCustomAnswer: true };
properties[questionCustomFieldKey(index)] = {
type: "string",
title: "Other",
@@ -211,13 +216,15 @@ export function askUserQuestionsToCreateRequest(
? "Type your own answer to add to your selection above (optional)."
: "Type your own answer, or add a note to the option you chose above (optional).",
// Marks the field as the custom answer companion of a select question,
// under `_meta.jetbrains.air.customAnswer`. Only AIR gets the marker.
// under `_meta.jetbrains.air.customAnswer` and under the root key that
// released AIR versions read. Only AIR gets the marker.
...(airClient
? {
_meta: withAirMeta(undefined, AIR_CUSTOM_ANSWER_KEY, {
questionId: questionFieldKey(index),
isCustomAnswer: true,
}),
_meta: withAirMeta(
{ [LEGACY_AIR_CUSTOM_ANSWER_KEY]: customAnswer },
AIR_CUSTOM_ANSWER_KEY,
customAnswer,
),
}
: {}),
};

View File

@@ -12,6 +12,18 @@ export function toSdkEffortLevel(value: string | undefined): EffortLevel | null
return value === undefined || value === "default" ? null : (value as EffortLevel);
}
// Since CLI 2.1.284 an `effortLevel` that changes the level, sent without an
// `ultracode` key, turns ultracode off. The CLI's own effort control keeps
// ultracode on at any level, so carry a settings-requested ultracode along
// with every effort apply.
export function effortFlagSettings(
value: string | undefined,
settings: Settings,
): { effortLevel: EffortLevel | null; ultracode?: true } {
const effortLevel = toSdkEffortLevel(value);
return settings.ultracode === true ? { effortLevel, ultracode: true } : { effortLevel };
}
function canonicalizeModelSettingsKey(value: string): string {
return value
.trim()

View File

@@ -260,6 +260,39 @@ function resolveSettingsModel(
return resolveModelPreference(models, settingsModel);
}
const MODEL_FAMILY_ALIASES = new Set(["opus", "sonnet", "haiku", "fable"]);
/** A model id without the spellings `deniedModels` ignores: provider
* prefixes, dates, version suffixes, `-fast` and context hints. */
function normalizeDeniedModelId(value: string): string {
let id = stripContextHints(value.trim().toLowerCase());
const providerPrefix = id.lastIndexOf("anthropic.");
if (providerPrefix !== -1) id = id.slice(providerPrefix + "anthropic.".length);
return id
.replace(/@.*$/, "")
.replace(/-v\d+(?::\d+)?$/, "")
.replace(/-fast$/, "")
.replace(/-\d{8}$/, "");
}
/**
* Whether managed `deniedModels` blocks a model id, following the CLI's rules
* closely enough for the picker: a family alias blocks the whole family, and a
* model id blocks that version in every spelling plus the later minor versions
* it prefixes (`claude-opus-5` also blocks `claude-opus-5-5`). The CLI stays
* the enforcer; this only keeps the picker from offering a refused row.
*/
export function isDeniedModel(modelId: string, deniedModels: readonly string[]): boolean {
const id = normalizeDeniedModelId(modelId);
if (!id) return false;
return deniedModels.some((entry) => {
const denied = normalizeDeniedModelId(entry);
if (!denied) return false;
if (MODEL_FAMILY_ALIASES.has(denied)) return id === denied || id.includes(`-${denied}`);
return id === denied || id.startsWith(`${denied}-`);
});
}
/**
* Restrict the SDK's model list to the user's `availableModels` allowlist
* (already merged-and-deduped across settings sources by `SettingsManager`).
@@ -275,11 +308,14 @@ function resolveSettingsModel(
* - `undefined` is handled by the caller (no allowlist applied).
* - The Default option is unaffected by `availableModels` — it always remains
* available, even when the allowlist is `[]`.
* - Managed `deniedModels` wins over the allowlist, so a denied entry is
* dropped even though the CLI would otherwise leave it unmatched.
*/
export function applyAvailableModelsAllowlist(
sdkModels: ModelInfo[],
allowlist: string[],
settingsModelOverrides?: Record<string, string>,
deniedModels: readonly string[] = [],
): ModelInfo[] {
// Default is always preserved per the docs. Synthesize one if the SDK
// didn't surface it so downstream code (e.g. `getAvailableModels` picking
@@ -310,6 +346,13 @@ export function applyAvailableModelsAllowlist(
if (seen.has(effective)) continue;
const sdkMatch = resolveModelPreference(sdkModelsWithoutDefault, trimmed);
if (
[trimmed, effective, sdkMatch?.resolvedModel].some(
(id) => id != null && isDeniedModel(id, deniedModels),
)
) {
continue;
}
if (sdkMatch) {
result.push({ ...sdkMatch, value: effective });
} else {

View File

@@ -49,6 +49,7 @@ export interface SettingsManagerOptions {
export class SettingsManager {
private cwd: string;
private effective: Settings = {};
private managedDeniedModels: string[] = [];
private watchers: fs.FSWatcher[] = [];
private onChange?: () => void;
private logger: { log: (...args: any[]) => void; error: (...args: any[]) => void };
@@ -106,9 +107,14 @@ export class SettingsManager {
try {
const resolved = await resolveSettings({ cwd: this.cwd });
this.effective = filterEscalatingDefaultMode(resolved);
// The CLI reads `deniedModels` from managed settings only.
this.managedDeniedModels = resolved.sources
.filter((entry) => entry.source === "managed")
.flatMap((entry) => entry.settings.deniedModels ?? []);
} catch (error) {
this.logger.error("Failed to resolve settings:", error);
this.effective = {};
this.managedDeniedModels = [];
}
}
@@ -171,6 +177,14 @@ export class SettingsManager {
return this.effective;
}
/**
* `deniedModels` entries from the managed tier, the only tier the CLI
* honors them from.
*/
getManagedDeniedModels(): string[] {
return this.managedDeniedModels;
}
/**
* Returns the current working directory
*/

View File

@@ -154,6 +154,51 @@ describe("ClaudeAcpAgent settings", () => {
},
);
it("keeps a settings-requested ultracode on when applying effort", async () => {
// Since CLI 2.1.284 an effortLevel apply without an ultracode key turns
// ultracode off; the CLI's own effort control keeps it on.
await fs.promises.writeFile(
path.join(tempDir, "settings.json"),
JSON.stringify({ ultracode: true }),
);
const applyFlagSettings = vi.fn();
querySpy.mockReturnValue(
makeMockQuery({
initializationResult: async () => ({
models: [
{
value: "default",
displayName: "Default",
description: "",
supportsEffort: true,
supportedEffortLevels: ["low", "medium", "high"],
},
],
}),
applyFlagSettings,
}),
);
const { ClaudeAcpAgent } = await import("../acp-agent.js");
const agent = new ClaudeAcpAgent(createMockClient());
(agent as any).clientCapabilities = {
_meta: { jetbrains: { air: { version: 1, capabilities: ["recommendedValue"] } } },
};
const response = await (agent as any).createSession({
cwd: tempDir,
mcpServers: [],
_meta: { disableBuiltInTools: true },
});
expect(applyFlagSettings).toHaveBeenLastCalledWith({ effortLevel: "medium", ultracode: true });
await agent.setSessionConfigOption({
sessionId: response.sessionId,
configId: "effort",
value: "high",
});
expect(applyFlagSettings).toHaveBeenLastCalledWith({ effortLevel: "high", ultracode: true });
});
it("seeds effort from higher-priority programmatic model settings", async () => {
await fs.promises.writeFile(
path.join(tempDir, "settings.json"),

View File

@@ -34,6 +34,7 @@ import {
toAcpNotifications,
promptToClaude,
isLocalCommandMetadata,
isReplayHiddenMetaMessage,
isSyntheticLoginMessage,
stripLocalCommandMetadata,
ClaudeAcpAgent,
@@ -2527,6 +2528,78 @@ describe("synthetic login message (issue #863)", () => {
});
});
describe("meta messages from other agents, sessions and channels", () => {
// The shape SDK 0.3.284 getSessionMessages returns for a host-injected peer
// message (live probe): is_meta plus origin, content is the full framing.
const peerMessage = {
type: "user",
uuid: "peer-1",
session_id: "s1",
parent_tool_use_id: null,
parent_agent_id: null,
is_meta: true,
origin: { kind: "peer", from: "uds:/tmp/fake.sock", hostInjected: true },
message: {
role: "user",
content:
'Another Claude session sent a message:\n<cross-session-message from="uds:/tmp/fake.sock">enveloped hi</cross-session-message>\n\nThis came from another Claude session — not typed by your user.',
},
};
it("isReplayHiddenMetaMessage matches only meta messages with an inter-agent origin", () => {
expect(isReplayHiddenMetaMessage(peerMessage)).toBe(true);
for (const kind of ["channel", "observer", "observer-activity", "slack-ping"]) {
expect(isReplayHiddenMetaMessage({ ...peerMessage, origin: { kind } })).toBe(true);
}
// Not meta: an un-framed message persisted as a normal user turn.
expect(isReplayHiddenMetaMessage({ ...peerMessage, is_meta: undefined })).toBe(false);
// Meta without an inter-agent origin, such as a compact summary.
expect(
isReplayHiddenMetaMessage({ ...peerMessage, origin: undefined, isCompactSummary: true }),
).toBe(false);
expect(isReplayHiddenMetaMessage({ ...peerMessage, origin: { kind: "human" } })).toBe(false);
expect(isReplayHiddenMetaMessage({ ...peerMessage, type: "assistant" })).toBe(false);
expect(isReplayHiddenMetaMessage(undefined)).toBe(false);
});
it("loadSession replay skips them but keeps the user's own prompt", async () => {
const updates: SessionNotification[] = [];
const client = {
sessionUpdate: async (u: SessionNotification) => {
updates.push(u);
},
} as unknown as AcpClient;
const agent = new ClaudeAcpAgent(client, { log: () => {}, error: () => {} });
vi.mocked(getSessionMessages).mockResolvedValueOnce([
{
type: "user",
uuid: "u1",
session_id: "s1",
parent_tool_use_id: null,
parent_agent_id: null,
message: { role: "user", content: [{ type: "text", text: "hi, say one word" }] },
},
peerMessage,
] as Awaited<ReturnType<typeof getSessionMessages>>);
await (
agent as unknown as { replaySessionHistory(sessionId: string): Promise<void> }
).replaySessionHistory("s1");
expect(
updates.some(
(u) =>
u.update.sessionUpdate === "user_message_chunk" &&
u.update.content.type === "text" &&
u.update.content.text.includes("hi, say one word"),
),
).toBe(true);
expect(JSON.stringify(updates)).not.toContain("cross-session-message");
expect(JSON.stringify(updates)).not.toContain("Another Claude session");
});
});
describe("usage-limit failure replay", () => {
const usageLimitMessage = {
type: "assistant" as const,
@@ -19310,6 +19383,59 @@ describe("turn steering (_session/steering)", () => {
await agent.sessions["test-session"]?.consumer;
});
// CLI 2.1.286: a steer that lands during a foreground tool call moves the tool
// to the background and joins the running cycle instead of aborting it, so
// the turn ends in ONE result stamped with both the prompt and the steer.
it.each([
{ name: "without an echo", echo: false },
{ name: "with a mid-cycle echo", echo: true },
])("settles a steer that joined the running cycle $name", async ({ echo }) => {
const timeline: string[] = [];
const agent = new ClaudeAcpAgent(timelineClient(timeline), {
log: () => {},
error: () => {},
});
injectGeneratorSession(agent, (input) => {
async function* messageGenerator() {
const iter = input[Symbol.asyncIterator]();
const u1 = await iter.next();
yield userEcho(u1.value);
yield createAssistantText("working on it");
const steered = await iter.next();
if (echo) yield userEcho(steered.value);
yield createAssistantText("STEERED-OK");
yield {
...createResultMessage(),
user_message_uuid: u1.value.uuid,
user_message_uuids: [u1.value.uuid, steered.value.uuid],
};
yield idleMessage();
}
return messageGenerator();
});
const turn = agent
.prompt({ sessionId: "test-session", prompt: [{ type: "text", text: "start" }] })
.then((response) => {
timeline.push(`prompt:${response.stopReason}`);
return response;
});
await waitFor(() => !!agent.sessions["test-session"]?.activeTurn);
await expect(
agent.steer({ sessionId: "test-session", prompt: [{ type: "text", text: "also handle X" }] }),
).resolves.toEqual({ outcome: "injected" });
const response = await turn;
await agent.sessions["test-session"]?.consumer;
expect(timeline).toEqual(["working on it", "STEERED-OK", "prompt:end_turn"]);
// One cycle ran, so usage is the single result's.
expect(response.usage?.inputTokens).toBe(10);
expect(agent.sessions["test-session"].turnQueue).toHaveLength(0);
expect(agent.sessions["test-session"].owedTrailingIdles ?? 0).toBe(0);
});
// Issue #1114's other hang, and #1063's: the steer aborts an autonomous
// followup, whose result counts a trailing idle that the abort then folds into
// the one idle spanning both cycles. Absorbing that idle as debt would starve

View File

@@ -50,6 +50,7 @@ import {
compareWithBaseline,
} from "./acp-scenarios/compare.js";
import { EXTENSION_SESSION_UPDATES, validateRecorded } from "./acp-scenarios/schema.js";
import { LEGACY_AIR_CUSTOM_ANSWER_KEY } from "../air-extension.js";
vi.mock("@anthropic-ai/claude-agent-sdk", async (importOriginal) => {
const actual = await importOriginal<typeof import("@anthropic-ai/claude-agent-sdk")>();
@@ -661,6 +662,7 @@ describe.skipIf(baselineDir)("ACP scenarios", () => {
(record) => record.kind === "createElicitation",
)!.payload as Record<string, any>;
expect(elicitation.requestedSchema.properties.question_0_custom._meta).toEqual({
_askUserQuestionCustomAnswer: { questionId: "question_0", isCustomAnswer: true },
jetbrains: {
air: { version: 1, customAnswer: { questionId: "question_0", isCustomAnswer: true } },
},
@@ -689,7 +691,13 @@ describe.skipIf(baselineDir)("ACP scenarios", () => {
for (const scenario of SCENARIOS) {
for (const { at, meta } of metaObjects(air(scenario.name))) {
const duplicates = [
...Object.keys(meta).filter((k) => AIR_ONLY_META_KEYS.has(k) && k !== "jetbrains"),
// Released AIR versions read only the legacy custom answer key, so AIR gets both.
...Object.keys(meta).filter(
(k) =>
AIR_ONLY_META_KEYS.has(k) &&
k !== "jetbrains" &&
k !== LEGACY_AIR_CUSTOM_ANSWER_KEY,
),
...Object.keys((meta.claudeCode as Record<string, unknown> | undefined) ?? {}).filter(
(k) => AIR_ONLY_CLAUDE_CODE_KEYS.has(k),
),

View File

@@ -3,7 +3,7 @@
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"_meta":{"claudeCode":{"toolName":"AskUserQuestion"}},"content":[],"kind":"other","name":"AskUserQuestion","sessionUpdate":"tool_call","status":"pending","title":"Asking for your input","toolCallId":"toolu_ask"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"sessionUpdate":"usage_update","size":200000,"used":3}}}
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"rawInput":{"questions":[{"header":"Database","multiSelect":false,"options":[{"description":"Relational","label":"Postgres","preview":"CREATE TABLE t ();"},{"description":"Key-value","label":"Redis"}],"question":"Which database?"}]},"sessionUpdate":"tool_call_update","toolCallId":"toolu_ask"}}}
{"kind":"createElicitation","payload":{"message":"Which database?","mode":"form","requestedSchema":{"properties":{"question_0":{"oneOf":[{"_meta":{"_claude/askUserQuestionOption":{"preview":"CREATE TABLE t ();"}},"const":"Postgres","description":"Relational","title":"Postgres"},{"const":"Redis","description":"Key-value","title":"Redis"}],"title":"Database","type":"string"},"question_0_custom":{"_meta":{"jetbrains":{"air":{"customAnswer":{"isCustomAnswer":true,"questionId":"question_0"},"version":1}}},"description":"Type your own answer, or add a note to the option you chose above (optional).","title":"Other","type":"string"}},"type":"object"},"sessionId":"<session>","toolCallId":"toolu_ask"}}
{"kind":"createElicitation","payload":{"message":"Which database?","mode":"form","requestedSchema":{"properties":{"question_0":{"oneOf":[{"_meta":{"_claude/askUserQuestionOption":{"preview":"CREATE TABLE t ();"}},"const":"Postgres","description":"Relational","title":"Postgres"},{"const":"Redis","description":"Key-value","title":"Redis"}],"title":"Database","type":"string"},"question_0_custom":{"_meta":{"_askUserQuestionCustomAnswer":{"isCustomAnswer":true,"questionId":"question_0"},"jetbrains":{"air":{"customAnswer":{"isCustomAnswer":true,"questionId":"question_0"},"version":1}}},"description":"Type your own answer, or add a note to the option you chose above (optional).","title":"Other","type":"string"}},"type":"object"},"sessionId":"<session>","toolCallId":"toolu_ask"}}
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"content":[{"content":{"text":"User has answered your questions: \"Which database?\"=\"Postgres\".","type":"text"},"type":"content"}],"sessionUpdate":"tool_call_update","status":"completed","toolCallId":"toolu_ask"}}}
{"kind":"sessionUpdate","payload":{"sessionId":"<session>","update":{"_meta":{"_claude/model":"claude-sonnet-4-6"},"cost":{"amount":0,"currency":"USD"},"sessionUpdate":"usage_update","size":200000,"used":2}}}
{"kind":"promptResponse","payload":{"_meta":{"quota":{"model_usage":[],"token_count":{"cachedInputTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"reasoningOutputTokens":0,"totalTokens":0}}},"stopReason":"end_turn","usage":{"cachedReadTokens":0,"cachedWriteTokens":0,"inputTokens":0,"outputTokens":0,"totalTokens":0}}}

View File

@@ -852,26 +852,40 @@ describe("createSession options merging", () => {
return (agent as unknown as { sessions: Record<string, any> }).sessions[sessionId];
}
it("does not call getContextUsage during session creation", async () => {
// getContextUsage stalls until the session's first prompt turn has run
// (it is not serviced pre-turn), so session/new must never call it —
it("does not wait for getContextUsage during session creation", async () => {
// SDK control requests are serialized and getContextUsage used to stall
// until the first prompt turn, so session/new only kicks it off —
// awaiting it inline is what regressed session/new latency in 0.59.0.
const ctxSpy = vi.fn(async () => ({ rawMaxTokens: 967000 }));
const ctxSpy = vi.fn(() => new Promise<never>(() => {}));
contextUsageResult = ctxSpy;
await agent.newSession({ cwd: process.cwd(), mcpServers: [] });
const response = await agent.newSession({ cwd: process.cwd(), mcpServers: [] });
expect(ctxSpy).not.toHaveBeenCalled();
expect(ctxSpy).toHaveBeenCalledOnce();
expect(sessionFor(response.sessionId).contextWindowSize).toBe(200000);
expect(sessionFor(response.sessionId).contextWindowAuthoritative).toBe(false);
});
it("seeds contextWindowSize from text inference, falling back to the default when it misses", async () => {
it("refines a guessed window from getContextUsage in the background", async () => {
// The mock model ("claude-sonnet-4-6" / "Claude Sonnet" / "Fast") carries
// no "1m" token anywhere, so inference misses and the window falls back to
// the default; the authoritative value arrives later via result.modelUsage.
// no "1m" token anywhere, so inference misses and the seed is the default
// until the background getContextUsage answers.
contextUsageResult = async () => ({ rawMaxTokens: 967000 });
const response = await agent.newSession({ cwd: process.cwd(), mcpServers: [] });
await vi.waitFor(() => expect(sessionFor(response.sessionId).contextWindowSize).toBe(967000));
expect(sessionFor(response.sessionId).contextWindowAuthoritative).toBe(true);
});
it("keeps the guessed window when getContextUsage reports a non-positive size", async () => {
const ctxSpy = vi.fn(async () => ({ rawMaxTokens: 0 }));
contextUsageResult = ctxSpy;
const response = await agent.newSession({ cwd: process.cwd(), mcpServers: [] });
await vi.waitFor(() => expect(ctxSpy).toHaveBeenCalled());
await new Promise((resolve) => setImmediate(resolve));
expect(sessionFor(response.sessionId).contextWindowSize).toBe(200000);
expect(sessionFor(response.sessionId).contextWindowAuthoritative).toBe(false);
});
@@ -918,7 +932,7 @@ describe("createSession options merging", () => {
expect(response.configOptions?.find((option) => option.id === "model")?.currentValue).toBe(
"haiku",
);
expect(ctxSpy).not.toHaveBeenCalled();
// Kicked off in the background, never awaited: it never answers here.
expect(sessionFor("resumed-model-probe").contextWindowAuthoritative).toBe(false);
expect(getSessionMessages).toHaveBeenCalledTimes(1);
expect(getSessionMessages).toHaveBeenCalledWith("resumed-model-probe");

View File

@@ -265,6 +265,7 @@ describe("askUserQuestionsToCreateRequest", () => {
type: "string",
title: "Other",
_meta: {
_askUserQuestionCustomAnswer: { questionId: "question_0", isCustomAnswer: true },
jetbrains: {
air: { customAnswer: { questionId: "question_0", isCustomAnswer: true } },
},
@@ -274,6 +275,7 @@ describe("askUserQuestionsToCreateRequest", () => {
type: "string",
title: "Other",
_meta: {
_askUserQuestionCustomAnswer: { questionId: "question_1", isCustomAnswer: true },
jetbrains: {
air: { customAnswer: { questionId: "question_1", isCustomAnswer: true } },
},

View File

@@ -156,7 +156,7 @@ describe("versioned model display names", () => {
// Account-dependent model availability requires the same authenticated environment
// as the adapter. No prompt is sent: inspect only the SDK initialization response.
it.skipIf(!process.env.RUN_INTEGRATION_TESTS)(
"the real SDK exposes one named Opus with the expected context suffix",
"the real SDK exposes one plain Opus row",
async () => {
let finishInput!: () => void;
const inputGate = new Promise<void>((resolve) => {
@@ -178,12 +178,14 @@ it.skipIf(!process.env.RUN_INTEGRATION_TESTS)(
/\bopus\b/i.test(`${model.value} ${model.resolvedModel} ${model.displayName}`),
);
expect(opusModels).toHaveLength(1);
// CLI 2.1.283 dropped the "[1m]" / "(1M context)" spelling from the Opus
// row: Opus is natively 1M, so the row names the plain model.
expect(opusModels[0]).toMatchObject({
value: "opus[1m]",
displayName: "Opus (1M context)",
resolvedModel: expect.stringMatching(/^claude-opus-.*\[1m\]$/),
description: expect.stringContaining("1M context"),
value: "opus",
displayName: "Opus",
resolvedModel: expect.stringMatching(/^claude-opus-[\d-]+$/),
});
expect(opusModels[0].description).not.toMatch(/\b1m\b/i);
} finally {
clearTimeout(timeout);
finishInput();

View File

@@ -5,9 +5,10 @@ import type { ModelInfo } from "@anthropic-ai/claude-agent-sdk";
import {
resolveModelPreference,
applyAvailableModelsAllowlist,
isDeniedModel,
matchResumedModel,
} from "../session-model.js";
import { settingsEffortForModel } from "../session-effort.js";
import { effortFlagSettings, settingsEffortForModel } from "../session-effort.js";
// Mirrors a real `supportedModels()` response: alias rows carry
// `resolvedModel`, and "Sonnet 5" has no `major.minor` dot unlike older
@@ -234,6 +235,49 @@ describe("applyAvailableModelsAllowlist - modelOverrides", () => {
});
});
describe("applyAvailableModelsAllowlist - managed deniedModels", () => {
it("drops an allowlisted model that deniedModels blocks, keeping Default", () => {
const result = applyAvailableModelsAllowlist(
LIVE_SHAPED_MODELS,
["claude-sonnet-5", "claude-opus-5-5"],
undefined,
["claude-opus-5"],
);
expect(result.map((m) => m.value)).toEqual(["default", "claude-sonnet-5"]);
});
it("checks an alias entry against the row it resolves to", () => {
const result = applyAvailableModelsAllowlist(LIVE_SHAPED_MODELS, ["sonnet"], undefined, [
"claude-sonnet-5",
]);
expect(result.map((m) => m.value)).toEqual(["default"]);
});
});
describe("isDeniedModel", () => {
it("blocks every spelling of a denied version and its later minors", () => {
const denied = ["claude-opus-5"];
expect(isDeniedModel("claude-opus-5", denied)).toBe(true);
expect(isDeniedModel("claude-opus-5-20260101", denied)).toBe(true);
expect(isDeniedModel("claude-opus-5[1m]", denied)).toBe(true);
expect(isDeniedModel("us.anthropic.claude-opus-5-v1:0", denied)).toBe(true);
expect(isDeniedModel("claude-opus-5-5", denied)).toBe(true);
expect(isDeniedModel("claude-opus-4-8", denied)).toBe(false);
});
it("blocks only that version when the entry names a minor", () => {
expect(isDeniedModel("claude-opus-5", ["claude-opus-5-5"])).toBe(false);
expect(isDeniedModel("claude-opus-5-5-fast", ["claude-opus-5-5"])).toBe(true);
});
it("blocks a whole family for a family alias and ignores settings-dependent aliases", () => {
expect(isDeniedModel("claude-haiku-4-5-20251001", ["haiku"])).toBe(true);
expect(isDeniedModel("haiku", ["haiku"])).toBe(true);
expect(isDeniedModel("claude-sonnet-5", ["haiku"])).toBe(false);
expect(isDeniedModel("claude-opus-5", ["default", "best", "opusplan"])).toBe(false);
});
});
// Runs against the real SDK (no mocks) so the fixture above is flagged the
// moment the SDK's actual model list shape drifts from it. Requires a usable
// ANTHROPIC_API_KEY, hence opt-in via RUN_INTEGRATION_TESTS.
@@ -325,3 +369,18 @@ describe("settingsEffortForModel", () => {
expect(settingsEffortForModel({ modelSettings: {} }, undefined)).toBeUndefined();
});
});
describe("effortFlagSettings", () => {
it("carries ultracode only when settings request it", () => {
expect(effortFlagSettings("high", { ultracode: true })).toEqual({
effortLevel: "high",
ultracode: true,
});
expect(effortFlagSettings("default", { ultracode: true })).toEqual({
effortLevel: null,
ultracode: true,
});
expect(effortFlagSettings("high", { ultracode: false })).toEqual({ effortLevel: "high" });
expect(effortFlagSettings(undefined, {})).toEqual({ effortLevel: null });
});
});

View File

@@ -1155,13 +1155,11 @@ describe("session config options", () => {
return (agent as unknown as { sessions: Record<string, any> }).sessions[SESSION_ID];
}
it("sets the window from text inference on model switch, without any getContextUsage IPC", async () => {
// getContextUsage stalls until the session's first prompt turn, so the
// switch path must never call it; the window is seeded from the text
// heuristic (here via the new model's resolvedModel) and later confirmed
// by result.modelUsage.
it("seeds the window from text inference on model switch", async () => {
// The window is seeded synchronously from the text heuristic (here via
// the new model's resolvedModel); the background refresh never answers.
const session = getSession();
session.query.getContextUsage = vi.fn(async () => ({ rawMaxTokens: 967000 }));
session.query.getContextUsage = vi.fn(() => new Promise<never>(() => {}));
session.modelInfos = session.modelInfos.map((m: ModelInfo) =>
m.value === "claude-sonnet-4-6" ? { ...m, resolvedModel: "claude-sonnet-5[1m]" } : m,
);
@@ -1172,15 +1170,17 @@ describe("session config options", () => {
value: "claude-sonnet-4-6",
});
expect(session.query.getContextUsage).not.toHaveBeenCalled();
expect(session.contextWindowSize).toBe(1_000_000);
expect(session.contextWindowAuthoritative).toBe(false);
});
it("falls back to the default window when inference misses, without any getContextUsage IPC", async () => {
it("falls back to the default window when inference misses, then refines it in the background", async () => {
const session = getSession();
session.contextWindowSize = 1_000_000;
// Present but must NOT be called; the switch never consults it.
session.query.getContextUsage = vi.fn(async () => ({ rawMaxTokens: 967000 }));
let answer!: (usage: { rawMaxTokens: number }) => void;
session.query.getContextUsage = vi.fn(
() => new Promise<{ rawMaxTokens: number }>((resolve) => (answer = resolve)),
);
// claude-sonnet-4-6 carries no "1m" token in its id, resolvedModel,
// displayName, or description, so inference misses → default window.
@@ -1190,22 +1190,40 @@ describe("session config options", () => {
value: "claude-sonnet-4-6",
});
expect(session.query.getContextUsage).not.toHaveBeenCalled();
// The switch resolved without waiting for getContextUsage.
expect(session.query.getContextUsage).toHaveBeenCalledOnce();
expect(session.contextWindowSize).toBe(200000);
answer({ rawMaxTokens: 967000 });
await vi.waitFor(() => expect(session.contextWindowSize).toBe(967000));
expect(session.contextWindowAuthoritative).toBe(true);
});
it("does not call getContextUsage even when switching to a fresh model", async () => {
it("drops a background answer that arrives after another switch", async () => {
const session = getSession();
const spy = vi.fn(async () => ({ rawMaxTokens: 967000 }));
session.query.getContextUsage = spy;
let answer!: (usage: { rawMaxTokens: number }) => void;
session.query.getContextUsage = vi.fn(
() => new Promise<{ rawMaxTokens: number }>((resolve) => (answer = resolve)),
);
await agent.setSessionConfigOption({
sessionId: SESSION_ID,
configId: "model",
value: "claude-sonnet-4-6",
});
const staleAnswer = answer;
await agent.setSessionConfigOption({
sessionId: SESSION_ID,
configId: "model",
value: "claude-opus-4-5",
});
const windowAfterSecondSwitch = session.contextWindowSize;
expect(spy).not.toHaveBeenCalled();
staleAnswer({ rawMaxTokens: 967000 });
await new Promise((resolve) => setImmediate(resolve));
expect(session.contextWindowSize).toBe(windowAfterSecondSwitch);
expect(session.contextWindowAuthoritative).toBe(false);
});
it("keeps the learned window when re-asserting the current model", async () => {