-
Notifications
You must be signed in to change notification settings - Fork 0
overview 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.
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
The seam is deliberately narrow, documented in CONTEXT.md and enforced by docs/adr/0001-kill-interactivemode-localsessionhost.md:
-
AgentConnectionis the only supported client-side surface: session state, event streams, and commands flow through it. It carries anextensionssub-interface (argument completions, diagnostics, shortcuts, message/tool renderers,bindExtensions) that is permanently process-local; daemon adapters throwAgentConnectionUnsupportedError. -
SessionViewis a read-only, serializable projection ofAgentSession(cwd, session dir, header, context-tree walks, session-file materialization) that deliberately excludes mutation. -
afterReplacewith a narrowReplacedClientContext(sendUserMessage,notify,setEditorText) is the supported hook for client work after a session swap (newSession/fork/switchSession). -
seedMessagespopulates 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.
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]
- 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-IDand the server replays missed frames; overflow emitsstate: resync-requiredso the UI falls back toGET /api/chat/session. -
confirm/select/inputbecometool-Questionframes resolved viaPOST /api/chat/question;notify/setStatus/setWidgetbecomestateframes. -
web/server/src/event-mapper.tsis a pure functionAgentSessionEvent → ChatStreamEvent[]; tool names pass throughtoPascalCase(tool-IPython,tool-Bash,tool-Edit,tool-Thinking, ...). Tool renderers inweb/design/src/components/agent-elements/tools/dispatch onpart.type. - The wire contract lives in
web/protocol/src/chat-protocol.ts(types) andweb/protocol/src/chat-protocol.zod.ts(validation schemas). - Optional
VITE_FLEET_PI_CHAT_RUNTIME_URLpoints the browser at a remote runtime; handlers are process-agnosticRequest → Responsefunctions. - Sessions persist as JSONL transcripts under
~/.prime/agent/sessions/and resume after reload; the SSE cursor survives page reloads insessionStorage.
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.
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.
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.
- Packages, per-package deep dives
- Agent connection seam, daemon protocol and wire compatibility
- Streaming chat, NDJSON and SSE flows end to end
- Web API, HTTP endpoint reference
- Design decisions, why the seam is the way it is