Skip to content

Architecture

AgentPane is a Turborepo monorepo with three packages:

  • apps/server — Hono HTTP server with Effect.ts service layers (port 3456)
  • apps/web — Next.js 16 App Router with React 19 (port 6767)
  • apps/docs — Astro + Starlight documentation site

npx agentpane starts two processes: the Hono API server on port 3456 and a Next.js standalone server on port 6767. In development, the Next.js dev server rewrites /api/* to the API server. No auth or CORS — everything is same-origin.

┌──────────────────────┐ ┌──────────────────────┐
│ Next.js (port 6767) │ │ Hono (port 3456) │
│ │ │ │
│ App Router UI │──▶│ /api/* routes │
│ (rewrites /api/*) │ │ │
└──────────────────────┘ │ Effect.ts Services │
│ ┌──────────────────┐ │
│ │ ConnectionManager │ │
│ │ PromptEngine │ │
│ │ EventHub │ │
│ │ WriteQueue │ │
│ │ SessionRepo │ │
│ └────────┬─────────┘ │
│ │ │
│ ┌────────┴────┐ │
│ │ SQLite │ │
│ │ (WAL) │ │
│ └─────────────┘ │
│ │
│ ┌────────────────┐ │
│ │ Agent (stdio) │ │
│ │ ACP/JSON-RPC │ │
│ └────────────────┘ │
└──────────────────────┘

The backend uses Effect.ts for dependency injection and error handling. Services are composed into a ManagedRuntime:

ServiceResponsibility
AcpClientFacade composing ConnectionManager, EventHub, PromptEngine, and WriteQueue
ConnectionManagerAgent subprocess lifecycle, ACP connection setup, session load/resume
PromptEnginePrompt execution orchestration, turn persistence, token tracking
EventHubSession-level event broadcaster management with idle cleanup
WriteQueueBatched DB writes (50ms debounce per session, crash recovery)
SessionRepoCRUD for sessions, turns, and message blocks in SQLite
SqliteLiveDatabase connection, migrations, WAL mode, foreign keys

Domain errors use Schema.TaggedError with httpStatus and httpMessage getters. The runEffect bridge function maps Effect exits to HTTP responses automatically.

The frontend is a Next.js 16 App Router application — pure UI with no backend dependencies. It communicates with the API via plain fetch using relative URLs (same-origin, no auth tokens).

Key patterns:

  • React Query — All server state with SSR prefetch and dehydration, query invalidation on SSE events
  • Always-on EventSource — SSE connection stays open regardless of agent connection state
  • React Compiler — Automatic memoization, no manual useMemo/useCallback
  • Streaming markdown — Via Streamdown with Shiki syntax highlighting

AgentPane talks to agents over the Agent Client Protocol (ACP) — JSON-RPC 2.0 over stdio:

  1. The server spawns the agent binary as a child process
  2. Communication uses newline-delimited JSON over stdin/stdout
  3. Events are broadcast to the frontend via SSE (Server-Sent Events)

Session-level broadcasters survive agent disconnects, delivering connection status events to keep the UI in sync.

SQLite with WAL mode, stored at ~/.agentpane/agentpane.db (configurable via AGENTPANE_DATA_DIR). Three tables:

  • sessions — id, name, working directory, agent type, timestamps
  • turns — id, session reference, role (user/assistant), stop reason
  • message_blocks — id, turn reference, kind, content