-
Notifications
You must be signed in to change notification settings - Fork 0
background design decisions
The documented rationale behind the codebase's shape: the seam language clients must use, the removal of a convenience backdoor, the fork's relationship to upstream, the web hardening waves, and the local-first posture of the web UI. Each section points to the file that records the decision.
CONTEXT.md defines the canonical "language" of the project: terminal clients, the headless daemon, and the web adapter all drive the same core session runtime through a single typed connection seam called AgentConnection. Session state, event streams, and commands flow through this interface and never directly to AgentSession or its internal managers.
The supported vocabulary is deliberately narrow:
-
AgentConnection, the single client-side seam between any front end and the agent runtime. -
extensions, a sub-interface onAgentConnectionexposing extension-runtime surface (argument completions, diagnostics, shortcuts, message/tool renderers,bindExtensions). It is permanently process-local; daemon adapters throwAgentConnectionUnsupportedError. Related process-local top-level members aregetAbortSignal,getReadonlySessionManager,getSystemPromptSync,setup, andwithSession. -
SessionView, a read-only, serializable projection ofAgentSession(cwd, session dir, header, context-tree walks, session-file materialization) that deliberately excludes mutation and anything needing the liveSessionManagerorExtensionRunner. -
ReplacedClientContext, the narrow surface given toafterReplacehooks after anewSession/fork/switchSessionswap: onlysendUserMessage,notify, andsetEditorText. -
afterReplace, the single supported hook shape for client-side work that must run immediately after a session swap; typed on the connection methods so daemon-backed clients can queue it over the wire. -
seedMessages, the supported way to populate a freshly created session with initial messages without a raw session handle; the daemonnew_sessioncommand carries them at protocol >= 8.
Anti-terms are listed so authors do not reintroduce them: InteractiveModeLocalSessionHost, host, bridge, SessionManager (as a seam type), Session, withSession, setup, and inventing wire shapes for executable callbacks.
docs/adr/0001-kill-interactivemode-localsessionhost.md records the decision to delete InteractiveModeLocalSessionHost, a documented "legacy" backdoor in packages/coding-agent/src/modes/interactive/interactive-mode-services.ts that exposed raw AgentSession, SessionManager, and ExtensionRunner, plus runtimeHost.session.agent.signal, to terminal UI code whenever the connection was in-process. Every feature built through the host silently became daemon-incompatible and undercut the AgentConnection contract from the inside.
The gap was closed by widening AgentConnection with the extensions sub-interface, a read-only SessionView, an afterReplace hook with a narrow ReplacedClientContext, and wire support for seedMessages on new_session at DAEMON_PROTOCOL_VERSION 8. The extension surface (executable callbacks), getAbortSignal, getReadonlySessionManager, getSystemPromptSync, and setup/withSession remain permanently process-local; daemon adapters throw AgentConnectionUnsupportedError. Dual-compat tests cover new-client/old-daemon and old-client/new-daemon for seedMessages.
The considered options were:
- Keep the host and document it as "in-process only", rejected because it had already produced many call sites in one file and direct
session.agent.transport =writes from the daemon; "document-only" had not worked. - Extend existing
AgentConnectionmethods with optional parameters, rejected because it would hide process-local operations (extension introspection) inside uniformly wire-capable calls and make caller intent opaque. - Invent wire shapes for executable callbacks, rejected because shortcut handlers, message/tool renderers, and
bindExtensionscannot serialize across the daemon socket without a different product contract.
The consequences: portable clients use AgentConnection for session ownership and seedMessages/afterReplace for new-session seeding and post-swap hooks; process-local extension members stay in-process and InteractiveMode degrades gracefully on daemon transports via tryExtensionSurface; the wire protocol sits at version 8 (schema revision 15) for seedMessages, so v7 clients must not send that field; createFakeAgentConnection(overrides) (which throws on unstubbed methods) remains the supported way to test interactive mode against the seam; and interactive-mode-boundary.test.ts asserts boundary awareness in addition to imports.
The repository is a hard fork of PrimeIntellect's prime-agent, originally badlogic's pi-mono by Mario Zechner. The fork deliberately retains the @earendil-works npm scope for the four core packages (pi-ai, pi-agent-core, pi-coding-agent, pi-tui) to preserve compatibility with the inherited package identifiers and their consumers.
The Qredence web UI under web/ is a standalone product and is deliberately not merged upstream. It is a separate pnpm workspace with its own contract (web/protocol/src/chat-protocol.ts), adapter (web/server, prime-bridge.ts and event-mapper.ts), and app layer. Browser code talks HTTP (NDJSON and SSE) only and never imports @earendil-works/*; web/server is the only web package that imports the agent packages. The merge boundary is strict: upstream merges land in packages/ only, never in web/. This keeps the two surfaces independently versioned and lets Qredence ship a web product on top of the agent core without coupling either side's releases.
The web chat renders generative UI through OpenUI. The wave-1 data display and charts work (LineChart, DonutChart, DataTable v2, and MetricGroup in the custom openUILibrary) shipped in web/design/src/components/openui/charts.tsx and web/design/src/components/openui/data.tsx, built on the existing primitives with no new dependencies. The constraints recorded for that work: named top-level function components (rules-of-hooks stays armed), zod-shaped props, and mirroring the BarChart positional-argument convention.
plans/ carries 12 improve-react hardening plans (numbered 001 to 012), all marked DONE. React Doctor (doctor.config.jsonc) sits at baseline score 100 against audit commit 89c8fb912. The plans cover chart color sanitization (XSS), settled-session reload gating, composer accessibility, streaming live-region throttling, settings dialog focus restore, markdown component hoisting, turn memo stabilization, SSE status reconnect, question and steer error surfacing, a markdown link allowlist, an OpenUI error boundary, and stopping stream subscription when settings close. The shared constraint is that npx react-doctor@0.9.5 --scope changed --project web/app,web/design must keep the score at 100 after each change.
The web UI is local-first. prime-agent web binds to 127.0.0.1:3000 by default (see scripts/prime-agent-web-launcher.mjs), uses the current directory as its workspace, and requires no account. The local-first posture means the packaged web runtime exposes no authentication layer by default and the browser talks only to the local server over HTTP (NDJSON and SSE). This is consistent with the desktop-style product posture: the web chat is a local frontend on the same machine's agent runtime rather than a multi-tenant hosted service.
- Background, the lens index
- Glossary, the seam terms defined
- Patterns and conventions, how the seam and wire rules are enforced
- History, the narrative around the seam removal and hardening wave