Repository navigation
feat: add an Agent Client Protocol adapter - #41
Merged
Merged
Conversation
Add Provider::Acp and providers::acp::Acp, one adapter for any agent that speaks ACP over stdio (Gemini CLI, GitHub Copilot, Cursor and the rest of the ACP registry), selected per turn through the agent harness option. Also adds TurnEvent::PlanUpdated and AgentAdapter::terminal_exit_grace. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
dviejokfs
marked this pull request as ready for review
October 12, 2026 19:14
|
…nd probes - keep the latest streamed tool output so a bare `completed`/`failed` update still reports the result or failure details - refuse a relative working directory (turns and `probe_agent` sessions): the agent starts inside it, so ACP's absolute `cwd` would resolve twice - add `AgentAdapter::interaction_answered` and drop a permission request from the open list when its reply is sent, so cancelling afterwards does not answer the same request id twice - bound the probe's wait for stderr after stopping the agent and abort the reader, so a descendant outside the process group cannot hang it Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Resolves the CHANGELOG overlap with the session title PR (#40). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…a turn Discovery and readiness treated Provider::Acp as one executable named `acp`, so registered agents and their Agent control never appeared in the inventory and readiness(Provider::Acp) reported the placeholder missing. AgentAdapter::selectable_executables (default None) lets an adapter that chooses among several executables per turn expose them; the runtime resolves each inside the configured transport, reports the provider installed when one resolves, names each agent in the detail and keeps only installed agents in the Agent control group. discover_harnesses now schedules Provider::Acp when the adapter is registered. A finished ACP turn could still time out: after process.wait() the turn awaited the stderr reader, which a detached descendant holding the pipe never ends. StderrCapture waits a bounded 2s for end-of-file, then stops reading and keeps the tail already read. The turn, the extension command, the metadata probe and Acp::probe_agent use it. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… grace Bounding the stderr wait of a finished turn must not close the read end of the pipe: a tool that ToolProcessPolicy::PreserveOnCompletion leaves running would then get SIGPIPE on its next stderr write. A turn now returns after the 2s grace with the tail it has and leaves the reader draining in the background (bounded tail, ends when the last writer closes). Probes, which have already stopped the process, still stop reading. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
StderrCapture::finish took the reader task out of the capture before waiting, so cancelling the wait (for example dropping Acp::probe_agent during cleanup) detached the task and Drop could no longer stop it. Keep the handle in the capture while waiting; only the deliberate detach path used by finished turns releases it. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ancelled A turn's stderr capture is detached: neither an elapsed grace nor a cancelled or dropped wait stops the reader, so a tool that ToolProcessPolicy::PreserveOnCompletion left running never gets a broken pipe. Probes keep the stop-on-drop default because they have already stopped the process. This replaces the finish/finish_detached pair with one finish and a detached() constructor option that also governs Drop. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Detaching at spawn also kept the reader alive when a turn timed out or returned early on an emit or write error, leaving a task and pipe per failure while an escaped descendant held stderr. The capture now stays stop-on-drop until the completion path has decided to preserve descendants (ToolProcessPolicy::PreserveOnCompletion); only then does it keep draining past the grace or a cancelled wait. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
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.
What
Adds
Provider::Acpandproviders::acp::Acp(default featureacp): one adapter for any agent that speaks the Agent Client Protocol over stdio, such as Gemini CLI (gemini --acp), GitHub Copilot (copilot --acp) and Cursor (cursor-agent acp).AcpAgentsregistry. A turn selects one with theagentharness option, advertised as aHarnessControlKind::Agentcontrol.initialize,session/new(orsession/resume, elsesession/loadwithout its replay), optional mode and model selection, thensession/prompt.session/request_permissionreaches the interaction handler unlessAcceptEdits/FullAccessdecide it. Cancellation sendssession/cancel;-32000fails as an authentication error before the prompt.Acp::probe_agentruns the handshake to report capabilities and sign-in state.TurnEvent::PlanUpdated(PlanEntry/PlanEntryStatus) andAgentAdapter::terminal_exit_grace.Docs:
docs/how-to/run-acp-agents.md,docs/reference/events.md. End-to-end tests intests/acp_stdio.rsuse a scripted POSIX-shell ACP agent.Why
Lets Temps Fleet run ACP agents through the same runtime as Claude, Codex and OpenCode. Fleet's ACP code sits behind
cfg(fleet_acp_runtime)until this merges and its pin moves.Verification
cargo fmt --check,cargo clippy --locked --all-targets --all-features -- -D warningsandcargo test --all-features --lib(442 passed) are clean locally. CI runs a newer clippy than my local toolchain, so its first run may report lints I could not reproduce.🤖 Generated with Claude Code