Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions specs/GH7326/product.md
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
Comment on lines +47 to +51
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] [SECURITY] The spec lets users configure arbitrary commands and args but does not define the execution trust boundary; require argv-based execution rather than shell interpolation, PATH/absolute-path resolution rules, and whether synced/shared config can cause another machine to execute a command.

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.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] This makes ACP sessions resumable in v1, but line 95 and tech.md line 199 make resumption an open/follow-up item; choose one requirement and align the success criteria.


### 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.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] This makes conversation history persistence a v1 success criterion, but the UX section says resumption may be out of scope and the tech spec does not define what ACP state is saved; specify whether v1 stores Warp block snapshots only, ACP transcripts/session IDs, or full agent-resumable history.


## 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)?

203 changes: 203 additions & 0 deletions specs/GH7326/tech.md
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),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] CLIAgent::Acp(String) conflicts with the current CLIAgent shape (Copy, Sequence, and &'static str command/display APIs), and lines 153-160 recommend a different fieldless/settings-backed design; choose one data model and update the proposed changes accordingly.

```

Update `command_prefix()`, `display_name()`, and `icon()` match arms:

```rust
// command_prefix
CLIAgent::Acp(cmd) => cmd.as_str(),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] command_prefix() currently returns &'static str and CLIAgent derives Copy/Sequence; CLIAgent::Acp(String) => cmd.as_str() will not fit those contracts, so specify the enum/API redesign or store configured ACP metadata elsewhere.


// 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)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] --acp or similar is not an implementable generic launch contract; specify whether ACP mode flags are entirely user-provided, come from per-agent presets, or are guaranteed by ACP-compatible CLIs.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] ACP does not define a universal --acp flag, so appending a generic transport flag would break arbitrary ACP agents; the spec should require users or a registry to provide the exact command/args needed for each agent.


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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] The core implementation path is underspecified: existing third-party harness runners launch terminal commands, while ACP needs a bidirectional JSON-RPC stdio process and an adapter into Warp's native conversation, diff, and tool-call UI.


```json
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"clientInfo": { "name": "warp", "version": "<warp_version>" },
"capabilities": {}
}
Comment on lines +97 to +105
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 [CRITICAL] The initialize example is not ACP-compatible: the request must include protocolVersion and clientCapabilities, the response is an initialize response, and ACP does not have an LSP-style initialized notification before session setup; rewrite this flow around initialize followed by session/new/session/prompt.

}
```

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.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] [SECURITY] Passing all configured MCP servers to arbitrary ACP agents changes the trust boundary and may expose local/private data or credentials; define user consent, default scoping, allow/deny behavior, and log redaction expectations.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] [SECURITY] MCP server configs can include secret-bearing env vars and executable args; specify whether forwarding them to an arbitrary configured ACP agent requires explicit user consent, how values are redacted from logs/snapshots, and whether any MCP servers are filtered.


### 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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] The settings/config story is incomplete; define the schema, persistence/sync behavior, stable identity for multiple ACP agents, and how selector state serializes a specific configured agent rather than only Harness::Acp.


### 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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] [SECURITY] The prompt loop omits the agent-to-client request surface (file reads/writes, terminal commands, permission requests); define how ACP requests map to Warp's existing approval UX, diff review, and workspace/path boundaries so third-party agents cannot bypass user review.

→ 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.