What Is the ACP (Agent Communication Protocol) Runtime in Routa?
The ACP runtime in Routa is the core subsystem that hosts Agent Communication Protocol sessions, responsible for launching, supervising, and communicating with external agent processes via JSON-RPC over stdio.
Routa’s ACP runtime serves as the bridge between the application’s orchestration layer and external AI agents like Claude-code, Opencode, or Codex. According to the phodal/routa source code, this runtime spans both the Next.js web backend and Rust desktop backend, exposing a unified HTTP/SSE interface while managing process lifecycles, session persistence, and health monitoring.
Architecture and Core Responsibilities
The ACP runtime is more than a simple process wrapper—it is a stateful subsystem that handles session management, transport encoding, and provider abstraction.
Session Lifecycle and Persistence
At the heart of the runtime sits the AcpSessionManager in src/core/acp/acp-session-manager.ts, which maintains a registry of active sessions, their IDs, providers, and status. This manager coordinates with src/core/acp/session-db-persister.ts to store chat history in SQLite, enabling session replay and debugging across restarts. When the orchestrator requests an agent, the session manager instantiates a new context and delegates process spawning to the process manager.
Process Management and JSON-RPC Transport
The AcpProcessManager (located in src/core/acp/acp-process-manager.ts) maps session IDs to live AcpProcess objects and implements start, stop, and reconnection semantics. Each AcpProcess instance in src/core/acp/acp-process.ts manages the low-level JSON-RPC read/write loop over stdio, encoding messages to the agent binary and forwarding notifications back to the AcpSessionManager. This layer handles health checks and error recovery when agent processes crash or become unresponsive.
Provider Abstraction Layer
Routa supports multiple ACP-compatible agents through a uniform adapter interface. Provider-specific logic resides in src/core/acp/provider-adapter/ (including claude-adapter.ts and opencode-adapter.ts), translating Routa’s internal event model into provider-specific JSON-RPC calls. This abstraction allows the runtime to treat Claude-code, Opencode, Docker-based agents, or custom binaries identically, regardless of their individual CLI quirks.
HTTP Bridge and Health Verification
The runtime exposes REST endpoints and Server-Sent Events (SSE) via src/app/api/acp/runtime/route.ts, implementing GET /api/acp/runtime for status checks and POST /api/acp/runtime for initialization. To verify connectivity before user interaction, src/core/acp/acp-warmup.ts executes a lightweight “ping” session when the UI calls /api/acp/warmup, confirming that the selected provider binary is reachable and functional.
Integration with Routa's Orchestration
The ACP runtime connects to Routa’s task orchestration through the Orchestrator class in src/core/orchestration/orchestrator.ts. When an orchestrated task requires agent assistance, the orchestrator requests a session from AcpSessionManager, which triggers AcpProcessManager to spawn the appropriate provider process. The frontend then communicates with this session via src/client/acp-client.ts, a typed façade that abstracts HTTP calls into methods like createSession, sendMessage, and streamEvents.
Working with the ACP Runtime: Code Examples
The following patterns demonstrate how to interact with the runtime using the TypeScript client library.
Warm-Up the Runtime
Verify that the ACP subsystem is functional before creating user-facing sessions:
import { AcpClient } from '@/client/acp-client';
async function initializeRuntime() {
await AcpClient.warmup();
console.log('ACP runtime ready');
}
This invokes POST /api/acp/warmup, internally handled by acp-warmup.ts to spawn a temporary validation session.
Create a New Session
Instantiate a session with a specific provider adapter and system prompt:
const session = await AcpClient.createSession({
provider: 'opencode',
systemPrompt: 'You are a helpful coding assistant.',
});
console.log('Session ID:', session.id);
The client calls POST /api/acp, triggering AcpSessionManager.createSession and ultimately spawning the process via AcpProcessManager.
Stream Messages and Responses
Send user input and subscribe to SSE events for agent responses:
async function chat(sessionId: string, message: string) {
await AcpClient.sendMessage(sessionId, {
role: 'user',
content: message
});
const eventSource = AcpClient.subscribe(sessionId);
eventSource.onmessage = (ev) => {
const update = JSON.parse(ev.data);
console.log('ACP update:', update);
};
}
This routes to POST /api/acp/{sessionId}/message and GET /api/acp/{sessionId}/events, with acp-process.ts handling the JSON-RPC translation layer.
Terminate a Session
Clean up resources when the conversation completes:
await AcpClient.terminateSession(sessionId);
This triggers DELETE /api/acp/{sessionId}, signaling AcpProcessManager to kill the underlying process and update persistence via session-db-persister.ts.
Summary
- The ACP runtime is Routa’s unified backend for hosting JSON-RPC agent sessions, spanning Next.js and Rust implementations.
- Process management in
acp-process-manager.tshandles spawning, health checks, and reconnection for child agent processes. - Session state is tracked by
acp-session-manager.tsand persisted to SQLite viasession-db-persister.ts. - Provider abstraction allows the runtime to communicate with Claude-code, Opencode, or Docker agents through adapter implementations in
src/core/acp/provider-adapter/. - HTTP/SSE bridge exposes the runtime via
/api/acp/runtimeendpoints, consumed by the front-endAcpClientand the orchestration layer.
Frequently Asked Questions
What is the difference between ACP runtime and a simple agent process?
The ACP runtime encompasses state persistence, lifecycle notifications, and health monitoring in addition to the child process itself. While a simple process only executes the agent binary, the runtime includes session-db-persister.ts for SQLite storage, lifecycle-notifier.ts for UI updates, and MCP integration for tool invocation, making it an observable, extensible environment rather than just a process wrapper.
How does the ACP runtime handle different agent providers?
The runtime uses a provider adapter pattern located in src/core/acp/provider-adapter/. Each adapter (e.g., claude-adapter.ts, opencode-adapter.ts) implements a uniform interface that translates between the provider’s specific JSON-RPC dialect and Routa’s internal SessionUpdateNotification events, allowing the core runtime to remain provider-agnostic.
What happens if an ACP agent process crashes during a session?
The AcpProcessManager monitors process health through the stdio pipe in acp-process.ts. If an agent exits unexpectedly, the manager triggers reconnection logic or marks the session as failed, notifying the UI via the lifecycle notifier. Session history remains available in SQLite via session-db-persister.ts, though the specific crashed process is terminated and cleaned up.
Can external tools interact with the ACP runtime outside the Routa UI?
Yes. The runtime exposes HTTP endpoints in src/app/api/acp/runtime/route.ts and SSE streams that any HTTP client can consume. The AcpClient in src/client/acp-client.ts serves as a reference implementation, but external scripts or CLI tools can directly call POST /api/acp to create sessions and GET /api/acp/{sessionId}/events to stream responses without using the React frontend.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →