-
Notifications
You must be signed in to change notification settings - Fork 3k
specs: add product and tech spec for ACP client support (GH7326) #9380
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| # Product Spec: Support for Agent Client Protocol (ACP) | ||
|
|
||
| **Issue:** [warpdotdev/warp#7326](https://github.com/warpdotdev/warp/issues/7326) | ||
| **Figma:** none provided | ||
|
|
||
| ## Summary | ||
|
|
||
| Warp should support the Agent Client Protocol (ACP) as a client, allowing any | ||
| ACP-compatible coding agent (e.g. opencode, Kimi CLI, Mistral's Devstral, custom | ||
| agents) to run inside Warp's native agent UX — blocks, diff view, file tree, and | ||
| conversation history — without requiring a custom Warp integration per agent. | ||
|
|
||
| ## Problem | ||
|
|
||
| Today, each third-party coding agent (Claude Code, Codex, Gemini CLI) requires a | ||
| dedicated harness in Warp. Adding a new agent means writing new integration code. | ||
| Meanwhile, ACP is an emerging open standard (backed by Zed, JetBrains, and a | ||
| growing ecosystem) that defines a standard JSON-RPC-over-stdio protocol for | ||
| agent-editor communication — analogous to how LSP standardized language server | ||
| integration. Without ACP support, Warp users cannot run the growing number of | ||
| ACP-native agents inside Warp's UI, and must fall back to raw TUI output. | ||
|
|
||
| ## Goals | ||
|
|
||
| - Any ACP-compatible agent can be launched inside Warp and uses Warp's native | ||
| agent UI (conversation blocks, diff view, file tree, tool call rendering). | ||
| - Users can configure ACP agents the same way they configure existing harnesses | ||
| (via the settings UI and/or the Warp config file). | ||
| - Warp passes its MCP servers to the ACP agent session so existing tool context | ||
| is available. | ||
| - The ACP harness validates that the target agent CLI is installed before launch | ||
| and surfaces a helpful install link if not. | ||
|
|
||
| ## Non-goals | ||
|
|
||
| - Warp as an ACP *server* (other IDEs using Warp's agent) — this is a follow-up. | ||
| - Supporting ACP agents that require remote/HTTP transport (stdio only in v1). | ||
| - Replacing existing dedicated harnesses (Claude Code, Codex, Gemini) — those | ||
| remain as-is for agents that benefit from custom integrations. | ||
|
|
||
| ## User experience | ||
|
|
||
| ### Configuring an ACP agent | ||
|
|
||
| 1. User opens Settings → Agents → Third-party CLI agents. | ||
| 2. A new "ACP agents" section lists any configured ACP agents alongside existing | ||
| harnesses. | ||
| 3. User clicks "Add ACP agent" and enters: | ||
| - **Name** (e.g. "opencode", "Kimi") | ||
| - **Command** (e.g. `opencode`, `kimi`) | ||
| - **Args** (optional, e.g. `--model gpt-4o`) | ||
| 4. Warp validates the command exists on PATH. If not, shows an error with a link | ||
| to the agent's install docs (if known). | ||
| 5. The configured agent appears in the harness selector dropdown in the agent | ||
| input footer. | ||
|
|
||
| ### Launching an ACP agent session | ||
|
|
||
| 1. User selects an ACP agent from the harness selector and sends a prompt. | ||
| 2. Warp spawns the agent process over stdio and initiates the ACP handshake | ||
| (initialize → initialized). | ||
| 3. The agent's responses render in Warp's native conversation UI: | ||
| - Text responses appear as agent message blocks. | ||
| - File edits surface in Warp's diff view for review. | ||
| - Tool calls (if passed via MCP) render as tool call blocks. | ||
| 4. The session persists in conversation history and is resumable. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| ### Edge cases | ||
|
|
||
| - **Agent not installed:** Warp shows an inline error with install instructions | ||
| before attempting to spawn the process. | ||
| - **Agent crashes mid-session:** Warp surfaces an error block and marks the | ||
| conversation as ended, matching behavior of existing harnesses. | ||
| - **ACP handshake fails:** Warp shows a descriptive error (e.g. "Agent did not | ||
| respond to initialize within 10 seconds") rather than hanging. | ||
| - **Agent sends unsupported capability:** Warp ignores unknown capability fields | ||
| gracefully (forward-compatible). | ||
| - **No MCP servers configured:** ACP session launches without MCP context; | ||
| no error is shown (same as existing harness behavior). | ||
|
|
||
| ## Success criteria | ||
|
|
||
| 1. An ACP agent configured with a valid command launches successfully and | ||
| produces a conversation block in Warp's UI. | ||
| 2. A file edit proposed by the agent appears in Warp's diff view. | ||
| 3. If the command is not on PATH, Warp shows an error before spawning. | ||
| 4. If the ACP handshake times out, Warp surfaces a descriptive error. | ||
| 5. Configured ACP agents appear in the harness selector alongside Claude Code, | ||
| Codex, and Gemini. | ||
| 6. MCP servers configured in Warp are passed to the ACP agent session. | ||
| 7. Conversation history for ACP sessions persists and is viewable. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| ## Open questions | ||
|
|
||
| 1. Should ACP agents support session resumption in v1, or is that a follow-up? | ||
| 2. How should Warp handle ACP agents that advertise capabilities Warp doesn't | ||
| yet render (e.g. multi-file context beyond diff view)? | ||
| 3. Should there be a curated list of known ACP agents with pre-filled commands | ||
| and install URLs (similar to how Codex and Gemini are pre-configured)? | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,203 @@ | ||
| # Tech Spec: Support for Agent Client Protocol (ACP) | ||
|
|
||
| **Issue:** [warpdotdev/warp#7326](https://github.com/warpdotdev/warp/issues/7326) | ||
| **Product spec:** `specs/GH7326/product.md` | ||
|
|
||
| ## Context | ||
|
|
||
| Warp currently supports third-party coding agents through a `ThirdPartyHarness` | ||
| trait defined in `app/src/ai/agent_sdk/driver/harness/mod.rs`. Each supported | ||
| agent (Claude Code, Gemini) implements this trait in its own file and is | ||
| registered as a variant of the `Harness` enum in `crates/warp_cli/src/agent.rs` | ||
| and `CLIAgent` enum in `app/src/terminal/cli_agent.rs`. | ||
|
|
||
| ACP (Agent Client Protocol) is a standard JSON-RPC-over-stdio protocol for | ||
| agent-editor communication. Rather than adding a new per-agent harness for every | ||
| ACP-compatible CLI, a single `AcpHarness` implementation covers all of them. | ||
| The user supplies the command (e.g. `opencode`, `kimi`) and Warp handles the | ||
| protocol layer generically. | ||
|
|
||
| ### Relevant files | ||
|
|
||
| - `crates/warp_cli/src/agent.rs:123` — `pub enum Harness` — needs new `Acp` variant | ||
| - `app/src/terminal/cli_agent.rs:108` — `pub enum CLIAgent` — needs new `Acp` variant with user-configurable command | ||
| - `app/src/ai/agent_sdk/driver/harness/mod.rs:57` — `ThirdPartyHarness` trait definition — the contract `AcpHarness` must implement | ||
| - `app/src/ai/agent_sdk/driver/harness/gemini.rs` — `GeminiHarness` — closest structural analog to `AcpHarness` | ||
| - `app/src/ai/agent_sdk/driver/harness/mod.rs:133` — `HarnessKind` enum — may need `Acp` variant | ||
| - `app/src/terminal/view/ambient_agent/harness_selector.rs:61` — harness dropdown — needs ACP entry | ||
| - `app/src/server/server_api/harness_support.rs` — server-side harness registration — needs ACP format slug | ||
|
|
||
| ## Proposed changes | ||
|
|
||
| ### 1. `crates/warp_cli/src/agent.rs` | ||
|
|
||
| Add a new `Acp` variant to `pub enum Harness`: | ||
|
|
||
| ```rust | ||
| /// Delegate to any ACP-compatible agent CLI (user-configured command). | ||
| #[value(name = "acp")] | ||
| Acp, | ||
| ``` | ||
|
|
||
| Place between `OpenCode` and the `Unknown` fallback variant. | ||
|
|
||
| ### 2. `app/src/terminal/cli_agent.rs` | ||
|
|
||
| Add `Acp` to `pub enum CLIAgent`. Unlike other variants whose commands are | ||
| hardcoded, `CLIAgent::Acp` carries the user-supplied command string: | ||
|
|
||
| ```rust | ||
| /// An ACP-compatible agent with a user-configured command. | ||
| Acp(String), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| ``` | ||
|
|
||
| Update `command_prefix()`, `display_name()`, and `icon()` match arms: | ||
|
|
||
| ```rust | ||
| // command_prefix | ||
| CLIAgent::Acp(cmd) => cmd.as_str(), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| // display_name | ||
| CLIAgent::Acp(cmd) => cmd.as_str(), // show the actual command as the name | ||
|
|
||
| // icon — no dedicated icon yet; fall through to None | ||
| CLIAgent::Acp(_) => None, | ||
| ``` | ||
|
|
||
| ### 3. New file: `app/src/ai/agent_sdk/driver/harness/acp.rs` | ||
|
|
||
| Create `AcpHarness` implementing `ThirdPartyHarness`. The struct carries the | ||
| user-configured command and optional args: | ||
|
|
||
| ```rust | ||
| pub(crate) struct AcpHarness { | ||
| command: String, | ||
| args: Vec<String>, | ||
| } | ||
| ``` | ||
|
|
||
| Key `ThirdPartyHarness` method implementations: | ||
|
|
||
| - `harness()` → `Harness::Acp` | ||
| - `cli_agent()` → `CLIAgent::Acp(self.command.clone())` | ||
| - `validate()` → call `validate_cli_installed(&self.command, self.install_docs_url())` | ||
| - `install_docs_url()` → `None` (unknown for arbitrary agents; follow-up can add a registry) | ||
| - `prepare_environment_config()` → write ACP session config if needed; for v1 this may be a no-op or minimal JSON config file with MCP server list | ||
| - `build_command()` → spawn `self.command` with `self.args`, plus the stdio transport flags required by ACP (`--acp` or similar, depending on agent) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| The ACP protocol handshake (JSON-RPC `initialize` / `initialized` exchange over | ||
| stdio) is handled in a new `run_acp_session()` function within this file, called | ||
| from `run()`. The initialize request must include: | ||
|
Comment on lines
+93
to
+95
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| ```json | ||
| { | ||
| "jsonrpc": "2.0", | ||
| "id": 1, | ||
| "method": "initialize", | ||
| "params": { | ||
| "clientInfo": { "name": "warp", "version": "<warp_version>" }, | ||
| "capabilities": {} | ||
| } | ||
|
Comment on lines
+97
to
+105
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚨 [CRITICAL] The initialize example is not ACP-compatible: the request must include |
||
| } | ||
| ``` | ||
|
|
||
| Warp then reads the `InitializeResult` from the agent's stdout, sends | ||
| `initialized`, and transitions to the active session loop. | ||
|
|
||
| **Timeout:** If no `InitializeResult` arrives within 10 seconds, `run()` returns | ||
| `AgentDriverError::HarnessConfigSetupFailed` with a descriptive message. | ||
|
|
||
| **MCP passthrough:** After initialization, if Warp has configured MCP servers, | ||
| they are passed to the agent via the ACP session configuration per the protocol | ||
| spec before the first prompt is sent. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| ### 4. `app/src/ai/agent_sdk/driver/harness/mod.rs` | ||
|
|
||
| - Add `mod acp;` and `pub(crate) use acp::AcpHarness;` | ||
| - Add `Acp` variant to `HarnessKind` (line 133) if this enum is exhaustively matched anywhere | ||
| - Register `AcpHarness` in the harness dispatch logic alongside `ClaudeHarness` and `GeminiHarness` | ||
|
|
||
| ### 5. `app/src/terminal/view/ambient_agent/harness_selector.rs` | ||
|
|
||
| Add `Harness::Acp` to the harness selector dropdown. For user-configured ACP | ||
| agents, each appears as a separate entry using its display name (the command | ||
| string). This requires reading the list of configured ACP agents from settings | ||
| and generating one selector entry per agent. | ||
|
Comment on lines
+127
to
+132
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| ### 6. `app/src/server/server_api/harness_support.rs` | ||
|
|
||
| Add an `"acp"` format slug for ACP conversations, used when creating a | ||
| conversation record on the server. This follows the pattern of `"gemini_cli"` | ||
| for Gemini. | ||
|
|
||
| ## Data flow | ||
|
|
||
| ``` | ||
| User selects ACP agent + sends prompt | ||
| → AcpHarness::validate() checks command on PATH | ||
| → AcpHarness::build_command() spawns process over stdio | ||
| → run_acp_session() sends initialize, awaits InitializeResult (10s timeout) | ||
| → sends initialized notification | ||
| → passes MCP servers to session config (if any) | ||
| → enters prompt/response loop: | ||
| Warp sends prompt as ACP session/message | ||
| Agent streams response chunks | ||
| Warp renders chunks as conversation blocks | ||
| File edits → diff view | ||
| Tool calls → tool call blocks | ||
|
Comment on lines
+150
to
+154
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| → on agent exit or crash → conversation marked ended | ||
| ``` | ||
|
|
||
| ## Tradeoffs | ||
|
|
||
| - **Per-agent command vs. shared variant:** Storing the command inside | ||
| `CLIAgent::Acp(String)` is a departure from the hardcoded variants pattern. | ||
| This makes serialization slightly more complex but avoids adding a new enum | ||
| variant for every ACP agent. If a specific ACP agent later warrants a custom | ||
| harness (e.g. for session resumption), it can graduate to its own variant. | ||
| - **Stdio-only in v1:** Excludes ACP agents that communicate over HTTP. This | ||
| covers the majority of current ACP agents (opencode, Kimi, Gemini, Codex all | ||
| support stdio) and avoids auth complexity for v1. | ||
|
|
||
| ## Testing and validation | ||
|
|
||
| Invariant-to-test mapping (from `product.md` success criteria): | ||
|
|
||
| 1. **Unit test in `acp.rs`:** `AcpHarness::validate()` returns `Ok(())` for a | ||
| command on PATH and `Err(AgentDriverError::CliNotInstalled)` for a missing | ||
| command. | ||
| 2. **Unit test:** `run_acp_session()` returns a timeout error if the mock agent | ||
| process does not send `InitializeResult` within the deadline. | ||
| 3. **Integration test under `crates/integration/`:** Spawn a minimal ACP echo | ||
| agent (a small test binary that implements initialize/initialized and | ||
| returns a static response), send a prompt, and assert a conversation block | ||
| is produced. | ||
| 4. **Manual:** Configure `opencode` as an ACP agent, send a prompt, confirm | ||
| response renders as a Warp conversation block. | ||
| 5. **Manual:** Confirm `opencode` appears in the harness selector alongside | ||
| Claude Code, Codex, and Gemini. | ||
| 6. **Regression:** `cargo nextest run` passes across existing harness tests | ||
| (`local_harness_launch_tests.rs`, `cli_agent_tests.rs`, | ||
| `mod_test.rs` in `driver/harness/`). | ||
|
|
||
| ## Risks and mitigations | ||
|
|
||
| - **ACP spec churn:** The ACP spec is still evolving. Mitigation: pin to the | ||
| stable `initialize`/`initialized`/`session/message` core and treat unknown | ||
| fields as ignored (forward-compatible deserialization with `#[serde(flatten)]` | ||
| or `deny_unknown_fields = false`). | ||
| - **CLIAgent enum serialization:** Adding `Acp(String)` changes the shape of a | ||
| serialized enum. Mitigation: verify that `CLIAgent` is not persisted to disk | ||
| or sent over the network in a way that would break existing stored data; if it | ||
| is, add a migration. | ||
| - **No icon for ACP agents:** The harness selector will show ACP agents without | ||
| a logo. Mitigation: acceptable for v1; a generic "agent" icon can be added as | ||
| a follow-up. | ||
|
|
||
| ## Follow-ups | ||
|
|
||
| - Session resumption for ACP agents (requires ACP `loadSession` capability). | ||
| - A curated registry of known ACP agents with pre-filled commands and install URLs. | ||
| - Warp as ACP server (exposing Warp Agent to external IDEs like Zed). | ||
| - Per-agent icons for known ACP CLIs. | ||
| - HTTP transport support for remote ACP agents. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.