The auth subcommand goes through a flow that requires pasting in a code
on remote environments, but does not allow for pasting that code in.
Until they fix this in the CLI subcommand, we'll have to go to the old
flow of launching the full CLI.
Bumps [@hono/node-server](https://github.com/honojs/node-server) from
1.19.12 to 1.19.13.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/honojs/node-server/releases"><code>@hono/node-server</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v1.19.13</h2>
<h2>Security Fix</h2>
<p>Fixed an issue in Serve Static Middleware where inconsistent handling
of repeated slashes (<code>//</code>) between the router and static file
resolution could allow middleware to be bypassed. Users of Serve Static
Middleware are encouraged to upgrade to this version.</p>
<p>See GHSA-92pp-h63x-v22m for details.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="fd64e659a3"><code>fd64e65</code></a>
1.19.13</li>
<li><a
href="025c30f55d"><code>025c30f</code></a>
Merge commit from fork</li>
<li>See full diff in <a
href="https://github.com/honojs/node-server/compare/v1.19.12...v1.19.13">compare
view</a></li>
</ul>
</details>
<br />
[](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)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/agentclientprotocol/claude-agent-acp/network/alerts).
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [hono](https://github.com/honojs/hono) from 4.12.11 to 4.12.12.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/honojs/hono/releases">hono's
releases</a>.</em></p>
<blockquote>
<h2>v4.12.12</h2>
<h2>Security fixes</h2>
<p>This release includes fixes for the following security issues:</p>
<h3>Middleware bypass via repeated slashes in serveStatic</h3>
<p>Affects: Serve Static middleware. Fixes a path normalization
inconsistency where repeated slashes (<code>//</code>) could bypass
route-based middleware protections and allow access to protected static
files. GHSA-wmmm-f939-6g9c</p>
<h3>Path traversal in toSSG() allows writing files outside the output
directory</h3>
<p>Affects: <code>toSSG()</code> for Static Site Generation. Fixes a
path traversal issue where crafted <code>ssgParams</code> values could
write files outside the configured output directory.
GHSA-xf4j-xp2r-rqqx</p>
<h3>Incorrect IP matching in ipRestriction() for IPv4-mapped IPv6
addresses</h3>
<p>Affects: IP Restriction Middleware. Fixes improper handling of
IPv4-mapped IPv6 addresses (e.g. <code>::ffff:127.0.0.1</code>) that
could cause allow/deny rules to be bypassed. GHSA-xpcf-pg52-r92g</p>
<h3>Missing validation of cookie name on write path in setCookie()</h3>
<p>Affects: <code>setCookie()</code>, <code>serialize()</code>, and
<code>serializeSigned()</code> from <code>hono/cookie</code>. Fixes
missing validation of cookie names on the write path, preventing
inconsistent handling between parsing and serialization.
GHSA-26pp-8wgv-hjvm</p>
<h3>Non-breaking space prefix bypass in cookie name handling in
getCookie()</h3>
<p>Affects: <code>getCookie()</code> from <code>hono/cookie</code>.
Fixes a discrepancy in cookie name handling that could allow
attacker-controlled cookies to override legitimate ones and bypass
prefix protections. GHSA-r5rp-j6wh-rvv4</p>
<hr />
<p>Users who use Serve Static, Static Site Generation, Cookie utilities,
or IP restriction middleware are strongly encouraged to upgrade to this
version.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="c37ba26da9"><code>c37ba26</code></a>
4.12.12</li>
<li><a
href="cc067c8559"><code>cc067c8</code></a>
Merge commit from fork</li>
<li><a
href="a586cd72e3"><code>a586cd7</code></a>
Merge commit from fork</li>
<li><a
href="48fa2233bc"><code>48fa223</code></a>
Merge commit from fork</li>
<li><a
href="b470278920"><code>b470278</code></a>
Merge commit from fork</li>
<li><a
href="9aff14bd72"><code>9aff14b</code></a>
Merge commit from fork</li>
<li>See full diff in <a
href="https://github.com/honojs/hono/compare/v4.12.11...v4.12.12">compare
view</a></li>
</ul>
</details>
<br />
[](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)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/agentclientprotocol/claude-agent-acp/network/alerts).
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Closes#512
Fingerprint session-defining params to detect cwd or MCP server
changes, reuse sessions when unchanged, and ignore MCP server
ordering differences. Extract session teardown into a helper and add
tests covering reuse and recreation paths.
Title:
fix: ANTHROPIC_MODEL env var not respected in ACP agent model selection
Body:
## Summary
The `ANTHROPIC_MODEL` environment variable is not being respected when
using Claude Code via the ACP (Agent Client Protocol). This causes users
who set their preferred model via
environment variable to have their setting ignored, with the default
model (`claude-sonnet-4-6`) being used instead.
## Problem
When using Claude Code through ACP integrations (e.g., agent platforms),
users expect the `ANTHROPIC_MODEL` environment variable to control which
model is used for LLM requests.
However, the current implementation only checks `settings.model` (from
configuration files) and completely ignores `ANTHROPIC_MODEL`.
## Root Cause
In `src/acp-agent.ts`, the `getAvailableModels` function only checks
`settings.model`:
```typescript
let currentModel = models[0];
if (settings.model) {
const match = resolveModelPreference(models, settings.model);
if (match) {
currentModel = match;
}
}
The ANTHROPIC_MODEL environment variable is never read, so
environment-based model configuration is silently ignored.
Reproduction
1. Set ANTHROPIC_MODEL=glm-4.7 (or any custom model)
2. Start Claude Code via ACP protocol
3. Send a prompt request
4. Capture network traffic - the actual model used is claude-sonnet-4-6
instead of glm-4.7
Fix
Added priority-based model selection that respects the ANTHROPIC_MODEL
environment variable, aligning with the main Claude Code CLI behavior:
// Model priority (highest to lowest):
// 1. ANTHROPIC_MODEL environment variable
// 2. settings.model (user configuration)
// 3. models[0] (default first model)
if (process.env.ANTHROPIC_MODEL) {
const match = resolveModelPreference(models,
process.env.ANTHROPIC_MODEL);
if (match) {
currentModel = match;
}
} else if (settings.model) {
const match = resolveModelPreference(models, settings.model);
if (match) {
currentModel = match;
}
}
This matches the model selection priority used by the main Claude Code
CLI.
Testing
- Verified ANTHROPIC_MODEL is now respected when set
- Verified settings.model still works when ANTHROPIC_MODEL is not set
- Verified default model (models[0]) is used when neither is set
Breaking Change
None - this fix makes behavior more consistent with user expectations
and the main CLI.
ACP version is `v0.25.0`. Should be the newest.
I get this error when pressing Shift+Tab to cycle through the modes:
```
Caused by:
RPC request LspQuery failed: unknown buffer id 12884902333
2026-04-05T21:46:02+00:00 WARN [agent_servers::acp] agent stderr: Error handling request {
2026-04-05T21:46:02+00:00 WARN [agent_servers::acp] agent stderr: jsonrpc: '2.0',
2026-04-05T21:46:02+00:00 WARN [agent_servers::acp] agent stderr: id: 48,
2026-04-05T21:46:02+00:00 WARN [agent_servers::acp] agent stderr: method: 'session/set_config_option',
2026-04-05T21:46:02+00:00 WARN [agent_servers::acp] agent stderr: params: {
2026-04-05T21:46:02+00:00 WARN [agent_servers::acp] agent stderr: sessionId: '$UUID_GOES_HERE',
2026-04-05T21:46:02+00:00 WARN [agent_servers::acp] agent stderr: configId: 'mode',
2026-04-05T21:46:02+00:00 WARN [agent_servers::acp] agent stderr: value: 'auto'
2026-04-05T21:46:02+00:00 WARN [agent_servers::acp] agent stderr: }
2026-04-05T21:46:02+00:00 WARN [agent_servers::acp] agent stderr: } {
2026-04-05T21:46:02+00:00 WARN [agent_servers::acp] agent stderr: code: -32603,
2026-04-05T21:46:02+00:00 WARN [agent_servers::acp] agent stderr: message: 'Internal error',
2026-04-05T21:46:02+00:00 WARN [agent_servers::acp] agent stderr: data: { details: 'Invalid Mode' }
2026-04-05T21:46:02+00:00 WARN [agent_servers::acp] agent stderr: }
2026-04-05T21:46:02+00:00 ERROR [agent_ui::config_options] Failed to set config option: Internal error: {
"details": "Invalid Mode"
}
```
I looked through the code on both sides and I think what might be
happening is, here are the modes:
f422c7c954/src/acp-agent.ts (L236-L244)
When a user gets to `bypassPermissions` and cycle again, the next
selected mode is `auto` but that option seems to be missing from here:
f422c7c954/src/acp-agent.ts (L1007-L1013)
Could the maintainers please help verify and test?
Signed-off-by: k4yt3x <i@k4yt3x.com>
Bumps
[@anthropic-ai/sdk](https://github.com/anthropics/anthropic-sdk-typescript)
from 0.80.0 to 0.81.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/anthropics/anthropic-sdk-typescript/releases"><code>@anthropic-ai/sdk</code>'s
releases</a>.</em></p>
<blockquote>
<h2>sdk: v0.81.0</h2>
<h2>0.81.0 (2026-03-31)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.80.0...sdk-v0.81.0">sdk-v0.80.0...sdk-v0.81.0</a></p>
<h3>Features</h3>
<ul>
<li>add .type field to APIError for error kind identification (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/790">#790</a>)
(<a
href="4bf637d962">4bf637d</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>memory:</strong> append path separator in validatePath
prefix check (<a
href="0ac69b3438">0ac69b3</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><strong>ci:</strong> run builds on CI even if only spec metadata
changed (<a
href="70b657aac1">70b657a</a>)</li>
<li><strong>ci:</strong> skip lint on metadata-only changes (<a
href="69bdc94a16">69bdc94</a>)</li>
<li><strong>internal:</strong> codegen related update (<a
href="7ff7390029">7ff7390</a>)</li>
<li><strong>internal:</strong> update gitignore (<a
href="46d66675d5">46d6667</a>)</li>
<li><strong>internal:</strong> update multipart form array serialization
(<a
href="d55b07d4f7">d55b07d</a>)</li>
<li><strong>tests:</strong> bump steady to v0.19.4 (<a
href="4957a5e65b">4957a5e</a>)</li>
<li><strong>tests:</strong> bump steady to v0.19.5 (<a
href="c511ae0421">c511ae0</a>)</li>
<li><strong>tests:</strong> bump steady to v0.19.6 (<a
href="6d2b4b910a">6d2b4b9</a>)</li>
<li><strong>tests:</strong> bump steady to v0.19.7 (<a
href="d6cff9d2c8">d6cff9d</a>)</li>
<li><strong>tests:</strong> bump steady to v0.20.1 (<a
href="284561fe36">284561f</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/anthropics/anthropic-sdk-typescript/blob/main/CHANGELOG.md"><code>@anthropic-ai/sdk</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>0.81.0 (2026-03-31)</h2>
<p>Full Changelog: <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.80.0...sdk-v0.81.0">sdk-v0.80.0...sdk-v0.81.0</a></p>
<h3>Features</h3>
<ul>
<li>add .type field to APIError for error kind identification (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/790">#790</a>)
(<a
href="4bf637d962">4bf637d</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>memory:</strong> append path separator in validatePath
prefix check (<a
href="0ac69b3438">0ac69b3</a>)</li>
</ul>
<h3>Chores</h3>
<ul>
<li><strong>ci:</strong> run builds on CI even if only spec metadata
changed (<a
href="70b657aac1">70b657a</a>)</li>
<li><strong>ci:</strong> skip lint on metadata-only changes (<a
href="69bdc94a16">69bdc94</a>)</li>
<li><strong>internal:</strong> codegen related update (<a
href="7ff7390029">7ff7390</a>)</li>
<li><strong>internal:</strong> update gitignore (<a
href="46d66675d5">46d6667</a>)</li>
<li><strong>internal:</strong> update multipart form array serialization
(<a
href="d55b07d4f7">d55b07d</a>)</li>
<li><strong>tests:</strong> bump steady to v0.19.4 (<a
href="4957a5e65b">4957a5e</a>)</li>
<li><strong>tests:</strong> bump steady to v0.19.5 (<a
href="c511ae0421">c511ae0</a>)</li>
<li><strong>tests:</strong> bump steady to v0.19.6 (<a
href="6d2b4b910a">6d2b4b9</a>)</li>
<li><strong>tests:</strong> bump steady to v0.19.7 (<a
href="d6cff9d2c8">d6cff9d</a>)</li>
<li><strong>tests:</strong> bump steady to v0.20.1 (<a
href="284561fe36">284561f</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="bc1a2ff79c"><code>bc1a2ff</code></a>
chore: release main (<a
href="https://redirect.github.com/anthropics/anthropic-sdk-typescript/issues/955">#955</a>)</li>
<li>See full diff in <a
href="https://github.com/anthropics/anthropic-sdk-typescript/compare/sdk-v0.80.0...sdk-v0.81.0">compare
view</a></li>
</ul>
</details>
<br />
[](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)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/agentclientprotocol/claude-agent-acp/network/alerts).
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
The `usage_update` notification reports `size: 200000` even when the
active model has a 1M context window (e.g. `opus[1m]`), causing clients
to display incorrect context utilization (e.g. `689k/200k (344.3%)`
instead of `689k/1000k (68.9%)`).
Four bugs fixed:
- **Min across all models**: The original code used `Math.min` across
all `modelUsage` entries, so subagent models (Sonnet/Haiku with 200k
windows) dragged down the reported size for the main Opus 1M model. Now
tracks the top-level assistant model and looks up its context window
specifically.
- **Model name mismatch**: The SDK's streaming path keys `modelUsage` by
the requested model alias (e.g. `claude-opus-4-6`) while
`BetaMessage.model` on assistant messages has the resolved API response
model (e.g. `claude-opus-4-6-20250514`). The exact-match lookup always
missed, falling back to the hardcoded 200k default. Now falls back to
prefix matching, preferring the longest/most-specific match.
- **Synthetic messages corrupt model tracking**: `/compact` and similar
commands emit assistant messages with `model: "<synthetic>"`. These were
updating `lastAssistantModel`, causing the next `usage_update` to miss
the `modelUsage` lookup and fall back to the 200k default. Now filters
out `<synthetic>` models.
- **Stale usage after compaction**: No `usage_update` was sent on
`compact_boundary`, so clients kept showing the pre-compaction context
size (e.g. `944k/1m`) right after "Compacting completed" until the next
full turn. Now sends `used: 0` immediately on compaction. This is a
deliberate approximation — the exact post-compaction size isn't known
until the SDK's next API call, which replaces it within seconds. The
alternative (no update) is worse UX: showing a full context bar right
after compaction.
Eight new tests cover: token sum correctness, current-model context
window lookup, model switching, subagent isolation, prefix matching in
both directions, and synthetic message filtering.
Note: `bin/test` (local CI validation script) is cherry-picked from
#353.
Would fix `agent-shell`'s usage indicator which currently has to defend
against this broken math:
https://github.com/xenodium/agent-shell/pull/364
## Testing
- [x] Manual riding with it for a bit.
---------
Co-authored-by: Tim Visher <194828183+timvisher-dd@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Steffen Deusch <steffen@deusch.me>
## Summary
- **Concurrent `setCwd()` calls** could produce duplicate file watchers
because both calls cleared `initialized` before either completed
`initialize()`. Added an `initPromise` guard so concurrent calls await
the same initialization.
- **Debounce timer callback** could fire after `dispose()`, reloading
stale settings and calling `onChange` on a disposed manager. Added a
`disposed` flag checked in the async callback before proceeding.
Follows up on #454 which fixed the missing `dispose()` calls — this PR
hardens the `SettingsManager` lifecycle against race conditions.
## Test plan
- [x] `npm run lint` passes
- [x] All settings tests pass
- [x] Verified `disposed` flag prevents stale callback execution
- [x] Verified `initPromise` deduplicates concurrent `initialize()`
calls
## Summary
- `loadSettingsFile()` silently returned `{}` for **all** errors
including malformed JSON, permission errors, and unexpected file content
- Only `ENOENT` (file not found) should be silent since missing settings
files are expected
- Other errors now log a warning via the logger to help diagnose
misconfigured settings (e.g. a typo in `settings.json` was previously
completely invisible)
## Test plan
- [x] `npm run lint` passes
- [x] All settings tests pass
- [x] Missing files still return `{}` silently (ENOENT path unchanged)
- [x] Malformed JSON now produces a logged warning
Bumps the minor group with 2 updates:
[@anthropic-ai/claude-agent-sdk](https://github.com/anthropics/claude-agent-sdk-typescript)
and [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite).
Updates `@anthropic-ai/claude-agent-sdk` from 0.2.83 to 0.2.84
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/anthropics/claude-agent-sdk-typescript/releases"><code>@anthropic-ai/claude-agent-sdk</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v0.2.84</h2>
<h2>What's changed</h2>
<ul>
<li>Added <code>taskBudget</code> option for API-side token budget
awareness, allowing the model to pace tool use within a token limit</li>
<li>Added <code>enableChannel()</code> method and
<code>capabilities</code> field on <code>McpServerStatus</code> for
SDK-driven MCP channel activation</li>
<li>Exported <code>EffortLevel</code> type (<code>'low' | 'medium' |
'high' | 'max'</code>) for consumers to reference effort values
directly</li>
<li>Fixed showing "[Request interrupted by user]" for errors
that were not caused by user interruption</li>
<li>Updated to parity with Claude Code v2.1.84</li>
</ul>
<h2>Update</h2>
<pre lang="sh"><code>npm install @anthropic-ai/claude-agent-sdk@0.2.84
# or
yarn add @anthropic-ai/claude-agent-sdk@0.2.84
# or
pnpm add @anthropic-ai/claude-agent-sdk@0.2.84
# or
bun add @anthropic-ai/claude-agent-sdk@0.2.84
</code></pre>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/anthropics/claude-agent-sdk-typescript/blob/main/CHANGELOG.md"><code>@anthropic-ai/claude-agent-sdk</code>'s
changelog</a>.</em></p>
<blockquote>
<h2>0.2.84</h2>
<ul>
<li>Added <code>taskBudget</code> option for API-side token budget
awareness, allowing the model to pace tool use within a token limit</li>
<li>Added <code>enableChannel()</code> method and
<code>capabilities</code> field on <code>McpServerStatus</code> for
SDK-driven MCP channel activation</li>
<li>Exported <code>EffortLevel</code> type (<code>'low' | 'medium' |
'high' | 'max'</code>) for consumers to reference effort values
directly</li>
<li>Fixed showing "[Request interrupted by user]" for errors
that were not caused by user interruption</li>
<li>Updated to parity with Claude Code v2.1.84</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="ba2688c1d4"><code>ba2688c</code></a>
chore: Update CHANGELOG.md</li>
<li>See full diff in <a
href="https://github.com/anthropics/claude-agent-sdk-typescript/compare/v0.2.83...v0.2.84">compare
view</a></li>
</ul>
</details>
<br />
Updates `vite` from 8.0.2 to 8.0.3
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/vitejs/vite/releases">vite's
releases</a>.</em></p>
<blockquote>
<h2>create-vite@8.0.3</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/create-vite@8.0.3/packages/create-vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
<h2>v8.0.3</h2>
<p>Please refer to <a
href="https://github.com/vitejs/vite/blob/v8.0.3/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md">vite's
changelog</a>.</em></p>
<blockquote>
<h2><!-- raw HTML omitted --><a
href="https://github.com/vitejs/vite/compare/v8.0.2...v8.0.3">8.0.3</a>
(2026-03-26)<!-- raw HTML omitted --></h2>
<h3>Features</h3>
<ul>
<li>update rolldown to 1.0.0-rc.12 (<a
href="https://redirect.github.com/vitejs/vite/issues/22024">#22024</a>)
(<a
href="84164ef47b">84164ef</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>html:</strong> cache unfiltered CSS list to prevent missing
styles across entries (<a
href="https://redirect.github.com/vitejs/vite/issues/22017">#22017</a>)
(<a
href="5464190a3e">5464190</a>)</li>
<li><strong>module-runner:</strong> handle non-ascii characters in
base64 sourcemaps (<a
href="https://redirect.github.com/vitejs/vite/issues/21985">#21985</a>)
(<a
href="77c95bff83">77c95bf</a>)</li>
<li><strong>module-runner:</strong> skip re-import if the runner is
closed (<a
href="https://redirect.github.com/vitejs/vite/issues/22020">#22020</a>)
(<a
href="ee2c2cd2c2">ee2c2cd</a>)</li>
<li><strong>optimizer:</strong> scan is not resolving sub path import if
used in a glob import (<a
href="https://redirect.github.com/vitejs/vite/issues/22018">#22018</a>)
(<a
href="ddfe20d19e">ddfe20d</a>)</li>
<li><strong>ssr:</strong> ssrTransform incorrectly rewrites
<code>meta</code> identifier inside <code>import.meta</code> when a
binding named <code>meta</code> exists (<a
href="https://redirect.github.com/vitejs/vite/issues/22019">#22019</a>)
(<a
href="cff5f0cca5">cff5f0c</a>)</li>
</ul>
<h3>Miscellaneous Chores</h3>
<ul>
<li><strong>deps:</strong> bump picomatch from 4.0.3 to 4.0.4 (<a
href="https://redirect.github.com/vitejs/vite/issues/22027">#22027</a>)
(<a
href="7e56003fec">7e56003</a>)</li>
</ul>
<h3>Tests</h3>
<ul>
<li><strong>html:</strong> add tests for
<code>getCssFilesForChunk</code> (<a
href="https://redirect.github.com/vitejs/vite/issues/22016">#22016</a>)
(<a
href="43fbbf9a09">43fbbf9</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="6a34ac3422"><code>6a34ac3</code></a>
fix(deps): update all non-major dependencies (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/21096">#21096</a>)</li>
<li><a
href="02ceaec45e"><code>02ceaec</code></a>
chore(deps): update dependency <code>@rollup/plugin-commonjs</code> to
v29 (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/21099">#21099</a>)</li>
<li><a
href="572aaca459"><code>572aaca</code></a>
release: v7.2.2</li>
<li><a
href="728c8eeebc"><code>728c8ee</code></a>
fix: revert "refactor: use fs.cpSync (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/21019">#21019</a>)"
(<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/21081">#21081</a>)</li>
<li><a
href="a532e68c6b"><code>a532e68</code></a>
release: v7.2.1</li>
<li><a
href="82d2d6ccf5"><code>82d2d6c</code></a>
fix(worker): some worker asset was missing (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/21074">#21074</a>)</li>
<li><a
href="f83264f5d9"><code>f83264f</code></a>
refactor(build): rename <code>indexOfMatchInSlice</code> to
<code>findPreloadMarker</code> (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/21054">#21054</a>)</li>
<li><a
href="8293de0e17"><code>8293de0</code></a>
release: v7.2.0</li>
<li><a
href="2833c5576a"><code>2833c55</code></a>
fix(types): add undefined to optional properties for
exactOptionalProperties ...</li>
<li><a
href="e3a6a83406"><code>e3a6a83</code></a>
chore(deps): update rolldown-related dependencies (<a
href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/21047">#21047</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/vitejs/vite/commits/create-vite@8.0.3/packages/vite">compare
view</a></li>
</ul>
</details>
<br />
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
#463 introduced a problem with cancelling prompts. Because cancel exited
the loop early, the idle session state message was not consumed. Then,
in the next prompt, we'd immediately get the idle state and assume it's
done.
Instead, we always wait for the idle state (and still finish early when
detecting a queued prompt).
Note that this is based on my tests that showed Claude only sending the
idle state when all background agents are done.
---------
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
Bumps
[actions/download-artifact](https://github.com/actions/download-artifact)
from 7.0.0 to 8.0.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/download-artifact/releases">actions/download-artifact's
releases</a>.</em></p>
<blockquote>
<h2>v8.0.1</h2>
<h2>What's Changed</h2>
<ul>
<li>Support for CJK characters in the artifact name by <a
href="https://github.com/danwkennedy"><code>@danwkennedy</code></a> in
<a
href="https://redirect.github.com/actions/download-artifact/pull/471">actions/download-artifact#471</a></li>
<li>Add a regression test for artifact name + content-type mismatches by
<a href="https://github.com/danwkennedy"><code>@danwkennedy</code></a>
in <a
href="https://redirect.github.com/actions/download-artifact/pull/472">actions/download-artifact#472</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/download-artifact/compare/v8...v8.0.1">https://github.com/actions/download-artifact/compare/v8...v8.0.1</a></p>
<h2>v8.0.0</h2>
<h2>v8 - What's new</h2>
<blockquote>
<p>[!IMPORTANT]
actions/download-artifact@v8 has been migrated to an ESM module. This
should be transparent to the caller but forks might need to make
significant changes.</p>
</blockquote>
<blockquote>
<p>[!IMPORTANT]
Hash mismatches will now error by default. Users can override this
behavior with a setting change (see below).</p>
</blockquote>
<h3>Direct downloads</h3>
<p>To support direct uploads in <code>actions/upload-artifact</code>,
the action will no longer attempt to unzip all downloaded files.
Instead, the action checks the <code>Content-Type</code> header ahead of
unzipping and skips non-zipped files. Callers wishing to download a
zipped file as-is can also set the new <code>skip-decompress</code>
parameter to <code>true</code>.</p>
<h3>Enforced checks (breaking)</h3>
<p>A previous release introduced digest checks on the download. If a
download hash didn't match the expected hash from the server, the action
would log a warning. Callers can now configure the behavior on mismatch
with the <code>digest-mismatch</code> parameter. To be secure by
default, we are now defaulting the behavior to <code>error</code> which
will fail the workflow run.</p>
<h3>ESM</h3>
<p>To support new versions of the @actions/* packages, we've upgraded
the package to ESM.</p>
<h2>What's Changed</h2>
<ul>
<li>Don't attempt to un-zip non-zipped downloads by <a
href="https://github.com/danwkennedy"><code>@danwkennedy</code></a> in
<a
href="https://redirect.github.com/actions/download-artifact/pull/460">actions/download-artifact#460</a></li>
<li>Add a setting to specify what to do on hash mismatch and default it
to <code>error</code> by <a
href="https://github.com/danwkennedy"><code>@danwkennedy</code></a> in
<a
href="https://redirect.github.com/actions/download-artifact/pull/461">actions/download-artifact#461</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/download-artifact/compare/v7...v8.0.0">https://github.com/actions/download-artifact/compare/v7...v8.0.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="3e5f45b2cf"><code>3e5f45b</code></a>
Add regression tests for CJK characters (<a
href="https://redirect.github.com/actions/download-artifact/issues/471">#471</a>)</li>
<li><a
href="e6d03f6737"><code>e6d03f6</code></a>
Add a regression test for artifact name + content-type mismatches (<a
href="https://redirect.github.com/actions/download-artifact/issues/472">#472</a>)</li>
<li><a
href="70fc10c6e5"><code>70fc10c</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/download-artifact/issues/461">#461</a>
from actions/danwkennedy/digest-mismatch-behavior</li>
<li><a
href="f258da9a50"><code>f258da9</code></a>
Add change docs</li>
<li><a
href="ccc058e5fb"><code>ccc058e</code></a>
Fix linting issues</li>
<li><a
href="bd7976ba57"><code>bd7976b</code></a>
Add a setting to specify what to do on hash mismatch and default it to
<code>error</code></li>
<li><a
href="ac21fcf45e"><code>ac21fcf</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/download-artifact/issues/460">#460</a>
from actions/danwkennedy/download-no-unzip</li>
<li><a
href="15999bff51"><code>15999bf</code></a>
Add note about package bumps</li>
<li><a
href="974686ed50"><code>974686e</code></a>
Bump the version to <code>v8</code> and add release notes</li>
<li><a
href="fbe48b1d27"><code>fbe48b1</code></a>
Update test names to make it clearer what they do</li>
<li>Additional commits viewable in <a
href="37930b1c2a...3e5f45b2cf">compare
view</a></li>
</ul>
</details>
<br />
[](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>
According to the type docs, this should be a more reliable indicator of
turn end:
> Mirrors notifySessionStateChanged. 'idle' fires after heldBackResult
flushes and the bg-agent do-while exits — authoritative turn-over
signal.
Instead of parsing <local-command-stdout> tags from the message stream,
intercept these commands early and forward
the clean result from the success handler directly to the client.
Applies to /context, /heapdump, and /extra-usage.
## Summary
- `gatewayAuthMeta` is declared as an optional property (`undefined`
when unset), but the subscription validation compared it against `null`
- This made the condition always false, so `--hide-claude-auth` never
blocked claude.ai subscription users when no gateway was configured
- Changed `=== null` to `!this.gatewayAuthMeta` to match the actual
property semantics
## Test plan
- [x] `npm run build` compiles clean
- [x] `npm run lint` passes
- [x] All 6 authorization tests pass
## Summary
- When a Bash tool execution produced both stdout and stderr, only
stdout was shown due to the `||` operator short-circuiting
- Changed to concatenate both streams with `[stdout,
stderr].filter(Boolean).join("\n")` so error output is no longer
silently dropped
## Test plan
- [x] `npm run build` compiles clean
- [x] `npm run lint` passes
- [x] All 34 tools tests pass
Fixes#442
## Summary
- When `cancel()` was called immediately after a prompt, the result
message arrived before `promptReplayed` was set to `true`
- The `!promptReplayed` check consumed it as a "background task result",
making the `session.cancelled` check unreachable
- The loop then blocked forever on `query.next()` since no further
messages would arrive
- Fix: move the `session.cancelled` check **before** the
`!promptReplayed` check so cancellation is always detected
## Test plan
- [x] `npm run build` compiles clean
- [x] `npm run lint` passes
- [x] All unit tests pass
- [x] Logic verified against the race condition sequence described in
#442
## Summary
- `SettingsManager.dispose()` was never called when a session was closed
via `unstable_closeSession` or when the Claude Agent process died
unexpectedly
- Each leaked session left up to 4 active `fs.watch()` file descriptors
and a pending debounce timer
- Added `session.settingsManager.dispose()` in both cleanup paths and
updated test mocks to assert the call
## Test plan
- [x] `npm run build` compiles clean
- [x] `npm run lint` passes
- [x] Existing `session/close` tests updated to verify `dispose()` is
called
- [x] All 4 session/close tests pass
## Summary
The plan text was accidentally dropped from the `ExitPlanMode` content
array during the switch to built-in Claude Code tools (#316, commit
`be618f5`). This meant ACP clients could not display the plan when
asking the user for approval — they only saw the title "Ready to code?"
with no plan body.
We noticed this in the Fabriqa app where our plan approval UI was
rendering an empty card.
## What changed
Restored the original `input.plan` → `content` mapping in
`toolInfoFromToolUse()` so the plan markdown is included in the
`tool_call` and `request_permission` events.
**Before (broken):**
```typescript
case "ExitPlanMode": {
return {
title: "Ready to code?",
kind: "switch_mode",
content: [],
};
}
```
**After (fixed):**
```typescript
case "ExitPlanMode": {
const planInput = toolUse.input as { plan?: string };
return {
title: "Ready to code?",
kind: "switch_mode",
content:
planInput?.plan
? [{ type: "content" as const, content: { type: "text" as const, text: planInput.plan } }]
: [],
};
}
```
## Where does `input.plan` come from?
The Claude Agent SDK passes tool inputs to the `canUseTool` callback as
`Record<string, unknown>` (see `Options.canUseTool` in
`@anthropic-ai/claude-agent-sdk/sdk.d.ts`). When Claude calls
`ExitPlanMode`, the model includes the plan text in the input.
The formal `ExitPlanModeInput` type (in
`@anthropic-ai/claude-agent-sdk/sdk-tools.d.ts`) only declares
`allowedPrompts`, but it has a `[k: string]: unknown` index signature
that allows additional properties. The model consistently sends `plan`
(string) and `planFilePath` (string). SDK v0.2.76 explicitly added
`planFilePath` to the tool input for hooks and SDK consumers, confirming
these fields are intentional.
The original implementation (commit `0289dd9`, "Support plan mode and
all other permission modes" #40) relied on `input.plan` and worked
correctly until the refactor in #316 dropped it.
### SDK history confirms `input.plan` is reliable
The Claude Agent SDK itself had a regression where `ExitPlanMode` input
was empty
([anthropics/claude-code#12288](https://github.com/anthropics/claude-code/issues/12288)):
| Version | Status |
|---------|--------|
| Claude Code 2.0.34 | `input.plan` populated |
| Claude Code 2.0.51 | Regression — `input: {}` empty |
| Agent SDK 0.1.54 | Fixed — `input.plan` restored |
| Agent SDK 0.2.76 (current) | Working — also added `planFilePath` |
The SDK team treated the empty input as a critical bug and fixed it
promptly. The current SDK (0.2.76) reliably sends `input.plan`, so this
adapter fix is safe to depend on.
## Test plan
- [x] Added test: plan text is included in content when `input.plan` is
provided
- [x] Added test: content is empty when `input.plan` is not provided
- [x] All existing tests pass
Fixes#450
Bumps the npm_and_yarn group with 1 update in the / directory:
[flatted](https://github.com/WebReflection/flatted).
Updates `flatted` from 3.4.1 to 3.4.2
<details>
<summary>Commits</summary>
<ul>
<li><a
href="3bf09091c3"><code>3bf0909</code></a>
3.4.2</li>
<li><a
href="885ddcc33c"><code>885ddcc</code></a>
fix CWE-1321</li>
<li><a
href="0bdba705d1"><code>0bdba70</code></a>
added flatted-view to the benchmark</li>
<li>See full diff in <a
href="https://github.com/WebReflection/flatted/compare/v3.4.1...v3.4.2">compare
view</a></li>
</ul>
</details>
<br />
[](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 <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/zed-industries/claude-agent-acp/network/alerts).
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Add support for `_meta.additionalRoots` in `claude-agent-acp`.
This change introduces a top-level `additionalRoots` field in session
metadata and forwards it to Claude by merging it into
`additionalDirectories` when creating a session.
Behavior:
- reads `_meta.additionalRoots` from the ACP session request
- treats it as a `string[]`
- merges it with any user-provided
`claudeCode.options.additionalDirectories`
- passes the combined list through to Claude unchanged
This keeps `additionalRoots` as an ACP-level extension rather than a
Claude-specific option, while mapping it onto Claude's existing
`additionalDirectories` support.
## Summary
Fixes#401 by enabling model alias/fuzzy resolution in
`setSessionConfigOption` when updating the `model` config option.
## Root cause
`setSessionConfigOption` validated model values using an exact string
match against config option values (model IDs like `claude-opus-4-5`):
```ts
const validValue = allValues.find((o) => o.value === params.value);
```
Clients often send human-friendly model preferences (`opus`, `sonnet`,
`Claude Sonnet`), which failed this exact check and produced:
- `Invalid value for config option model: <value>`
The file already had `resolveModelPreference(...)`, which supports
exact/display/substring/tokenized matching, but that logic was only used
in model initialization paths and not in `setSessionConfigOption`.
## What changed
### 1) Fuzzy model resolution in `setSessionConfigOption`
- Kept the existing exact-match validation path.
- When `configId === "model"` and exact match fails, map config option
entries to `ModelInfo[]` (`value` + `displayName`) and call
`resolveModelPreference(...)`.
- If resolution succeeds, use the resolved canonical model ID
(`resolvedValue`) for:
- `query.setModel(...)`
- `session.configOptions` current value update
- response payload (`configOptions`)
### 2) Notification behavior unchanged by design
- Intentionally **did not** add `updateConfigOption(...)` in
`setSessionConfigOption`.
- This preserves existing behavior/tests that verify no
`config_option_update` notification is sent for this RPC (response
already returns full `configOptions`).
## Tests
Added focused coverage in `src/tests/session-config-options.test.ts`
under `describe("setSessionConfigOption")`:
- resolves model alias `opus` -> `claude-opus-4-5`
- resolves model alias `sonnet` -> `claude-sonnet-4-5`
- resolves display name `Claude Sonnet` -> `claude-sonnet-4-5`
- still accepts exact model ID
- still throws for invalid model value (`gpt-4`)
## Behavior before / after
### Before
- `setSessionConfigOption({ configId: "model", value: "opus" })` ->
error
### After
- same call resolves to canonical ID and succeeds:
- `setModel("claude-opus-4-5")`
- returned `configOptions.model.currentValue === "claude-opus-4-5"`
## Verification
- `npm run build` ✅
- `npm test` ✅ (all tests passing)
Closes#415
Add `getOrCreateSession` so resume/load returns existing session config
without recreating the session. Replace ad-hoc permission tracking with
`modes.currentModeId` and centralize config updates through
`syncSessionConfigState` to keep `modes` and `models` in sync with
config option changes. Update tests for the new session shape.
Adds a new `ExitPlanMode` choice so users can switch directly to
`bypassPermissions` after accepting a plan, instead of only
`acceptEdits` or `default`.
### Changes
- Updated the `ExitPlanMode` permission options in `src/acp-agent.ts` to
conditionally include:
- `Yes, and bypass permissions` (`optionId: "bypassPermissions"`)
- Reused existing `ALLOW_BYPASS` guard so the option is only shown when
bypass is supported.
- Updated selection handling so `bypassPermissions` is treated as a
valid accepted outcome and applies mode updates like the existing paths.
Followup for
https://github.com/zed-industries/claude-agent-acp/pull/386.
When sending `/compact`, there's no replay for the user message, so we
did not resolve the prompt. Same for `/context`.
I tried what happens when sending a `/context` or `/compact` while
another prompt is running and Claude does not properly handle it.
Instead, it responds with something like
> I also noticed you sent /compact while I was working. That's a
built-in CLI command (not a skill), which compacts the conversation
context. It should be invoked directly in the CLI rather than as a
message to me.
The important part is
f52f214937.
## Summary
- Pass through `_meta.claudeCode.options.tools` to the Claude Agent SDK
instead of hard-coding `{ type: "preset", preset: "claude_code" }`
- Reimplement `disableBuiltInTools` as `tools: []` instead of pushing 14
tool names into `disallowedTools`
- Explicit `tools` array takes precedence over `disableBuiltInTools`
## Problem
The ACP adapter hard-coded `tools: { type: "preset", preset:
"claude_code" }`, ignoring any value the caller provided via
`_meta.claudeCode.options.tools`. This caused two problems:
1. **Wasted context tokens** — All ~15 tool schemas were always sent to
the model, even when the caller only needed a subset (e.g., Read and
Glob for a review-only workflow) or none at all (MCP-only sessions).
2. **Rejected tool call round trips** — When `disableBuiltInTools: true`
was used, the tools were still registered in the model's context (the
flag only added them to `disallowedTools`). The model would see the tool
schemas, attempt to call them, get rejected, and waste a round trip.
Additionally, the existing `disableBuiltInTools` flag maintained a
hard-coded list of 14 tool names to blocklist. If the SDK added a new
built-in tool, `disableBuiltInTools` wouldn't cover it — the new tool
would leak through.
## SDK Contract Validation
The `tools` option is documented in the [Claude Agent SDK TypeScript
reference](https://platform.claude.com/docs/en/agent-sdk/typescript) as:
```typescript
tools?: string[] | { type: 'preset'; preset: 'claude_code' };
```
> Tool configuration. Pass an array of tool names or use the preset to
get Claude Code's default tools.
The type `string[]` includes `[]` (empty array) with no minimum length
constraint. Passing an empty array to disable all built-in tools is a
valid use of the SDK contract, not a workaround.
## Changes
### `src/acp-agent.ts`
**One line of behavioral change** — the `tools` field now uses the
caller-provided value if present, falling back to the preset:
```typescript
tools: userProvidedOptions?.tools ?? { type: "preset", preset: "claude_code" },
```
**`disableBuiltInTools` reimplemented** — instead of pushing 14 tool
names into `disallowedTools`, it resolves to `tools: []`:
```typescript
const resolvedTools: Options["tools"] =
userProvidedOptions?.tools ??
(params._meta?.disableBuiltInTools === true
? []
: { type: "preset", preset: "claude_code" });
```
Resolution priority:
```
_meta.claudeCode.options.tools → use it (explicit whitelist)
_meta.disableBuiltInTools: true → tools: [] (legacy shorthand)
neither → { type: "preset", preset: "claude_code" } (default)
```
### `src/tests/create-session-options.test.ts`
4 new tests + 1 updated test:
| Test | Verifies |
|------|----------|
| `defaults tools to claude_code preset when not provided` | Backward
compatibility — no `tools` = full preset |
| `passes through user-provided tools string array` | Whitelist:
`["Read", "Glob"]` arrives as-is |
| `passes through empty tools array to disable all built-in tools` |
`tools: []` disables everything |
| `explicit tools array takes precedence over disableBuiltInTools` |
Resolution priority: explicit `tools` wins over `disableBuiltInTools` |
| `sets tools to empty array when disableBuiltInTools is true` | Legacy
flag now produces `tools: []` instead of blocklist |
## Usage Examples
```typescript
// Review-only session: model can only read files
_meta: { claudeCode: { options: { tools: ["Read", "Glob"] } } }
// MCP-only session: no built-in tools
_meta: { claudeCode: { options: { tools: [] } } }
// Legacy: still works, now equivalent to tools: []
_meta: { disableBuiltInTools: true }
```
## Non-breaking
Callers that don't provide `tools` get identical behavior (full
`claude_code` preset). `disableBuiltInTools: true` still works but is
now a less flexible alias for `tools: []`.
## Test plan
- [x] All existing tests pass (`npm test` — 131 tests, 126 passed, 5
skipped)
- [x] New tests cover: default preset, explicit array, empty array,
precedence over `disableBuiltInTools`
- [x] Verified `tools` type against [official SDK
docs](https://platform.claude.com/docs/en/agent-sdk/typescript)
---------
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
## Summary
- Tool call titles (Read, Write, Edit) currently display absolute file
paths like `/Users/jane/my-project/src/main.ts`, which are long and
redundant since the user already knows their project root.
- This PR converts those paths to project-relative display paths (e.g.,
`src/main.ts`) by comparing against the session's `cwd`. Paths outside
the project directory remain absolute.
- The `locations.path` field is **not** changed, it stays absolute for
Zed's file navigation.
## Test plan
- [x] Added unit tests for `toDisplayPath` (relativizes paths inside
cwd, keeps absolute paths outside, handles partial directory name
matches)
- [x] Added unit test for `toolInfoFromToolUse` with `cwd` parameter
(verifies title uses relative path, locations stay absolute)
- [x] All 124 existing tests pass
Closes#381.
Relates to #348.
In #348 I added replay to handle queued prompts.
In Tidewave, we ignore user_message_chunks, so I did not experience the
issue when testing. But clients that show the user_message_chunk like
Zed showed the replayed content. This commit ensures that replayed
messages are skipped.
## Summary
- The Claude SDK renamed the `Task` tool to `Agent`, but
`toolInfoFromToolUse` only matched `case "Task":`, so the `Agent` tool
fell through to the `default` case — rendering as a bare "Agent" label
with no description or content in the chat UI.
- Adds `case "Agent":` as a fallthrough before `case "Task":` so both
names are handled identically.
## Test plan
- [ ] Verify that when Claude spawns a subagent, the tool call in Zed's
chat shows the agent's description as the title (not a bare "Agent"
label)
In 0.20.0, the env option was introduced with an explicit object spread,
but without including process.env. This caused the subprocess to receive
an empty environment, stripping HOME, PATH and other inherited variables
when spawned from an external editor such as Zed.
Add ...process.env as the base so user-provided and gateway env vars
still override it, while preserving the parent process environment.
Add tests to verify HOME/PATH are inherited and that user-provided vars
are merged on top correctly.
Fixes#373
---------
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
Updated authentication methods for IDEA compatibility and SDK compliance
#### New authentication type: gateway (url + custom headers)
- Required to support native IDEA authentication flows
#### Process argument to hide Claude Code authentication
- Ensures compliance with Anthropic’s third-party authentication
policies.
https://platform.claude.com/docs/en/agent-sdk/overview
> Unless previously approved, Anthropic does not allow third party
developers to offer claude.ai login or rate limits for their products,
including agents built on the Claude Agent SDK. Please use the API key
authentication methods described in this document instead.
---------
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
As seen in https://github.com/zed-industries/zed/issues/39563, Claude
Agent child processes can die mid-session (crash, signal, OOM, et al).
When that happens, the SDK throws errors with internal messages like
`"ProcessTransport closed"`, `"Failed to write to process stdin"`, or
`"process terminated by signal"`. These would bubble up to the user
as-is, which is not helpful.
At this point, it’s [not possible to continue the
session](https://github.com/zed-industries/zed/issues/39563#issuecomment-3776814727)
either (which I also confirmed myself), but that is not obvious from the
error the user sees (screenshot on the left).
In my testing, I could not get the `Error: ProcessTransport is not ready
for writing` error, but you can easily trigger the `SIGTERM` (exit code
143) by simply killing the `claude` child process spawned by the ACP
adapter while the agent is streaming a response:
<img width="1621" height="54" alt="image"
src="https://github.com/user-attachments/assets/29d989f1-7e56-44b3-992d-065218444958"
/>
In this case, you’d do `kill 74573` to trigger the error.
AFAICT, the child process is managed internally by the Claude Agent SDK
(inside the `query` object returned by `query()`). The ACP agent only
sees it as an `AsyncGenerator`. There’s no `process.pid` or
`ChildProcess` handle exposed to check liveness against. The only way to
discover the process is dead is when the SDK throws one of these
transport-level errors*.
\*I got these error strings by asking Opus 4.6 to de-obfuscate
(`node_modules/@anthropic-ai/claude-agent-sdk/sdk.mjs`)
| Before | After |
|--------|--------|
| <img width="466" height="62" alt="image"
src="https://github.com/user-attachments/assets/dc959924-0929-4587-9ddd-4a3873f1fa5d"
/> | <img width="462" height="77" alt="image"
src="https://github.com/user-attachments/assets/77eff880-28da-489a-b9e3-e9f93abf3c7f"
/> |
In other words, I think the best we (the ACP adapter) can do here is to
grep for these transport errors and surface a _better_ error message to
the user, as seen above.
---------
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
## Summary
- When `loadSession` calls `createSession({ resume: sessionId })` and
the CLI subprocess can't find persisted session data (no `.jsonl` file),
the subprocess exits immediately and `createSession` rejects with a
generic `-32603` ("Query closed before response received")
- ACP clients like [acpx](https://github.com/openclaw/acpx) check for
`-32001`/`-32002` (resource not found) to fall back to `session/new` —
the generic `-32603` bypasses this, causing the turn to fail entirely
- This commonly happens when `session/new` creates an ACP session but
the subprocess exits before writing session data (no prompt to process),
then `session/prompt` tries `session/load` to resume a session that was
never persisted
## Fix
Wrap the `createSession({ resume })` call in `loadSession()` with
try/catch and re-throw as `RequestError.resourceNotFound()` (code
`-32002`). This lets callers handle it with their existing fallback
logic.
## Verification
Confirmed working in production with the
[OpenClaw](https://github.com/openclaw/openclaw) gateway + acpx
integration. Before the fix, all ACP tasks failed silently. After
patching both this code path and acpx's `shouldFallbackToNewSession()`
([openclaw/acpx#30](https://github.com/openclaw/acpx/pull/30)), sessions
fall back to `session/new` correctly.
Fixes#338Closes#361Closes#362
---------
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
## Summary
- `@agentclientprotocol/sdk@0.14.1` declares `zod` as a peer dependency
(`"^3.25.0 || ^4.0.0"`) but this package does not include it as a direct
dependency
- On clean installs (e.g. Zed's external agent directory), `zod` is not
hoisted from any other package, causing `ERR_MODULE_NOT_FOUND` on every
agent start
- Users must manually `npm install zod` after every update as a
workaround
## Fix
Add `"zod": "^3.25.0 || ^4.0.0"` to `dependencies` to match the SDK's
peer dependency range.
Fixes#357
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
## Summary
- When the Claude SDK reports `stop_reason: "max_tokens"`, the ACP
adapter now returns `{ stopReason: "max_tokens" }` instead of throwing a
`RequestError.internalError` (JSON-RPC `-32603`)
- Scoped to `success` and `error_during_execution` result subtypes only
— `error_max_turns` / `error_max_budget_usd` still correctly map to
`max_turn_requests`
## Problem
When tokens are exhausted, the SDK sends a `result` message with
`stop_reason: "max_tokens"` and `is_error: true`. The adapter hit the
`is_error` check first and threw an internal error, so ACP clients never
received the `stopReason` field. Downstream tools like
[toad](https://github.com/batrachianai/toad/discussions/176#discussioncomment-15445958)
saw a JSON-RPC exception instead of a clean stop reason.
## Fix
Check `stop_reason === "max_tokens"` before the `is_error` throw in the
`success` and `error_during_execution` branches. Token exhaustion is a
normal stop condition, not an internal error.
## Test plan
- [x] Added 5 unit tests covering:
- `success` + `max_tokens` + `is_error: false` → returns `max_tokens`
- `success` + `max_tokens` + `is_error: true` → returns `max_tokens`
(main bug)
- `error_during_execution` + `max_tokens` + `is_error: true` → returns
`max_tokens`
- `success` + `null` stop_reason → still returns `end_turn` (no
regression)
- `success` + `is_error: true` + non-max_tokens → still throws (no
regression)
- [x] All 100 tests pass
- [x] Build passes
Fixes#235🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Most user settings (`env`, permission rules like `allow`/`deny`/`ask`)
already work through the SDK's `settingSources` or are handled by the
CLI subprocess. Two settings require explicit handling by the ACP agent
and were not being respected:
**`permissions.defaultMode`**: Must be passed as the `permissionMode`
option to `query()`. The SDK defaults to `"default"` internally and does
not read it from settings files. The CLI handles this by reading the
setting and passing it to the SDK; `createSession` was hardcoding it to
`"default"` instead.
**`model`**: The CLI persists display strings like `"opus[1m]"` in
settings, but the SDK model list uses IDs like `"claude-opus-4-6-1m"`.
The existing substring matching in `getAvailableModels` couldn't resolve
these aliases, so they silently fell back to the first model. Passing
the raw alias to `query.setModel()` doesn't work either — the CLI's
internal `dg()` function strips `[1m]` and loses the context hint.
Changes:
- Fix pre-existing build errors from SDK type changes
(`SDKTaskStartedMessage` removed, `SDKMessageTemp` replaced with SDK's
own `SDKMessage`)
- Add `permissions.defaultMode` to `ClaudeCodeSettings`, merge it
through the settings hierarchy, and resolve it via
`resolvePermissionMode()` with case-insensitive aliases, validation, and
root-user guard
- Add tokenized model matching (`resolveModelPreference`) that handles
CLI display strings like `"opus[1m]"` by scoring against the available
models list
- Add unit and integration tests for both features
Co-authored-by: Codex <codex@openai.com>
---------
Co-authored-by: Tim Visher <194828183+timvisher-dd@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Codex <codex@openai.com>
This basically implements the pending prompt queueing RFD. When a prompt
request is received while the agent is already generating, we
immediately forward the message to Claude and then wait until it replays
the message back to us. When it replays it, we respond to the previous
prompt request with "end_turn", which also singifies to the client that
the message is now being processed by the agent.
See
https://github.com/agentclientprotocol/agent-client-protocol/pull/484.
* Update claude-agent-sdk to 0.2.96 ([#526](https://github.com/agentclientprotocol/claude-agent-acp/issues/526)) ([c073131](https://github.com/agentclientprotocol/claude-agent-acp/commit/c07313148808a55f27f385c10babbaf7511a7f12))
### Bug Fixes
* Remove bun builds from release ([#525](https://github.com/agentclientprotocol/claude-agent-acp/issues/525)) ([fcf5aaf](https://github.com/agentclientprotocol/claude-agent-acp/commit/fcf5aaf06dfe9f7d1b285b976eeb2d1e20ea8dec))
* Use TUI login for remote environments ([#523](https://github.com/agentclientprotocol/claude-agent-acp/issues/523)) ([cc73e37](https://github.com/agentclientprotocol/claude-agent-acp/commit/cc73e37b41678aa67813c4fbef66ba33ca538743))
* Drop claude-agent-sdk back to 0.2.91 to fix broken import ([#513](https://github.com/agentclientprotocol/claude-agent-acp/issues/513)) ([26f3e8a](https://github.com/agentclientprotocol/claude-agent-acp/commit/26f3e8a5216295985fadb80fb3b977045c0c1b2c))
* Recreate resumed sessions when params change ([#515](https://github.com/agentclientprotocol/claude-agent-acp/issues/515)) ([aa82193](https://github.com/agentclientprotocol/claude-agent-acp/commit/aa82193330026bae132fed8391c47dde777dcf5a))
* prioritize ANTHROPIC_MODEL env var over settings.model in model … ([#505](https://github.com/agentclientprotocol/claude-agent-acp/issues/505)) ([bea1a40](https://github.com/agentclientprotocol/claude-agent-acp/commit/bea1a40e9bfe1e06672b118a727f9339def3be23))
* add `auto` to valid modes in applySessionMode to fix mode cycling ([#507](https://github.com/agentclientprotocol/claude-agent-acp/issues/507)) ([15e91fb](https://github.com/agentclientprotocol/claude-agent-acp/commit/15e91fb5c3449de2600b583cb7a8d36b5b510443))
* Add auto permission mode support ([#501](https://github.com/agentclientprotocol/claude-agent-acp/issues/501)) ([a161453](https://github.com/agentclientprotocol/claude-agent-acp/commit/a16145396fe2e6ead8734478961b2de65707e335))
* Add separate Claude and Console terminal logins ([#502](https://github.com/agentclientprotocol/claude-agent-acp/issues/502)) ([063cd35](https://github.com/agentclientprotocol/claude-agent-acp/commit/063cd353809df8f3dda9e57d8ea848c0a6d44257))
* Update to claude-agent-sdk 0.2.91 ([#500](https://github.com/agentclientprotocol/claude-agent-acp/issues/500)) ([65a2230](https://github.com/agentclientprotocol/claude-agent-acp/commit/65a223038576d72b74e1483fed10e982a1f842bd))
### Bug Fixes
* log warnings for malformed settings files instead of silent fallback ([#486](https://github.com/agentclientprotocol/claude-agent-acp/issues/486)) ([ae6c388](https://github.com/agentclientprotocol/claude-agent-acp/commit/ae6c38831415f9fc1de2d3dd1d4a247becbbd32f))
* prevent race conditions in SettingsManager setCwd and debounce ([#485](https://github.com/agentclientprotocol/claude-agent-acp/issues/485)) ([7506223](https://github.com/agentclientprotocol/claude-agent-acp/commit/7506223cffb1aba4b4560feda11f69a1395a8c9d))
* use current model's context window for usage_update size ([#412](https://github.com/agentclientprotocol/claude-agent-acp/issues/412)) ([d07799d](https://github.com/agentclientprotocol/claude-agent-acp/commit/d07799d7b3b4e438c8b158266c79723a0b592c07))
* Cleanup based on new idle state [#463](https://github.com/agentclientprotocol/claude-agent-acp/issues/463) ([#480](https://github.com/agentclientprotocol/claude-agent-acp/issues/480)) ([23b3073](https://github.com/agentclientprotocol/claude-agent-acp/commit/23b30730253752f0bc4e30b619a6236f16fafdb9))
## 0.24.0
Rename from `@zed-industries/claude-agent-acp` to `@agentclientprotocol/claude-agent-acp`.
We are moving this to the main ACP org to better allow multiple teams to contribute and maintain this adapter.
## 0.23.1
- Add back error_during_execution break point (#469)
## 0.23.0
- Use idle session state as end of turn (#463)
- Update claude-agent-sdk to 0.2.83 (#462)
- Fix handling of local-only slash commands (#432)
- fix: correct null check for gatewayAuthMeta in subscription validation (#455)
- fix: include both stdout and stderr in Bash tool output (#456)
- fix: prevent prompt loop hang when cancel races with first result (#458)
- fix: dispose SettingsManager on session close to prevent resource leaks (#454)
- fix: restore plan content in ExitPlanMode tool call (#451)
## 0.22.2
- Add experimental meta param for testing additional directories
## 0.22.1
- Fix: invalid auth required state in gateway mode
## 0.22.0
- Use stable list sessions method (#429)
- Use correct Claude CLI path for static binaries (#428)
- Update claude-agent-sdk to 0.2.76 (#427)
- fix: resolve model aliases in setSessionConfigOption (#401) (#403)
- Reuse existing sessions for load/resume if possible (#426)
- Remove interrupt flag from deny responses (#425)
- Allow Bypass permissions mode after Exiting plan (#410)
- Don't get out of sync when background task creates new init/result (try 2) (#400)
- Add session/close support (#409)
## 0.21.0
- Update to claude-agent-sdk 0.2.71
- show project-relative paths in tool call titles
- Lib: pass through tools array to control built-in tool availability
- fix: skip user replay
- fix: handle renamed Agent tool in toolInfoFromToolUse
## 0.20.2
- Update to @anthropic-ai/claude-agent-sdk@0.2.68
## 0.20.1
- fix: inherit process.env when spawning agent subprocess
## 0.20.0
- Update to @anthropic-ai/claude-agent-sdk@0.2.63
- Respect user settings for permission mode and model selection
- Better handling of concurrent prompts
- Support --cli for node as well
- Propagate max_tokens stop reason instead of throwing internal error
- fix: throw resourceNotFound when loadSession fails to resume
- fix: add missing zod dependency
- Surface better error message when Claude Code process exits unexpectedly
## 0.19.2
- Fix for broken notifications when reloading session messages
## 0.19.1
- Support windows arm builds and clean up artifact files
Use [Claude Agent SDK](https://platform.claude.com/docs/en/agent-sdk/overview#branding-guidelines) from [ACP-compatible](https://agentclientprotocol.com) clients such as [Zed](https://zed.dev)!
Use [Claude Agent SDK](https://platform.claude.com/docs/en/agent-sdk/overview#branding-guidelines) from [ACP-compatible](https://agentclientprotocol.com) clients!
This tool implements an ACP agent by using the official [Claude Agent SDK](https://platform.claude.com/docs/en/agent-sdk/overview), supporting:
@@ -18,41 +18,8 @@ This tool implements an ACP agent by using the official [Claude Agent SDK](https
Learn more about the [Agent Client Protocol](https://agentclientprotocol.com/).
## How to use
## Contribution Policy
### Zed
This project does not require a Contributor License Agreement (CLA). Instead, contributions are accepted under the following terms:
The latest version of Zed can already use this adapter out of the box.
To use Claude Agent, open the Agent Panel and click "New Claude Agent Thread" from the `+` button menu in the top-right:
Read the docs on [External Agent](https://zed.dev/docs/ai/external-agents) support.
### Other clients
Or try it with any of the other [ACP compatible clients](https://agentclientprotocol.com/overview/clients)!
#### Installation
Install the adapter from `npm`:
```bash
npm install -g @zed-industries/claude-agent-acp
```
You can then use `claude-agent-acp` as a regular ACP agent:
```
ANTHROPIC_API_KEY=sk-... claude-agent-acp
```
#### Single-file executable
Pre-built single-file binaries are available on the [Releases](https://github.com/zed-industries/claude-agent-acp/releases) page for Linux, macOS, and Windows.
These binaries bundle everything needed and don't require Node.js.
## License
Apache-2.0
> By contributing to this project, you agree that your contributions will be licensed under the [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0). You affirm that you have the legal right to submit your work, that you are not including code you do not have rights to, and that you understand contributions are made without requiring a Contributor License Agreement (CLA).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.