fix(acp): don't fail the loop when an agent lacks session/set_mode - #68
Merged
Merged
Conversation
Ralph asked every agent to switch into its preferred session mode as soon
as the session opened, guarded only by `modes?.currentModeId !== preferred`.
That guard is true when the agent advertises no modes at all, so agents
without session-mode support got a `session/set_mode` request they answer
with "Method 'session/set_mode' not found" — aborting iteration 1.
Session modes are optional in ACP, and an agent only has to accept modes it
advertised in `session/new`. Only send `session/set_mode` when the preferred
mode is in `availableModes` and isn't already current, and treat a rejected
call as a warning rather than an iteration error.
Also stop a chatty agent from killing the connection: `ndJsonStream`
enqueues any line that parses as JSON, including bare scalars, and the SDK
dispatcher's `"method" in message` throws a TypeError on a primitive that
escapes its receive loop as an unhandled rejection ("message is not an
Object"). Non-object messages are now dropped before they reach the SDK.
Adapters gained an install hint, surfaced by `ralph run` when the ACP binary
is missing, and the docs now spell out that Claude Code needs the scoped
`@zed-industries/claude-code-acp` package — the unscoped `claude-code-acp`
package on npm is an unrelated project that installs `cc-acp`.
Covered by new adapter tests driving a fake ACP agent subprocess.
Fixes #67
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NHqjSU8nVvcMyAYNr6XEb9
`@zed-industries/claude-code-acp` is deprecated on npm — it was renamed to `@agentclientprotocol/claude-agent-acp`, which ships a `claude-agent-acp` binary, tracks ACP SDK 1.x, and was last published this month (the old package's last release was February). Point the Claude adapter at `claude-agent-acp` and add a general `fallbackCommands` list to AcpAdapter so an adapter can accept older binary names: `claude-code-acp` still resolves for users who have the deprecated package installed, with a warning naming the maintained one. `isAvailable()` and the spawn in `run()` now share that resolution, so a fallback install is detected rather than reported missing. Docs and README name the maintained package and explain the three similarly-named packages: the maintained one, the deprecated rename, and the unrelated third-party `claude-code-acp` on npm that installs `cc-acp`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NHqjSU8nVvcMyAYNr6XEb9
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #67
Problem
ralph runaborts on iteration 1 against any agent that doesn't implement session modes:Root cause
Two independent bugs, both reproduced in the new tests before fixing.
1.
session/set_modesent to agents that never advertised modes. The guard inAcpAdapter.run()was:When an agent advertises no modes,
modesisundefined, soundefined !== "acceptEdits"is true and the request goes out anyway. Session modes are optional in ACP — an agent only has to accept modes it advertised insession/new— so the agent answersMethod 'session/set_mode' not found, which threw out ofrun()and failed the iteration.2. A bare JSON scalar on the agent's stdout kills the connection.
ndJsonStreamenqueues every line that parses as JSON, including scalars like"some log line". The SDK dispatcher then evaluates"method" in message, which throws aTypeErroron a primitive and escapes its receive loop as an unhandled rejection — themessage is not an Objectline above.Changes
Protocol robustness
applyPreferredMode()only sendssession/set_modewhen the preferred mode appears inavailableModesand isn't already current; a rejected call is logged as a warning instead of aborting the iteration. Mode selection is a nicety, not a requirement for the loop to run.adapters/acp/message-filter.tsdrops non-object messages before they reach the SDK dispatcher.Point the Claude adapter at the maintained agent
The adapter targeted
claude-code-acp, the binary from@zed-industries/claude-code-acp. That package is deprecated on npm — renamed to@agentclientprotocol/claude-agent-acp, which ships aclaude-agent-acpbinary, tracks ACP SDK 1.x, and was last published this month (the old package's last release was February).ClaudeAcpAdapter.commandis nowclaude-agent-acp.AcpAdaptergained afallbackCommandslist:claude-code-acpstill resolves for users who have the deprecated package installed, with a warning naming the maintained one.isAvailable()and the spawn inrun()share that resolution, so a fallback install is detected rather than reported missing.installHint, printed byralph runwhen no candidate binary is onPATH.Docs
README and the installation guide name the maintained package and disambiguate the three similarly-named ones:
@agentclientprotocol/claude-agent-acp(maintained),@zed-industries/claude-code-acp(deprecated rename, still accepted), and the unrelated third-partyclaude-code-acpon npm that installscc-acp— installing that one and symlinking it is what led the reporter to an agent that doesn't implement the protocol.Tests
packages/cli/src/adapters/acp.test.tsdrives the adapter against a fake ACP agent subprocess whose behaviour is set by flags (advertises modes or not, accepts or rejectsset_mode, prints a bare JSON scalar or not). Four of the five cases failed with the exact errors from the issue before the fix. Three further tests cover primary/fallback/missing command resolution.Verification
Locally, matching CI:
bun run lint,bun run test(582 pass),bun turbo typecheck, andbun run --cwd packages/cli build --singleall clean.Note on dependencies
Bumping
@agentclientprotocol/sdkdoes not fix this. The SDK added the non-object guard in v1.0.0 (ralph pins^0.14.1), so a bump would resolve bug 2 — but bug 1 is ralph's own guard logic, and the SDK forwards the agent'sMethod not founderror either way. The 0.14 → 1.4 bump is a major-version jump and is left out of this fix.