mirror of
https://github.com/agentclientprotocol/claude-agent-acp.git
synced 2026-10-27 04:30:59 +00:00
77ceab0339a70e6d2fc1073e02f5e04ebef15e8a
734 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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 "es6" <code>target</code>. From now on, I intend to freely use any features that are deemed "Widely available" 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 "special characters", 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 "undefined" 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 "extended headers", 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 "trailing garbage"</strong></p> <p>That is: after a patch, or between files/indexes in a patch, it is now acceptable to have arbitrary lines of "garbage" (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 "garbage" lines not understood by generic unified diff parsers. (For example, SVN patches can include "Property changes on:" 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 "garbage".</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="v0.82.1-preview.1 |
||
|
|
18de376240 |
chore(main): release 0.82.0 (#1180)
🤖 I have created a release *beep* *boop* --- ## [0.82.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.81.2...v0.82.0) (2026-09-28) ### Features * AIR tool call contract, exact git patches, plan file, and fixes for every client ([#1153](https://github.com/agentclientprotocol/claude-agent-acp/issues/1153)) ([v0.82.0 |
||
|
|
875d75f2a4 |
feat: AIR tool call contract, exact git patches, plan file, and fixes for every client (#1153)
## Summary One PR with separate commits (the earlier stack #1168 and #1169 is folded back in). ### Exact git patches - `src/diff.ts` builds exact git patches for Edit and Write: real line numbers, file modes, CRLF, the final-newline marker, a size limit (including a bound for `replace_all`), binary detection, and an asynchronous diff with a time budget. The approval preview mirrors the input normalisation of the bundled CLI. - No creation patch over an existing file or for an Edit of an existing empty file, and no patch for a Write that changed nothing. ### For every client Clients that do not declare `_meta.jetbrains.air` (Zed, plain ACP clients) get the same information as on `main`. - A `tool_call_update` omits top-level fields that did not change since the last report of the same tool call (ACP merge semantics). The `_meta` of each report stays complete. - Bug fixes: a failed approval preview no longer blanks the approval diff; a permission denial replaces a pinned approval patch; the compaction summary is not repeated when the streamed text is identical; streamed subagent text is not sent twice; async task fields are recorded only after a successful send; late updates of a finished native subagent are dropped; the updates of a fresh Claude context after a clear-context restart reach the ACP session. ### For AIR only - One fact in one field: input in `rawInput`, the file text of an edit only in the diff (`diffPatch`), output once, no display copies of the input (`rawInputRendering`), `_meta.claudeCode` reduced to `toolName`, `parentToolUseId` and the `toolResponse` markers, per-key `_meta` filtering. - Plan file mode (`planFile`): ExitPlanMode carries only `rawInput.planFilePath`; AIR follows the file that Claude writes. - AIR-only keys move under `_meta.jetbrains.air.*` and are no longer sent to other clients. ### Structure and docs - `src/tool-calls/`: one reporter per tool, one `ClientCapabilities`, one `AcpToolCallRenderer`, the field tracker and the changed-meta filter. - `docs/air-extensions.md` replaces the per-extension AIR docs. ## Tests - `npm run build`, `npm run check`, `npm run test:run`: 1995 passed. - Scenario tests for plain, Zed, and AIR clients: the SDK mock honours the adapter's session id, every update must reach its ACP session, every message is validated against the ACP JSON schema with all formats checked, plain and Zed are compared with a recorded `main` baseline, AIR keeps compact golden recordings.v0.81.3-preview.2 |
||
|
|
e6681d2a57 |
fix: use ACP auth errors without access updates (#1179)
Authentication failures now use the ACP authRequired error only. The adapter does not send a second access failure or login text as a chat message. Provider verification and cloud credential errors use the service category because signing in cannot resolve them. Checks: npm run check; npm run build; npm run test:run (1468 passed, 28 skipped).v0.81.3-preview.1 |
||
|
|
5dbb453c63 |
chore(main): release 0.81.2 (#1171)
🤖 I have created a release *beep* *boop* --- ## [0.81.2](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.81.1...v0.81.2) (2026-09-24) ### Bug Fixes * announce resumed subagent generations when they start ([#1173](https://github.com/agentclientprotocol/claude-agent-acp/issues/1173)) ([v0.81.2 |
||
|
|
b495e55a22 |
fix: announce resumed subagent generations when they start (#1173)
### Summary Fixes #1158. When a native subagent session is resumed for subsequent turn execution (`task_started`), `this.parentByToolUse.get(task.toolUseId)` returns `undefined` because `finishTask` deletes the mapping at the completion of earlier generations. Because `knownParentSessionId` evaluated to `undefined`, `if (knownParentSessionId || !task.toolUseId)` evaluated to `false`, postponing the subagent announcement until `finishTask`. ### Changes - In `src/native-subagents.ts`: Added fallback `const knownParentSessionId = this.parentByToolUse.get(task.toolUseId) ?? previous?.parentSessionId;` so resumed subagents recognize their existing session parent at `task_started`. - In `src/tests/native-subagents.test.ts`: Added unit test confirming `subagent_spawned` is emitted immediately upon `taskStarted` for resumed sessions. ### Verification - `npm test src/tests/native-subagents.test.ts` (all 8 tests passing). - `npx tsc --noEmit` (clean, 0 type errors). --------- Co-authored-by: shobhitagnihotri69 <shobhitagnihotri69@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>v0.81.2-preview.2 |
||
|
|
a2c362c948 |
fix: continue clear-context plans approved in a background followup (#1170)
Fixes #1167. ## Problem In plan mode the model often runs Explore agents in the background. The user turn's result then comes back while those agents are still running, so `session/prompt` is held open (#864/#866). When an agent finishes, the model writes the plan and calls `ExitPlanMode` in the **task-notification followup**. The user accepts it with a clear-context option. The interrupted cycle's `[ede_diagnostic] … stop_reason=tool_use` result carries that cycle's `task-notification` origin. That sends it into the autonomous-result branch, which: 1. sends the `usage_update` with `_claude/origin: task-notification`. This is the "background task finished at the same time" update from the report; it's actually the same cycle. 2. settles the held turn with `end_turn` and returns early, before the ExitPlanMode restart check runs. The plan is never continued, the mode stays `plan`, and nothing tells the client. ## Fix - The diagnostic match now lives in one shared function, `isExitPlanInterruptionResult()` in `exit-plan.ts`. Both the user-result path and the new check call it; the user path behaves as before. - In the autonomous branch, when the active turn is held and the result is the recorded ExitPlanMode interruption: - **Clear-context option:** drop the held turn's stored outcome, then run the usual fresh-context restart. The fresh query doesn't have the old background tasks, so the stale hold would otherwise settle the moved turn with `end_turn` before the plan runs. The continuation's own result settles the turn. - **"No, keep planning":** settle `cancelled` with the user turn's own usage, through `settleOrDefer`, so the turn stays open while other spawned subagents are still running. ## Tests There are two new tests in the deferred-settlement block. Each runs a held turn whose task-notification followup calls `ExitPlanMode`: one for the clear-context restart, one for keep-planning. The restart test also fails if the held outcome isn't dropped. ## Known limitations (unchanged by this PR) - **Background agents are killed by the restart.** Upstream `/clear` keeps background agent tasks running (`hasAgentTaskSurvivingClear`). The adapter's restart closes the old query, so any other running subagents are killed, just as on the existing user-turn restart path. - **Plans approved after the turn has ended are still dropped.** If `ExitPlanMode` runs after the turn has already settled, there's no open turn to continue, and the plan is dropped as before. A client-visible notice for that case would be a follow-up. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>v0.81.2-preview.1 |
||
|
|
b264b52bee |
chore(main): release 0.81.1 (#1162)
🤖 I have created a release *beep* *boop* --- ## [0.81.1](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.81.0...v0.81.1) (2026-09-23) ### Bug Fixes * gate login result text on error state ([#1103](https://github.com/agentclientprotocol/claude-agent-acp/issues/1103)) ([v0.81.1 |
||
|
|
da11f1feb2 |
fix: include model on usage updates (#1032)
## Summary - include the current assistant model id on `usage_update` notifications as `model` - mirror the same value in `_meta["_claude/model"]` so clients that prefer extension metadata can consume it - preserve existing `_meta` entries such as `_claude/origin` and rate-limit metadata Fixes #1021 ## Verification - `npm run build` - `npm run check` - `npm run test:run -- src/tests/acp-agent.test.ts -t 'usage_update|effective model|origin handling'` - `npm run test:run` *(fails only in existing root-environment `resolve-permission-mode` expectations: `bypassPermissions` falls back to `default` when run as root; unrelated to usage updates)* --------- Signed-off-by: Emre Koca <110906681+kocaemre@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>v0.81.1-preview.13 |
||
|
|
df38cccae0 |
fix: recreate resumed sessions when any session option changes (#1097)
A warm `session/resume` or `session/load` currently ignores changed `maxTurns` and `maxBudgetUsd`: the session fingerprint compares only cwd and MCP servers, so the SDK query retains its old options. A client can request a tighter limit and receive success while the old limit remains active. Include these two SDK execution limits in the session fingerprint. Adding, changing or removing a limit recreates the query with the supplied options and the same session ID. Identical limits reuse the existing query; object key order does not trigger recreation. Budget accounting across recreated SDK processes remains the client's responsibility. Validation: - Four regression cases failed before the fix; six new cases cover both methods, both limits, tightening/removal, and unchanged reuse. - `npm run check` and `npm run build` passed. - Focused suites: 551 passed, 9 skipped. - Full suite with `--maxWorkers=2`: 1,240 passed, 27 skipped. An initial unrestricted run hit one existing provider-switch test's 5s timeout and macOS `EMFILE` watcher errors; bounding workers passed without changing tests or timeouts. Prepared while integrating typed runtime limits in BinaryBourbon/fountain#1732. No live SDK budget-enforcement claim is made by these adapter tests. --------- Signed-off-by: Jake Gaylor <jhgaylor@gmail.com> Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>v0.81.1-preview.12 |
||
|
|
39062b63ff |
fix: preserve context usage when quota is exhausted (#1132)
Synthetic usage-limit frames from the Claude SDK carry an all-zero usage object. When one follows a real assistant response, the ACP bridge currently overwrites the latest context measurement and emits a final `usage_update` with `used: 0`. Keep the previous measurement for a synthetic usage-limit frame when one exists. A fresh quota-limited turn remains zero. Includes a regression test covering a 170,910-token context followed by a synthetic spend-limit frame. Verified with: - `npx vitest run src/tests/acp-agent.test.ts` - `npm run check` - `npm run build` --------- Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>v0.81.1-preview.11 |
||
|
|
29a4e98477 |
fix: preserve native Vertex endpoint defaults (#1104)
## Summary - keep `ANTHROPIC_VERTEX_BASE_URL` unset when `providers/set` selects Vertex's native default endpoint - retain explicit `ANTHROPIC_VERTEX_BASE_URL` only for custom Vertex gateways/endpoints - add a regression test for the default Vertex endpoint env mapping Closes #1087 ## Test Plan - `npm run test:run -- src/tests/providers.test.ts` — passed (21 tests) - `npm run check` — passed (`eslint src --ext .ts` + `prettier --check .`) - `npm run build` — passed (`tsc`) - `git diff --check origin/main..HEAD` — passed Note: `claude -p` delegation was attempted for candidate discovery but this cron shell's Claude Code auth is not logged in (`Not logged in · Please run /login`), so the implementation/validation above was done directly with local tests. Signed-off-by: Emre K <110906681+kocaemre@users.noreply.github.com>v0.81.1-preview.10 |
||
|
|
6e97e071c5 |
fix: gate login result text on error state (#1103)
## Summary - Fixes #1092 by treating `Please run /login` in a `success` result as auth-required only when the result is actually error-shaped. - Applies the same error-state guard to goal-command failure detection so quoted login text in a normal answer does not roll back the active goal. - Adds a regression test for a successful model answer that mentions `/login` without publishing a session failure. ## Test Plan - [x] `npm run test:run -- src/tests/acp-agent.test.ts -t 'login|auth_required'` - [x] `npm run build` - [x] `npm run check` - [ ] `npm run test:run` (blocked locally by pre-existing root permission-mode assertions: `resolvePermissionMode("bypassPermissions")` and `resolvePermissionMode("bypass")` resolve to `default` when running as root) Signed-off-by: Emre K <110906681+kocaemre@users.noreply.github.com>v0.81.1-preview.9 |
||
|
|
2b0201a801 |
test: tolerate minute-aligned scheduled wakeups (#1044)
## Summary - allow the scheduled-wakeup live audit to wait through the full minute-alignment window - keep a small buffer for the autonomous model turn to complete - raise the Vitest case timeout accordingly `ScheduleWakeup(delaySeconds=60)` is persisted as a minute-aligned cron entry. Depending on where scheduling lands within a minute, delivery can take close to two minutes. The previous 75-second predicate timeout can therefore fail even though the idle ACP stream delivers the wakeup correctly. I reproduced both outcomes against current `main`: one run timed out at 75 seconds, while a second run delivered `LIVE_WAKE_OK` through the idle ACP stream. Related: #838 ## Verification - `npm run check` - `npm run build` - `npm run test:run` (971 passed, 27 skipped) - live SDK/ACP probe on Agent SDK 0.3.238 observed `SCHEDULED` followed by autonomous `LIVE_WAKE_OK` while idle |
||
|
|
97394d93b6 |
fix: replay marker-only slash skill prompts (#1035)
## Summary - reconstructs marker-only model-bound slash skill prompts during history replay - keeps client/local command metadata hidden when local stdout/stderr is present - adds regression coverage for both string and content-array transcript shapes Closes #998. ## Verification - `npm run test:run -- src/tests/acp-agent.test.ts -t 'stripLocalCommandMetadata'` - `npm run test:run -- src/tests/acp-agent.test.ts` - `npm run build` - `npm run check` - `git diff --check` I also ran `npm run test:run`. It completed with 965 passing tests, 27 skipped, and the same two root-environment `resolve-permission-mode` failures seen when running as root: `bypassPermissions` is intentionally downgraded to `default` in that environment. The changed replay/metadata tests passed. Signed-off-by: Emre Koca <1328630750+kocaemre@users.noreply.github.com> Co-authored-by: Emre Koca <1328630750+kocaemre@users.noreply.github.com>v0.81.1-preview.8 |
||
|
|
388a0f5191 |
fix(session): recreate query when configured skills change (#968)
## Summary - include normalized Claude SDK `skills` in the resumed-session fingerprint - preserve the distinct semantics of omitted, `'all'`, empty, and explicit skill selections - avoid unnecessary Query recreation for reordered or duplicate skill names - keep unrelated `_meta.claudeCode.options` fields out of the fingerprint - recreate the Query with the same native session ID when skills change - merge the latest `origin/main` so the change is validated against the current session logic and SDK 0.3.238 ## Testing - clean Linux Node 22 environment with `@anthropic-ai/claude-agent-sdk@0.3.238` - `npm run build` - `npm run lint` - `npm run check` - targeted lifecycle tests: `425 passed, 9 skipped` - full test suite: `978 passed, 27 skipped` - successful lifecycle case: `newSession(['pdf']) -> resumeSession(['docx'])` Closes #955v0.81.1-preview.7 |
||
|
|
9392c69cc6 |
fix: settle steered turns on the result that answers the steer (#1166)
Fixes #1114. ## Problem A steered turn settles only at an SDK `idle`, and only once the steer's echo has been replayed and an outcome recorded. Two ways that never happens, both from #1114: - **Missing echo.** If the CLI never replays the steered message, every idle is swallowed and `session/prompt` parks until cancel or the next prompt. - **Owed-idle debt eats the settling idle.** If the steer aborts an autonomous followup, that followup's result counts a trailing idle, but the abort folds it into the one idle that spans both cycles. The debt lane absorbs that idle and the steered turn never settles. ## Fix 1. **Exact lane: settle on the result that answers the steer.** A result whose `user_message_uuids` (falling back to `user_message_uuid`) names one of the turn's steers retires that steer's echo, even if the CLI never replayed it. Once no steer is outstanding, the turn leaves the steer lane and settles at that result through the ordinary lanes, including the subagent hold and failure lanes. It owes its trailing idle like any other settling result. No timer. 2. **Idle fallback, for producers that don't stamp results: count only a result recorded after the last echo.** Before this, the outcome of the cycle the steer interrupted (or a held turn's outcome that `steer()` moved over) made the turn look ready as soon as the echo landed. `steeredAwaitingResult` is set when an echo drains and cleared when a result is recorded. 3. **A ready steered turn takes the idle ahead of owed-idle debt.** Its outcome is final, so whichever idle arrives settles it correctly. The debt is left standing: a separate trailing idle still absorbs the idle it belongs to, and one that never comes is cleared by the existing sweep at the next `running` transition. A steered turn that isn't ready still goes through the debt lane first, so an earlier turn's late idle can never settle a steered cycle that's still running. ## Live verification (CLI 2.1.280, SDK 0.3.280) A probe with the adapter's query options (`replay-user-messages`, partial messages) steered a running turn three ways: mid-generation at `now`, mid-tool-call at `now`, and mid-tool-call at `later`. All three had the same frame order: ``` RESULT stamp=ORIG stamps=[ORIG] <- interrupted (or finished) original cycle lifecycle ORIG cancelled|completed lifecycle STEER started ECHO STEER RESULT stamp=STEER stamps=[STEER] <- steered cycle lifecycle STEER completed STATE idle <- the only idle ``` So the interrupted cycle's result never names the steer, and the steered cycle's result always does. `later` doesn't fold the steer into the running turn either; it runs as its own cycle after the original finishes, with no idle in between. ## Tests Three new tests in `turn steering (_session/steering)`: - **Echo missing, stamped result:** the turn settles at the result before any idle, and the idle that follows is absorbed. Hangs on `main`. - **Steer aborts an autonomous followup, only one idle:** the turn settles. Hangs on `main`. - **Owed idle arrives after the echo but before the steered result:** the turn must not settle on it, and it ends with both cycles' usage. Passes on `main`, but fails with the post-echo readiness check removed, so it guards against settling early on a pre-steer outcome. `npm run build`, `npm run test:run` and `npm run check` pass. ## Remaining gap If the echo never replays and the result carries no stamp (older producers), the turn still waits for cancel or the next prompt, as documented before. Supersedes #1115 and #1063. Thanks to @KlausUllrich for the #1114 analysis and the result-acknowledgement idea, which this builds on. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>v0.81.1-preview.6 |
||
|
|
281e47eb81 |
fix: honor permissions.disableBypassPermissionsMode (#1165)
## Problem Claude Code refuses `bypassPermissions` when any settings tier sets `permissions.disableBypassPermissionsMode` to `"disable"`. The adapter ignored the setting: it still listed the Bypass mode, passed `allowDangerouslySkipPermissions`, and accepted `defaultMode: "bypassPermissions"` as the starting mode. ## Change Fold the setting into `allowBypass` at session creation. That single value already drives the mode catalog, the SDK flag and the spawn-time mode clamp, so all three now agree with the CLI. Like the CLI, the setting is honored from any tier. It can only take away a permission, so project settings are trusted for it. ## Validation - `npm run build`, `npm run check` and `npm run test:run` pass. - New tests set the option in user settings and in project settings. Both fail without the fix. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>v0.81.1-preview.5 |
||
|
|
43c27e97aa |
fix: offer bypass alongside auto in plan approval (#1164)
Supersedes #1140 by @s-cork. Their commit is kept as-is (rebased onto `main`), plus one follow-up commit. The fork is org-owned, so maintainers can't push to it. ## Problem ExitPlanMode always led with Auto and dropped bypass, even when the session advertised `bypassPermissions`. Clients that deliberately run in full-access mode lost that choice at plan approval. Claude Code itself offers one elevated choice and prefers bypass over Auto. It can do that because the CLI only offers bypass to users who launched with it. The adapter advertises bypass for every non-root session, so copying that priority would hide Auto for almost everyone. ## Change - Offer both Auto and bypass whenever the session advertises them (@s-cork's commit). - Follow-up: remember the mode the session left when it entered plan mode (`SessionModeManager.syncConfig`, which every mode change goes through, including plan approval since #1163). If that was `bypassPermissions`, bypass comes first and is also what the clear-context option uses. Otherwise Auto leads, as before. A remembered bypass mode is ignored if the session no longer advertises it. This also fills the gap where the clear-context choice only ever came in the Auto flavour. Partially addresses #1077. ## Validation - `npm run build`, `npm run check` and `npm run test:run` pass. - New tests cover the pre-plan mode tracking and the option order with and without a pre-plan bypass mode, including the clear-context row. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: stu <stucork@gmail.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
bd059c10e0 |
fix: mark informational chunks with metadata (#1055)
## Summary - tag SDK `informational` notices with `_meta.claudeCode.kind = "informational"` - include the notice `level` so clients can separate hook/status notices from model text - keep the existing rendered text unchanged for clients that ignore `_meta` Fixes #1042 ## Test plan - `npm test -- --run src/tests/acp-agent.test.ts -t informational` - `npm run build` - `npm run check` - `IS_SANDBOX=1 npm run test:run` - `git diff --check` --------- Signed-off-by: Emre Koca <110906681+kocaemre@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>v0.81.1-preview.4 |
||
|
|
58c5db1fdc |
fix: keep the agent starting when the managed-policy tier cannot be read (#1146)
Fixes #1124. ## The defect `src/index.ts` awaited `resolveSettings` at module scope with nothing around it: ```ts const policy = await resolveSettings({ settingSources: [] }); for (const [key, value] of Object.entries(policy.effective.env ?? {})) { process.env[key] = value; } ``` That is a top-level `await` in an ESM entry point, so a rejection aborts module evaluation and the process exits before serving any ACP traffic. The client only sees a process that exited 1 with no protocol messages. Two neighbours make this the only call that can do it, and both are still true on \`main\`: - `SettingsManager.loadAllSettings` (`src/settings.ts:105-113`) wraps the same SDK call in `try`/`catch` and falls back to `{}`. - The `process.on("unhandledRejection", ...)` handler is registered at \`src/index.ts:62\`, after this await, so it does not cover it. Reading the managed tier is best-effort by nature — on a host with no MDM and no managed policy the result is empty and the agent behaves identically — so a transient `EINTR`/`EMFILE`/`EAGAIN` while reading it should leave the agent running with no policy env rather than stop it from starting. ## The change The guard lives in a new `src/managed-policy.ts` rather than inline, for one reason: `src/index.ts` is an ESM entry point with top-level side effects, so nothing in it can be imported by a test. Extracting the tier read makes the failure path testable, which is what the regression test below exercises. `applyManagedPolicyEnv` mirrors `loadAllSettings`: it takes the SDK call through a parameter defaulting to `resolveSettings` and a target env defaulting to `process.env`, reports the failure on stderr, and returns without applying anything. `src/index.ts` becomes: ```ts await applyManagedPolicyEnv(); ``` `console.error` is correct here: this runs before stdout is redirected, so the ACP stream stays clean. ## Verification Four tests in `src/tests/managed-policy.test.ts` cover the applied case, an empty tier, a tier with no `env` table, and the failure path — the last one asserts the promise **resolves** rather than rejects, with the target env untouched, which is the property the entry point depends on. ``` before after tests 17 failed | 1366 passed 17 failed | 1370 passed ``` The 17 failures are pre-existing in this checkout and untouched by this change; the four added tests account for the difference. `eslint src --ext .ts`, `tsc`, and `prettier --check` (with the repository's `.prettierrc.json`) are clean on the three files. ## Note I have not established whether those 17 pre-existing failures are host-specific or real, so I am not claiming anything about them or changing anything for them.v0.81.1-preview.3 |
||
|
|
a2f4239543 |
fix: publish effective mode after plan approval (#1163)
Supersedes #1139 by @s-cork. Their commit is kept as-is (rebased onto `main`), plus one follow-up commit. The fork is org-owned, so maintainers can't push to it. ## Problem Approving a plan through ExitPlanMode changes the SDK permission mode, but ACP clients keep showing Plan. When Auto isn't available for the current model, clients should also see the fallback mode that actually applies. ## Change - After an ExitPlanMode permission is allowed, send `current_mode_update` and `config_option_update` with the session mode as it stands after `applyPermissionFallback`. Rejection and clear-context handling don't change. - Follow-up: wrap those two notifications in a try/catch. The plan is already approved at that point, so a rejected `sessionUpdate` is logged and no longer turns the approval into a failed permission request. This matches `publishFallbackWarning`. Partially addresses #1077. It doesn't fix session restoration after a clear-context approval. ## Validation - `npm run build`, `npm run check` and `npm run test:run` pass. - Regression tests cover the ordinary default, bypass and Auto approvals, plus Auto falling back to acceptEdits. They check both the stored mode and both ACP notifications. - A new test rejects the mode notification and checks that the approval still returns `allow` with the `setMode` update. It fails without the follow-up fix. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: stu <stucork@gmail.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>v0.81.1-preview.2 |
||
|
|
90cde4b397 |
fix: render Write tool calls that use path/file_text aliases (#1161)
Claude Code 2.1.280 (SDK 0.3.280, bumped in #1157) now accepts Write calls where the model sends `path` instead of `file_path`, or `file_text`/`file_content` instead of `content`. The CLI renames these for its own validation, but the streamed `tool_use` block keeps the model's original field names. So we showed the call as "Preparing file…" with no diff or location, even though the write went through. `toolInfoFromToolUse` now reads those fields the same way the CLI does. When both spellings are present, the canonical fields win. Nothing else in the 0.3.280 / 2.1.280 changelog needed an adapter change. I checked Opus 5.5 model rows, the hand-back frame header, `requires_action` during MCP elicitation, and headless elicitation cancellation. ## Test plan - New `toolInfoFromToolUse - Write input aliases` tests - `npm run build`, `npm run check`, `npm run test:run` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>v0.81.1-preview.1 |
||
|
|
d571358e26 |
chore(main): release 0.81.0 (#1156)
🤖 I have created a release *beep* *boop* --- ## [0.81.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.80.0...v0.81.0) (2026-09-22) ### Features * **deps:** update Claude SDK ([#1157](https://github.com/agentclientprotocol/claude-agent-acp/issues/1157)) ([v0.81.0 |
||
|
|
eb989e93f4 | feat(deps): update Claude SDK (#1157) v0.80.1-preview.2 | ||
|
|
79851b8668 | feat: Send experimental session notices for live advisories (#1155) v0.80.1-preview.1 | ||
|
|
16c9d1a3af |
chore(main): release 0.80.0 (#1151)
🤖 I have created a release *beep* *boop* --- ## [0.80.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.79.0...v0.80.0) (2026-09-22) ### Features * **deps:** Bump claude-agent-sdk to 0.3.278 ([#1147](https://github.com/agentclientprotocol/claude-agent-acp/issues/1147)) ([v0.80.0 |
||
|
|
a05aca6b81 |
fix: close interrupted compaction lifecycles (#1154)
## Summary Close unfinished ACP compactions before interrupted prompts settle. Previously, force cancellation could return `stopReason: cancelled` while leaving the compaction `in_progress`; SDK stream teardown and conversation reset had similar cleanup gaps. - Add shared, idempotent interruption cleanup for cancellation, force-cancel recovery, provider failures, SDK EOF/errors, and conversation reset. - Close compactions before resolving echo-less `/compact` commands, which may still be queued while their compaction is running. - Discard abandoned pending summaries and ignore late compaction openings, completions, chunks, and hooks until an identified live turn resumes. - Preserve normal early-summary buffering across ordinary result boundaries. - Add regression coverage for terminal-before-response ordering, echo-less cancellation, late SDK events, fresh-turn recovery, EOF/errors, reset, and best-effort delivery failures. After interruption, hooks without a command identity are conservatively omitted until a new live turn is identified, rather than risking attribution to the wrong compaction. Older echo-less commands without dispatch identity may likewise omit the ambiguous compaction lifecycle until turn activation. This fixes an implementation gap found while auditing the [Session Compaction RFD](https://github.com/agentclientprotocol/agent-client-protocol/blob/main/docs/rfds/session-compaction.mdx). No protocol or RFD changes are included. ## Validation - [x] `npm run test:run` — 1,397 passed, 28 skipped - [x] `npm run build` - [x] `npm run check` — ESLint and Prettier - [x] `git diff --check` Tests use mocked SDK streams; no live provider calls were made.v0.79.1-preview.3 |
||
|
|
5bc3b4e560 |
feat(deps): Bump claude-agent-sdk to 0.3.278 (#1147)
Bumps the minor group with 11 updates: | Package | From | To | | --- | --- | --- | | [@anthropic-ai/claude-agent-sdk](https://github.com/anthropics/claude-agent-sdk-typescript) | `0.3.274` | `0.3.276` | | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `26.5.1` | `26.6.1` | | [prettier](https://github.com/prettier/prettier) | `3.9.7` | `3.9.8` | | @anthropic-ai/claude-agent-sdk-darwin-arm64 | `0.3.274` | `0.3.276` | | @anthropic-ai/claude-agent-sdk-darwin-x64 | `0.3.274` | `0.3.276` | | @anthropic-ai/claude-agent-sdk-linux-arm64 | `0.3.274` | `0.3.276` | | @anthropic-ai/claude-agent-sdk-linux-arm64-musl | `0.3.274` | `0.3.276` | | @anthropic-ai/claude-agent-sdk-linux-x64 | `0.3.274` | `0.3.276` | | @anthropic-ai/claude-agent-sdk-linux-x64-musl | `0.3.274` | `0.3.276` | | @anthropic-ai/claude-agent-sdk-win32-arm64 | `0.3.274` | `0.3.276` | | @anthropic-ai/claude-agent-sdk-win32-x64 | `0.3.274` | `0.3.276` | Updates `@anthropic-ai/claude-agent-sdk` from 0.3.274 to 0.3.276 <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.276</h2> <h2>What's changed</h2> <ul> <li>Updated to parity with Claude Code v2.1.276</li> </ul> <h2>Update</h2> <pre lang="sh"><code>npm install @anthropic-ai/claude-agent-sdk@0.3.276 # or yarn add @anthropic-ai/claude-agent-sdk@0.3.276 # or pnpm add @anthropic-ai/claude-agent-sdk@0.3.276 # or bun add @anthropic-ai/claude-agent-sdk@0.3.276 </code></pre> <h2>v0.3.275</h2> <h2>What's changed</h2> <ul> <li>Fixed a deferred tool call's result being emitted with internal keys such as <code>toolUseResult</code> instead of <code>tool_use_result</code> when the tool re-runs at the start of a resumed turn</li> <li>Fixed <code>getSessionMessages()</code> and <code>forkSession()</code> sometimes missing a turn's assistant message when called right after the turn's <code>result</code> message</li> <li>Fixed <code>forkSession({ upToMessageId })</code> rejecting a client-supplied <code>SDKUserMessage.uuid</code> that is not in UUID format</li> <li>Fixed <code>forkSession</code> rejecting the id that <code>getSessionMessages</code> returns for a message sent while Claude was working, and a fork showing a re-run prompt twice</li> <li>Fixed <code>getSessionMessages()</code> omitting a task notification or other queued message that Claude read while running a tool; it now comes back where Claude read it</li> <li>Updated to parity with Claude Code v2.1.275</li> </ul> <h2>Update</h2> <pre lang="sh"><code>npm install @anthropic-ai/claude-agent-sdk@0.3.275 # or yarn add @anthropic-ai/claude-agent-sdk@0.3.275 # or pnpm add @anthropic-ai/claude-agent-sdk@0.3.275 # or bun add @anthropic-ai/claude-agent-sdk@0.3.275 </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.276</h2> <ul> <li>Updated to parity with Claude Code v2.1.276</li> </ul> <h2>0.3.275</h2> <ul> <li>Fixed a deferred tool call's result being emitted with internal keys such as <code>toolUseResult</code> instead of <code>tool_use_result</code> when the tool re-runs at the start of a resumed turn</li> <li>Fixed <code>getSessionMessages()</code> and <code>forkSession()</code> sometimes missing a turn's assistant message when called right after the turn's <code>result</code> message</li> <li>Fixed <code>forkSession({ upToMessageId })</code> rejecting a client-supplied <code>SDKUserMessage.uuid</code> that is not in UUID format</li> <li>Fixed <code>forkSession</code> rejecting the id that <code>getSessionMessages</code> returns for a message sent while Claude was working, and a fork showing a re-run prompt twice</li> <li>Fixed <code>getSessionMessages()</code> omitting a task notification or other queued message that Claude read while running a tool; it now comes back where Claude read it</li> <li>Updated to parity with Claude Code v2.1.275</li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="v0.79.1-preview.2 |
||
|
|
c6f9668c12 |
fix: prefer terminal output deltas (#1150)
When a client advertises terminal_output_delta, send Bash output through that channel. Keep the completion update free of duplicate raw output while preserving the existing terminal_output fallback.\n\nTracks IJAI-2376.v0.79.1-preview.1 |
||
|
|
d421f56a6c |
chore(main): release 0.79.0 (#1142)
🤖 I have created a release *beep* *boop* --- ## [0.79.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.78.0...v0.79.0) (2026-09-17) ### Features * Update to claude agent sdk 0.3.274 ([#1143](https://github.com/agentclientprotocol/claude-agent-acp/issues/1143)) ([v0.79.0 |
||
|
|
d0fa8e0385 |
feat: Update to claude agent sdk 0.3.274 (#1143)
- **Update deps** - **fix: Hold through placeholder task results**v0.78.1-preview.2 |
||
|
|
bbae5c2c09 |
fix: show shell commands in permission prompts (#1070)
## Summary - use the canonical tool-call title for permission prompts, so shell approvals show the exact command instead of the model-authored description - map `PowerShell` through the same shell presentation path as `Bash` - complete the PowerShell terminal lifecycle with matching `terminal_info`, `terminal_output`, and `terminal_exit` metadata, including failed commands - add focused regression coverage without changing the existing Bash lifecycle tests Fixes #1068 ## PowerShell input and result contract `PowerShell` is a distinct tool name, but its documented input fields match `Bash`: the executable command is in `command`, while `description` is an optional human-readable summary. The Claude Code hooks reference documents the shared `command`, `description`, `timeout`, and `run_in_background` fields, and the tools reference likewise states that PowerShell hooks receive the command in `tool_input.command`. Anthropic closed [claude-code#83647](https://github.com/anthropics/claude-code/issues/83647#issuecomment-5310033500) after documenting this equivalence. Reusing the SDK's exported `BashInput` shape for the common input fields is therefore intentional; the SDK currently does not export a separate `PowerShellInput` type. PowerShell results do not need a PowerShell-specific output assumption here. The result mapper first shape-checks structured stdout/stderr and otherwise falls back to the standard raw `tool_result` string or content-block array. Routing PowerShell through that existing mapper preserves this fallback while completing the adapter's capability-gated terminal sequence: 1. initial `tool_call` with `terminal_info` 2. `tool_call_update` with `terminal_output` 3. final `tool_call_update` with `terminal_exit` All three events use the original tool-use ID, so the client can reconcile and close the terminal. Failed PowerShell results also bypass the generic error renderer and finish the terminal with a non-zero display exit code. The permission presentation introduced in #1004 already handled `Bash` and `PowerShell` together when choosing a description-based permission title, but the canonical tool presentation and terminal result paths only handled `Bash`. This PR completes both paths. References: - [Claude Code hooks: PowerShell input](https://code.claude.com/docs/en/hooks#powershell) - [Claude Code tools reference: PowerShell tool](https://code.claude.com/docs/en/tools-reference#powershell-tool) - [Anthropic schema confirmation](https://github.com/anthropics/claude-code/issues/83647#issuecomment-5310033500) ## Verification - `npm run check` - `npm run build` - `npm run test:run -- src/tests/tools.test.ts -t "PowerShell terminal output"` (1 passed) - full suite with an empty temporary Claude config: `CLAUDE_CONFIG_DIR=<empty temporary directory> npm run test:run` (1070 passed, 27 skipped) --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>v0.78.1-preview.1 |
||
|
|
becd854dd8 |
chore(main): release 0.78.0 (#1130)
🤖 I have created a release *beep* *boop* --- ## [0.78.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.77.0...v0.78.0) (2026-09-15) ### Features * Add experimental ACP compaction update support ([#1134](https://github.com/agentclientprotocol/claude-agent-acp/issues/1134)) ([v0.78.0 |
||
|
|
91f1699366 |
perf: report file changes from Claude checkpoints (#1138)
## Summary - replace the hidden model-authored file audit turn with Claude native file checkpointing - preview changed paths with rewindFiles at the terminal turn boundary while preserving the existing AIR metadata contract - enable checkpointing only for clients that negotiate agentFileChangeReport and fail open after two seconds - retain legacy audit filtering so persisted sessions do not replay old hidden audit turns This follows Claude Code checkpoint semantics. It tracks changes made through supported Claude file tools; Bash and most subagent changes may not be included. Reference: https://code.claude.com/docs/en/checkpointing ## Validation - npm run build - npm run check - env -u ANTHROPIC_BASE_URL npm run test:run (1368 passed, 28 skipped)v0.77.1-preview.4 |
||
|
|
735950a83e |
perf: supply AIR diff counts from Claude structured patches (#1122)
Claude supplies structured patches for Edit and Write, but ACP diff blocks previously carried only the reconstructed texts. AIR had to compare those texts again to display added and removed counts. Count patch operations in the existing conversion loop and publish `_meta.jetbrains.air.diffStats` on each diff block. This uses the same contract as https://github.com/agentclientprotocol/codex-acp/pull/501. Each block has its own counts. No extra traversal, full file comparison, or navigation calculation is needed. Omit statistics when patches are absent or inconsistent, so clients retain their existing fallback. Exclude EOF markers from the displayed texts. Document the extension and cover per-block counts, Edit and Write hooks, EOF changes, and fallback behavior. Validation: - `npm run build` - `npm run check` - `env -u ANTHROPIC_BASE_URL npm run test:run` (the local endpoint override affects unrelated provider tests)v0.77.1-preview.3 |
||
|
|
543a9a2f97 | feat: Add experimental ACP compaction update support (#1134) v0.77.1-preview.2 | ||
|
|
8823ea6fcb |
fix: keep the picked option when AskUserQuestion custom text is also supplied (#1131)
## Summary For a single-select `AskUserQuestion`, a non-empty custom-answer field (`question_<n>_custom`) replaced the selected option. A client that renders that box as a notes step (as in #1050, seen via Devin/Windsurf) therefore returned only the typed text and the selection never reached the model. - The pick now stays in `answers`, and the typed text travels beside it as the tool's own per-question `annotations[question].notes`. That is the slot the CLI itself uses for free text attached to a selection and renders to the model as `"Q"="A" notes: ...`, so no new answer format is invented. - Custom text with no pick is still the answer on its own (the CLI's "Other"). - Multi-select folding from #1031 is unchanged: the text still joins the checked options with CLI-style quoting. - `annotations` is only added to `updatedInput` when non-empty. - The single-select field description now says the box can also add a note to the chosen option. Supersedes #1054, which conflicted with #1031 and glued label and text into one string. The "empty notes field won't submit" part of #1050 is client-side: nothing in the requested schema is marked required. Closes #1050 ## Test plan - `npm run check` - `npm run build` - `npm run test:run` (1325 passed, 27 skipped) - New test: pick + custom text yields `answers: { Q: "A" }` and `annotations: { Q: { notes } }` - New test: custom text alone on a single-select stays the answer 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>v0.77.1-preview.1 |
||
|
|
dfe823b958 |
chore(main): release 0.77.0 (#1118)
🤖 I have created a release *beep* *boop* --- ## [0.77.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.76.0...v0.77.0) (2026-09-14) ### ⚠ BREAKING CHANGES * claudeCode.options.agent is no longer forwarded to Claude Code, and the agent-picker exports DEFAULT_AGENT_ID, AGENT_CONFIG_ID, BUILTIN_AGENT_NAMES, and discoverCustomAgents are removed. Consumers relying on main-thread agent selection must stop passing that option. ### Features * Add tool names to tool calls ([#1128](https://github.com/agentclientprotocol/claude-agent-acp/issues/1128)) ([v0.77.0 |
||
|
|
3d99cafe4a |
fix: honor host opt-out for allowDangerouslySkipPermissions (#1129)
## Summary Closes #1119. Supersedes #1120 by @amansk (their commits are included on this branch; the fork does not allow maintainer edits, so review fixes landed here instead). Hosts that never use `bypassPermissions` can opt a session out by sending `_meta.claudeCode.options.allowDangerouslySkipPermissions: false` on `session/new` (and again on `session/load`). When omitted or `true`, behavior is unchanged: bypass is available on non-root sessions. ## Design Bypass availability is decided once per session in `createSession`: ```ts const allowBypass = ALLOW_BYPASS && sessionMeta?.claudeCode?.options?.allowDangerouslySkipPermissions !== false; ``` and threaded to the three places that must agree: - the SDK `allowDangerouslySkipPermissions` flag (the actual enforcement; the CLI refuses bypass without it) - the spawn-time mode clamp in `resolvePermissionMode`, so a `permissions.defaultMode` of `bypassPermissions` (or a mode carried across a clear-context restart) becomes `default` before `query()` rather than failing at spawn - the session mode catalog in `SessionModeManager.initialize`, so the client is never offered a mode the CLI would reject ## Changes from #1120 - Fold the host opt-out into `resolvePermissionMode` via an `allowBypass` parameter instead of a second inline clamp; drop the separate `resolveAllowDangerouslySkipPermissions` helper - Make `allowBypass` a required `initialize` parameter and remove the module-global `ALLOW_BYPASS` fallback from `session-mode.ts` - Fix `tsc` errors in the new tests (`modes` is optional on `NewSessionResponse`), which broke the CI build job - Move the settings-driven clamp test onto the shared `acp-agent-settings` fixture (isolated cwd, no double module reload, passes as root) - Document the opt-out in `docs/permission-extension.md` ## Testing - `npm run check`, `npm run build` — pass - `npm run test:run` — pass (the branch is based before #1123, so the SDK version guard in `model-presentation.test.ts` only passes with the pinned 0.3.257; it clears on rebase) Co-authored-by: Cursor Agent <cursoragent@cursor.com> 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Amandeep Khurana <amansk@gmail.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>v0.76.1-preview.8 |
||
|
|
1a4512c711 |
fix: keep multi-select answers when a custom answer is typed (#1031)
## Summary
`applyAskElicitationResponse` drops a multi-select question's checked
options as soon as
that question's custom-answer field is non-empty: the typed text
replaces the selection
instead of joining it, so the checked options never reach the model.
Versions: reproduced on v0.69.0, code unchanged on v0.70.0 and current
`main` (
v0.76.1-preview.7
|
||
|
|
b274923ef8 |
fix: parse TaskList output without ambiguous regex (#1006)
## Summary - Replace the ambiguous regex used to parse textual `TaskList` output with linear string parsing. - Preserve support for owners, multiple dependencies, and malformed suffixes treated as subject text. - Add a regression test covering adversarial input. This addresses a potential ReDoS reported by CodeQL without changing the structured JSON path. Closes #1005. ## Testing - `npm run test:run` - `npm run build` - `npm run check`v0.76.1-preview.6 |
||
|
|
15ba608a79 |
fix: strip injected system reminders from replayed prompts (#1040)
## Summary - strip `<system-reminder>` blocks from replayed user messages, through the marker scanner that already handles the local-command tags - what the user actually typed is preserved; a reminder-only message strips to nothing and is skipped - regression coverage for the string shape, the content-array shape, and `replaySessionHistory` end to end ## Why `stripLocalCommandMetadata` exists for a stated reason: *"The live prompt loop drops them; replay must strip them too or they leak into the UI on session/load."* Injected reminders are the same class of leak and are not covered by it. The CLI appends `<system-reminder>` blocks to a user turn to steer the model — a worktree that was deleted, a background task that started elsewhere. They are persisted in the same text block as the prose the user typed, and they are not `isMeta`, so `getSessionMessages` returns them. Live, nothing reaches the client: the prompt loop's user-message skip covers the whole message. On `session/load` the block is replayed verbatim, so a client renders a paragraph of instructions the user never wrote, followed by their one-line prompt. Reproduced against `main` with a real transcript: a reminder about a deleted worktree, followed by a five-word prompt, arrives as a single `user_message_chunk` containing both. With this change the chunk is the five words. ## Not included, deliberately - **`<task-notification>` envelopes.** They still replay as user messages, but #1028 is already doing that properly, keyed on the SDK-provided `origin.kind` rather than the text — the better trust boundary, since a user can type the tag themselves. - **`[Request interrupted by user]`.** Also still replayed as a user message. It is untagged prose and it is the only trace a cancelled turn leaves, so whether it should be dropped, typed, or marked in `_meta` reads as your product call rather than a bug fix. Happy to open a follow-up if you want it handled. The exported name `stripLocalCommandMetadata` is left alone — it is part of `lib.ts` — so only the shared marker list and the doc comment grew. ## The one false positive A user who literally types `<system-reminder>…</system-reminder>` in a prompt loses that span on replay, while seeing it live. It is the same trade already accepted for `<command-name>`, the surrounding prose still survives, and text is the only signal available here — unlike task notifications, reminders carry no `origin` to key on. Flagging it rather than leaving it for review to find. ## Verification - `npm run build` - `npm run check` - `env -u ANTHROPIC_BASE_URL npm run test:run` — 974 passed, 27 skipped - the three new tests fail on `main` and pass with the change 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 <noreply@anthropic.com>v0.76.1-preview.5 |
||
|
|
d3205ab11f |
feat(deps): Bump @anthropic-ai/claude-agent-sdk to 0.3.270 (#1123)
Bumps the minor group with 8 updates in the / directory: | Package | From | To | | --- | --- | --- | | [@anthropic-ai/claude-agent-sdk](https://github.com/anthropics/claude-agent-sdk-typescript) | `0.3.257` | `0.3.263` | | [@anthropic-ai/sdk](https://github.com/anthropics/anthropic-sdk-typescript) | `0.122.0` | `0.124.0` | | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `26.4.0` | `26.5.0` | | [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `8.69.0` | `8.70.0` | | [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) | `8.69.0` | `8.70.0` | | [eslint](https://github.com/eslint/eslint) | `10.9.1` | `10.10.0` | | [hono](https://github.com/honojs/hono) | `4.13.5` | `4.13.7` | | [jose](https://github.com/panva/jose) | `6.2.10` | `6.2.12` | Updates `@anthropic-ai/claude-agent-sdk` from 0.3.257 to 0.3.263 <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.263</h2> <h2>What's changed</h2> <ul> <li>Updated to parity with Claude Code v2.1.263</li> </ul> <h2>Update</h2> <pre lang="sh"><code>npm install @anthropic-ai/claude-agent-sdk@0.3.263 # or yarn add @anthropic-ai/claude-agent-sdk@0.3.263 # or pnpm add @anthropic-ai/claude-agent-sdk@0.3.263 # or bun add @anthropic-ai/claude-agent-sdk@0.3.263 </code></pre> <h2>v0.3.261</h2> <h2>What's changed</h2> <ul> <li>Added <code>pluginDelivery: 'initialize'</code> to send <code>plugins</code> over stdin so the launch command line no longer grows with the plugin count (fixes Windows start failures with many plugins)</li> <li>Fixed <code>query()</code> throwing "Object not disposable" in runtimes without a native <code>Symbol.dispose</code>, such as Node ≤22 <code>vm</code> contexts (Jest's <code>node</code> environment, vitest <code>vmThreads</code>/<code>vmForks</code>) and Node <18.18</li> <li>Updated to parity with Claude Code v2.1.261</li> </ul> <h2>Update</h2> <pre lang="sh"><code>npm install @anthropic-ai/claude-agent-sdk@0.3.261 # or yarn add @anthropic-ai/claude-agent-sdk@0.3.261 # or pnpm add @anthropic-ai/claude-agent-sdk@0.3.261 # or bun add @anthropic-ai/claude-agent-sdk@0.3.261 </code></pre> <h2>v0.3.260</h2> <h2>What's changed</h2> <ul> <li>Added optional <code>user_message_uuid</code> to <code>thinking_tokens</code> system messages, linking thinking progress to the user message that triggered the turn</li> <li>Added optional <code>first_content_frame_ms</code>, <code>first_stream_post_ms</code>, <code>first_stream_post_ack_ms</code> and <code>first_stream_post_wall_ms</code> fields to the success result message for remote-session latency breakdowns</li> <li>Fixed <code>managedSettings</code> <code>disableAutoMode: "disable"</code> (either spelling) being dropped by the restrictive-only filter instead of turning auto mode off for the spawned session</li> <li>Fixed <code>rewindFiles()</code> reporting success when no files could be restored (for example when checkpoint backups are missing); it now fails</li> <li>Changed <code>error_max_structured_output_retries</code> results to append the last StructuredOutput tool error; validation errors now name the offending key, allowed values, and actual length or count</li> <li>Changed <code>rate_limit_event</code> to also re-emit during an exceeded window on repeat 429s (about once per 30 seconds per limit window), so stream consumers can refresh stale rate-limit state</li> <li>Updated to parity with Claude Code v2.1.260</li> </ul> <h2>Update</h2> <pre lang="sh"><code></tr></table> </code></pre> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/anthropics/claude-agent-sdk-typescript/blob/main/CHANGELOG.md">@anthropic-ai/claude-agent-sdk's changelog</a>.</em></p> <blockquote> <h2>0.3.263</h2> <ul> <li>Updated to parity with Claude Code v2.1.263</li> </ul> <h2>0.3.262</h2> <ul> <li>Updated to parity with Claude Code v2.1.262</li> </ul> <h2>0.3.261</h2> <ul> <li>Added <code>pluginDelivery: 'initialize'</code> to send <code>plugins</code> over stdin so the launch command line no longer grows with the plugin count (fixes Windows start failures with many plugins)</li> <li>Fixed <code>query()</code> throwing "Object not disposable" in runtimes without a native <code>Symbol.dispose</code>, such as Node ≤22 <code>vm</code> contexts (Jest's <code>node</code> environment, vitest <code>vmThreads</code>/<code>vmForks</code>) and Node <18.18</li> <li>Updated to parity with Claude Code v2.1.261</li> </ul> <h2>0.3.260</h2> <ul> <li>Added optional <code>user_message_uuid</code> to <code>thinking_tokens</code> system messages, linking thinking progress to the user message that triggered the turn</li> <li>Added optional <code>first_content_frame_ms</code>, <code>first_stream_post_ms</code>, <code>first_stream_post_ack_ms</code> and <code>first_stream_post_wall_ms</code> fields to the success result message for remote-session latency breakdowns</li> <li>Fixed <code>managedSettings</code> <code>disableAutoMode: "disable"</code> (either spelling) being dropped by the restrictive-only filter instead of turning auto mode off for the spawned session</li> <li>Fixed <code>rewindFiles()</code> reporting success when no files could be restored (for example when checkpoint backups are missing); it now fails</li> <li>Changed <code>error_max_structured_output_retries</code> results to append the last StructuredOutput tool error; validation errors now name the offending key, allowed values, and actual length or count</li> <li>Changed <code>rate_limit_event</code> to also re-emit during an exceeded window on repeat 429s (about once per 30 seconds per limit window), so stream consumers can refresh stale rate-limit state</li> <li>Updated to parity with Claude Code v2.1.260</li> </ul> <h2>0.3.259</h2> <ul> <li>Added <code>user_message_uuids</code> beside <code>user_message_uuid</code> on a turn's first reply frame and result: every user message the turn answered, so a reply to several merged messages can be matched to each</li> <li>Added <code>permissionPrompts: 'none'</code> option to auto-deny permission prompts in sessions with nobody to answer them, without disabling auto mode's classifier</li> <li>Updated to parity with Claude Code v2.1.259</li> </ul> <h2>0.3.258</h2> <ul> <li>Updated to parity with Claude Code v2.1.258</li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="v0.76.1-preview.4 |
||
|
|
67c7c9b117 |
feat: Add tool names to tool calls (#1128)
Supports the new tool call name RFDv0.76.1-preview.3 |
||
|
|
4deace4037 | ci: poll for package availability after publish before triggering registry update (#1121) v0.76.1-preview.2 | ||
|
|
6b7473b1af |
refactor!: remove agent config option (#1112)
## Why The main-thread agent option is a rarely used, provider-specific parameter. It is not used by ACP clients or their settings, and maintaining a public picker for it adds config state, discovery calls, restart propagation, and live-switch handling for functionality that clients do not need. Removing it keeps the session configuration contract focused on settings that ACP clients actually use. This does not remove ACP subagent sessions or the Agent/Task tool behavior; it only removes the unused main-thread agent selector and parameter plumbing. ## Summary - remove the main-thread Agent session config picker and custom-agent discovery - remove live agent switching and session state used only by that picker - ignore claudeCode.options.agent and stop restoring it during context resets - document agent as ignored in the exported NewSessionMeta contract - preserve ACP subagent sessions and Agent/Task tool behavior ## Breaking change BREAKING CHANGE: claudeCode.options.agent is no longer forwarded to Claude Code, and the agent-picker exports DEFAULT_AGENT_ID, AGENT_CONFIG_ID, BUILTIN_AGENT_NAMES, and discoverCustomAgents are removed. Consumers relying on main-thread agent selection must stop passing that option. ## Testing - npm run check - npm run build - npx vitest run src/tests/create-session-options.test.ts (62 passed)v0.76.1-preview.1 |
||
|
|
c2e4815029 |
chore(main): release 0.76.0 (#1101)
🤖 I have created a release *beep* *boop* --- ## [0.76.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.75.1...v0.76.0) (2026-09-09) ### Features * advertise recommended config values ([#1111](https://github.com/agentclientprotocol/claude-agent-acp/issues/1111)) ([v0.76.0 |
||
|
|
2b666c3d35 |
feat: advertise recommended config values (#1111)
Clients that negotiate the AIR `recommendedValue` capability receive concrete model and effort choices instead of ambiguous default rows. The SDK's default model is mapped only to an exact selectable model (including equivalent context suffix spellings); unavailable recommendations retain the legacy default. Recommendations remain independent of explicit selections. The adapter applies the displayed effort to the SDK at startup and on model switches, preserving supported user pins and re-reading per-model settings for automatic choices. Permission modes and clients without the capability retain their existing configuration behavior. Model and effort configuration logic is extracted into dedicated modules. Terse SDK model labels are enriched with concrete versions from model metadata: for example, `Fable` becomes `Fable 5.1`, `Sonnet` becomes `Sonnet 5`, and `Opus (1M context)` becomes `Opus 5`. Custom names are preserved, and collisions fall back to the original labels. An SDK version guard requires review on upgrades, and an opt-in live initialization test verifies the upstream model contract without sending a prompt. Validation: - `npm run check` - `npm run build` - `env -u ANTHROPIC_BASE_URL npm run test:run` — 1253 passed, 28 skipped - `RUN_INTEGRATION_TESTS=true npx vitest run src/tests/model-presentation.test.ts` — 4 passed - Regression coverage for unavailable model generations/context windows, startup effort, per-model settings, unsupported effort pins, and duplicate Opus labelsv0.75.2-preview.2 |
||
|
|
45f8b34020 | ci: don't wait for CI workflow when publishing preview releases, replace --dry-run with real publication (#1113) v0.75.2-preview.1 |