## Problem The ACP steering extension injects follow-up user messages into a running Claude SDK turn. Until now, every injected message used priority `now`. That is normally desirable because it makes Claude react immediately, but `now` is interrupting delivery: the SDK aborts the currently running cycle before processing the new message. This becomes unsafe while the SDK is blocked in a user-input callback, such as a permission request or elicitation. Interrupting that cycle makes the SDK send `$/cancel_request` for the pending ACP request. The client then removes the permission/elicitation card before the user can answer it. Depending on timing, the original ACP prompt can remain pending because the callback and interrupted cycle no longer complete through their normal lifecycle. The failure sequence was: 1. Claude requests permission or opens an elicitation. 2. The ACP client displays a user-input card and waits for an answer. 3. The user sends a steering message while that card is still open. 4. The adapter injects the message with priority `now`. 5. The Claude SDK interrupts the cycle and emits `$/cancel_request` for the open card. 6. The card disappears, and the original prompt may be left waiting indefinitely. ## Fix Track the number of permission/elicitation requests currently awaiting user input for each session. Steering now selects its internal SDK delivery priority as follows: - `later` while at least one user-input request is pending, so the steering message is queued without interrupting or cancelling the open request; - `now` at all other times, preserving the existing immediate steering behavior. The state is a counter rather than a boolean because multiple background subagents can have concurrent permission requests. The `finally` cleanup keeps the count correct for accepted, declined, cancelled, aborted, and failed requests. All ACP-backed user-input paths are covered: - regular tool permission requests; - the built-in `AskUserQuestion` form elicitation; - MCP form/URL elicitations; - refusal-fallback user dialogs. ## Regression coverage Added parameterized tests that hold each user-input path open, inject a steering message, and verify that the SDK message uses priority `later`. The existing steering regression test continues to verify priority `now` when no user input is pending. The tests also verify that the pending-user-input state is cleared after the request settles. Fixes IJAI-1191. ## Validation - `npm run check` - `npm run build` - `env -u ANTHROPIC_BASE_URL npm run test:run` — 975 passed, 27 skipped `ANTHROPIC_BASE_URL` was removed only for the test command because the Codex environment injects a local proxy URL, while four provider tests intentionally assert the native `https://api.anthropic.com` default.
ACP adapter for the Claude Agent SDK
Use Claude Agent SDK from ACP-compatible clients!
This tool implements an ACP agent by using the official Claude Agent SDK, supporting:
- Context @-mentions
- Images
- Tool calls (with permission requests)
- Following
- Edit review
- TODO lists
- Nested subagent transcripts
- Interactive (and background) terminals
- Custom Slash commands
- Client MCP servers
- Session-scoped long-running goals through the provider-neutral goal extension
- Structured errors, recovery, and warnings through the opt-in session failure extension
- Tool permission presentation, editable choices, and durable effects through the permission extension
Learn more about the Agent Client Protocol.
Nested subagent transcripts
ACP 1.2 has no standard subagent tool kind or nested-message relationship. Clients that can render
nested transcripts can opt in with clientCapabilities._meta["subagent-transcript"] = true.
The agent then forwards subagent text, thinking, and tool calls, relating nested updates to the
launching Agent/Task call through _meta.claudeCode.parentToolUseId. Agent/Task calls are marked
with _meta.claudeCode.subagent = true.
Clients that do not advertise the capability retain the legacy flattened behavior. In both modes, the normal Agent/Task tool result is preserved as the protocol-compatible fallback.
Contribution Policy
This project does not require a Contributor License Agreement (CLA). Instead, contributions are accepted under the following terms:
By contributing to this project, you agree that your contributions will be licensed under the Apache License, Version 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).