Editor Compatibility

One Core, Many Surfaces

An agent harness is not a CLI. It is a core runtime — conversation loop, tool registry, permission model, configuration — presented through interface surfaces. Build the core as a library with no I/O. Build each surface as a thin translation layer between the core’s event stream and whatever medium the user is working in.

This separation is not optional for any agent that intends to reach developers where they work: terminal, VS Code, JetBrains, Neovim, web. An agent that lives in only one of these is an agent most developers cannot use.


The Interface Matrix

Each surface has distinct constraints on transport, input handling, output rendering, and lifecycle.

SurfaceTransportInputOutputLifecycle
CLI / REPLstdin/stdoutLine editing, signal handling (Ctrl+C/D)Streaming markdown, syntax highlightingProcess lifetime = session lifetime
VS CodeJSON-RPC over stdio or WebSocketExtension API, editor selections, file contextWebview panels, inline annotations, decorationsExtension host; survives restart via state serialization
JetBrainsHTTP/WebSocket or stdioPlugin API, PSI tree context, selectionsTool windows, editor inlays, balloonsJVM plugin lifecycle; handles project open/close
Neovimstdio or TCP (Lua RPC)Lua callbacks, buffer context, visual selectionsFloating windows, virtual text, quickfixPlugin lazy-loaded on demand
WebHTTP/SSE or WebSocketREST / WebSocket messagesJSON event stream rendered by frontendStateless server; session state externalized

The Harness Event Stream

The core emits a single, interface-agnostic event stream. Every surface consumes it and translates into native presentation.

graph LR
    subgraph core ["Agent Core"]
        CL["Conversation Loop"]
        TR["Tool Registry"]
        PM["Permission Model"]
    end

    ES["Event Stream"]

    subgraph surfaces ["Interface Surfaces"]
        CLI["CLI / REPL"]
        VSC["VS Code Extension"]
        JB["JetBrains Plugin"]
        WEB["Web Server"]
    end

    core --> ES
    ES --> CLI
    ES --> VSC
    ES --> JB
    ES --> WEB

Illustrative Event Vocabulary

The shape below is pedagogical, not a spec. Anthropic’s Messages API streams content_block_start/delta, message_delta, message_stop, tool_use blocks. ACP defines its own JSON-RPC method set (session/prompt, session/update, …). OpenAI’s Responses API has yet another shape. The architectural principle — one core, many surfaces, a single event vocabulary between — holds even though no standard names the events.

EventPayloadCLI renders asIDE renders asWeb emits as
MessageStart{ role, turn_id }Newline + role prefixNew chat bubbleSSE message_start
TokenDelta{ text }Append to terminalAppend to webviewSSE token_delta
ToolCallRequest{ tool, args }Print tool + argsInline annotationJSON tool_request
PermissionPrompt{ tool, args, risk }Blocking readline (y/n)Modal dialogJSON awaiting frontend confirm
ToolCallResult{ tool, status, content }Print result, highlightedCollapsible chat panelSSE tool_result
Error{ code, message }stderr in redNotification balloonSSE error

The key principle: the core never imports terminal, editor, or HTTP libraries. It emits events; surfaces translate.


Permission Prompts Across Surfaces

Permission handling is the hardest interface problem. The core must not block on a prompt. It emits an async event and awaits an async response. The surface decides how to collect the decision: CLI uses blocking readline; IDE uses a modal dialog; web uses an HTTP round-trip.

sequenceDiagram
    participant C as Agent Core
    participant S as Interface Surface

    C->>S: PermissionPrompt { tool: "bash", args: "rm -rf dist/", risk: "destructive" }
    Note over S: CLI: blocking readline<br/>IDE: modal dialog<br/>Web: async HTTP
    alt User approves
        S->>C: PermissionResponse { approved: true }
        C->>C: Execute tool
    else User denies
        S->>C: PermissionResponse { approved: false }
        C->>C: Return denial to model
    end

Timeouts and batch approvals (“approve all bash(git *) for this session”) are surface-specific. The core sees individual approve/deny; the surface manages the UX.


Core Extraction Pattern

The architectural pattern is core extraction: agent logic as a library with no I/O dependencies; each interface as a thin shell wiring the library to its environment.

agent-core/          # Library — no I/O, no UI
  conversation.rs    # Agentic loop, tool dispatch
  tools.rs           # Tool registry, execution
  permissions.rs     # Permission model, approval flow
  config.rs          # Configuration loading, merging
  events.rs          # Event type definitions

agent-cli/           # Binary — depends on agent-core
agent-vscode/        # Extension — depends on agent-core (subprocess/WASM)
agent-jetbrains/     # Plugin — depends on agent-core (subprocess/HTTP)
agent-server/        # HTTP server — depends on agent-core

The litmus test: can you test the core without an interface? A mock surface that records events and replays permission responses should be enough to drive the full conversation loop. If your tests require a terminal emulator or a browser, the abstraction is leaking.


Current Cross-Platform Adoption (May 2026)

PlatformReach
Claude CodeFirst-party VS Code extension (5.2M installs) + JetBrains plugin (beta, IntelliJ/PyCharm/WebStorm/etc.) + community coder/claudecode.nvim (WebSocket MCP).
OpenAI CodexFirst-party VS Code extension (4.9M installs) + native JetBrains integration since Jan 2026 (IntelliJ 2025.3+, PyCharm, WebStorm, Rider).
Gemini CLIVS Code Companion extension + JetBrains via ACP + Neovim via ACP (CodeCompanion, avante.nvim).
Cursor / WindsurfSingle-surface VS Code forks. No JetBrains build, no Neovim plugin.
Aider / othersTerminal-only. No first-party IDE story.

Agents that extract the core early (Claude Code, Codex, Gemini CLI) expand to new surfaces without rewriting agent logic. Agents that embed logic in the interface (Cursor, Windsurf) are locked to a single editor fork.


Agent Client Protocol (ACP)

The emerging cross-editor standard is the Agent Client Protocol (ACP), originated by Zed and backed by JetBrains since their Oct 2025 partnership. ACP is to agent-to-IDE communication what LSP is to language servers: a JSON-RPC method set (session/prompt, session/update, …) any editor can speak to any conforming agent.

If you are building a new agent and want IDE reach without writing N native extensions, ACP is the lowest-cost path to plurality.


State Synchronization Across Surfaces

When the same core serves multiple surfaces at once (CLI plus VS Code), session state must be synchronized or explicitly isolated.

StrategyHow it worksTradeoff
Exclusive sessionsEach surface owns its session. No sharing.Simple; users lose continuity when switching.
Shared serverLocal daemon runs the core; CLI and IDE are clients.Full continuity; adds a daemon to manage.
State file lockingSession state on disk; surfaces acquire a lock.Works offline; lock contention possible.
Cloud syncState in a remote service; all surfaces read/write via API.Works across machines; needs network.

The shared-server pattern is emerging as dominant for local development. One agent-server process owns sessions; CLI and IDE extension connect as clients. This avoids duplicate model calls and keeps a tool call initiated in VS Code visible in the terminal — what users expect once they have the agent in more than one place.