Skip to content

overview architecture

Zachary BENSALEM edited this page Aug 15, 2026 · 1 revision

Architecture

Prime Agent is a TypeScript monorepo with one core runtime and three client surfaces. The core is @earendil-works/pi-coding-agent (packages/coding-agent/); the clients are the terminal UI (packages/tui/), the CLI/daemon (packages/coding-agent/src/modes/), and the web chat (web/). Every client reaches the runtime through the same typed connection seam, AgentConnection (packages/coding-agent/src/modes/agent-connection/), never through raw session internals.

System overview

graph TD
    TUI[Terminal UI<br/>packages/tui] -->|in-process or daemon socket| CONN[AgentConnection seam]
    CLI[CLI / daemon<br/>packages/coding-agent modes] -->|Unix socket, protocol v8| CONN
    WEB[Web chat<br/>web/app + web/server] -->|in-process| CONN
    CONN --> RUNTIME[Session runtime<br/>AgentSession, SessionManager]
    RUNTIME --> BUS[Event bus]
    RUNTIME --> KERNEL[IPython kernel<br/>packages/coding-agent/src/core/kernel]
    RUNTIME --> LLM[LLM providers<br/>packages/ai]
    RUNTIME --> STORE[(JSONL transcripts<br/>~/.prime/agent/sessions/)]
    BUS --> TUI
    BUS --> WEB
    LLM -->|stream events| RUNTIME
Loading

Language and the connection seam

The seam is deliberately narrow, documented in CONTEXT.md and enforced by docs/adr/0001-kill-interactivemode-localsessionhost.md:

  • AgentConnection is the only supported client-side surface: session state, event streams, and commands flow through it. It carries an extensions sub-interface (argument completions, diagnostics, shortcuts, message/tool renderers, bindExtensions) that is permanently process-local; daemon adapters throw AgentConnectionUnsupportedError.
  • SessionView is a read-only, serializable projection of AgentSession (cwd, session dir, header, context-tree walks, session-file materialization) that deliberately excludes mutation.
  • afterReplace with a narrow ReplacedClientContext (sendUserMessage, notify, setEditorText) is the supported hook for client work after a session swap (newSession/fork/switchSession).
  • seedMessages populates a freshly created session with initial messages and is wire-supported on the daemon at protocol version 8.

The previous backdoor, InteractiveModeLocalSessionHost, was deleted because every method on it bypassed the seam and produced daemon-incompatible features.

Web architecture

The web UI is a single-process Node dev server: TanStack Start (web/app) hosts HTTP routes that are thin wrappers over web/server handlers, which in turn call PrimeBridge (web/server/src/prime-bridge.ts). The bridge owns live AgentSessions keyed by sessionId, ring buffers of the last 500 event frames per session (the SSE replay source), a PendingDialogRegistry with a 60-second timeout for interactive questions, and a kernel-readiness promise via IpythonKernelProvisioner.ensure().

graph LR
    B[Browser] -->|NDJSON POST /api/chat| TS[TanStack Start routes]
    B -->|SSE GET /api/chat/events| TS
    TS --> H[web/server handlers]
    H --> PB[PrimeBridge]
    PB -->|sessions map| S[AgentSession]
    PB -->|ring buffers 500 frames| RB[RingBuffer]
    PB -->|PendingDialogRegistry 60s| PD[Pending dialogs]
    PB -->|kernelReady| KP[IpythonKernelProvisioner]
    S --> CA[packages/coding-agent]
Loading
  • Turns stream as NDJSON over POST /api/chat; the assistant turn renders in place in the browser.
  • Out-of-turn events (tool questions, status/notify state, agent messages) push over SSE with ring-buffer replay: the client reconnects with Last-Event-ID and the server replays missed frames; overflow emits state: resync-required so the UI falls back to GET /api/chat/session.
  • confirm/select/input become tool-Question frames resolved via POST /api/chat/question; notify/setStatus/setWidget become state frames.
  • web/server/src/event-mapper.ts is a pure function AgentSessionEvent → ChatStreamEvent[]; tool names pass through toPascalCase (tool-IPython, tool-Bash, tool-Edit, tool-Thinking, ...). Tool renderers in web/design/src/components/agent-elements/tools/ dispatch on part.type.
  • The wire contract lives in web/protocol/src/chat-protocol.ts (types) and web/protocol/src/chat-protocol.zod.ts (validation schemas).
  • Optional VITE_FLEET_PI_CHAT_RUNTIME_URL points the browser at a remote runtime; handlers are process-agnostic Request → Response functions.
  • Sessions persist as JSONL transcripts under ~/.prime/agent/sessions/ and resume after reload; the SSE cursor survives page reloads in sessionStorage.

Current web limitations: no daemon-backed attach (in-process connection), no multi-user auth (binds to 127.0.0.1 with no tokens), and pending dialogs are not persisted across restart.

Terminal and daemon architecture

The TUI (packages/tui/) is a rendering engine (double-buffer differential rendering over a terminal abstraction) plus components; the interactive mode in packages/coding-agent/src/modes/interactive/ drives it through AgentConnection. The daemon (packages/coding-agent/src/modes/daemon/) is a separate long-running process reachable over a Unix socket with a versioned wire protocol (DAEMON_PROTOCOL_VERSION 8, DAEMON_SCHEMA_REVISION 15 in packages/coding-agent/src/modes/daemon/daemon-protocol.ts). Sessions keep running when the terminal disconnects and can be reattached with prime-agent attach. Every wire change is classified as backward-compatible, capability-gated, or incompatible, and dual-compat tests cover new-client/old-daemon and old-client/new-daemon combinations.

Language and size snapshot

The codebase is predominantly TypeScript, with a small Python package for the IPython kernel shim. Rough source sizes at HEAD (57e3d5445): packages/coding-agent/src ~44.6k lines across 270 files, packages/ai/src ~10.6k lines across 51 files, packages/tui/src ~5.2k lines across 32 files, web/design/src ~4.3k lines across 164 files, web/server/src ~2.9k lines, and prime-agent-runtime/src ~1.5k lines of Python. See By the numbers for the full snapshot.

Related pages

Clone this wiki locally