Daemon module providing the ACP and pi RPC subprocess protocol adapters used to drive external AI agent CLIs from the Open Design daemon.
The module was originally two flat source files alongside a shared helper:
acp.ts(~1,744 lines) β the ACP (Agent Control Protocol) adapter, covering session setup, JSON-line streaming, model detection, and MCP server forwarding.pi-rpc.ts(~684 lines) β the pi--mode rpcadapter, covering prompt delivery, event mapping, session-file capture, and image forwarding.
Both files imported createJsonLineStream from the same JSON-line transport helper. The flat layout made the dependency between pi-rpc and acp invisible, and any file could reach into the other's internals.
The refactor used a strangler-fig pattern β no logic changes, only structural moves:
- Created
core/,acp/, andpi-rpc/subdirectories. - Moved
createJsonLineStreamintocore/json-line-stream.tsβ the one primitive shared by both adapters. - Split
acp.tsinto eight concern files underacp/:types.ts,constants.ts,json.ts,models.ts,rpc.ts,session-params.ts,session.ts,updates.ts. - Split
pi-rpc.tsinto four concern files underpi-rpc/:internal.ts,events.ts,models.ts,session.ts. - Added per-subdirectory barrel
index.tsfiles with@moduledocblocks. - Made the root
index.tsre-export only from those subdir barrels, preserving the exact prior public surface (10 names). - Added
@moduledocblocks and per-export JSDoc to every file.
The public API surface (index.ts exports) is unchanged β external consumers see no difference.
Moving createJsonLineStream into core/ dissolved the former pi-rpc β acp import edge that existed in the flat layout. The result is a pure star topology:
core/ β acp/
core/ β pi-rpc/
Neither acp/ nor pi-rpc/ imports the other. The declared allowedEdges for this domain is [] β no cross-concern imports are permitted at all. Any future shared primitive must land in core/ rather than creating a lateral edge.
This layout was chosen for the same reason as the design-systems reference implementation: structure without enforcement is cosmetic. The guard registration is intentionally deferred (see "Known limitations" below), but the full documented structure is in place so the registration is a single-line addition once the infra lands on main.
- All relative imports use
.jsextensions (Node ESM). core/is the foundation kernel. Bothacp/andpi-rpc/may import fromcore/directly ('../core/index.js'or a'../core/<file>.js'path);core/itself imports no sibling.acp/andpi-rpc/must not import each other. Cross-concern imports go only through the root barrel ('../index.js'), which is itself off-limits to the concern subdirs (it re-exports all of them and would create a cycle). If a new shared primitive is needed, add it tocore/.- The root barrel uses explicit named re-exports β never
export *β so the public surface is enumerable and free of silent name collisions. - External daemon code imports from
'./agent-protocol/index.js'(or the subpath equivalent) β never from a subdirectory path directly.
The 10 names on the public surface, in barrel order:
createJsonLineStream // core/
AcpMcpServerInput // acp/ (type)
ModelOption // acp/ (type)
buildAcpSessionNewParams // acp/
normalizeModels // acp/
detectAcpModels // acp/
attachAcpSession // acp/
mapPiRpcEvent // pi-rpc/
attachPiRpcSession // pi-rpc/
parsePiModels // pi-rpc/
Guard registration is intentionally deferred. The check-barrel-imports.ts guard infra (CAPABILITY_BARREL_DOMAINS registry, pnpm guard wiring) is not yet on main. The full capability-barrel structure β core/ + concern subdirs, @module docblocks, per-export JSDoc, this README β is in place. The CAPABILITY_BARREL_DOMAINS entry lands in a follow-up once the guard infra merges.
core/ currently contains one file. json-line-stream.ts is the only member. A single-file core/ is justified here: it is the shared transport primitive that both adapters depend on, and the subdir makes the dependency direction explicit without requiring a comment to explain it.
agent-protocol/
βββ index.ts Root barrel β named re-exports from core/, acp/, and pi-rpc/
βββ core/
β βββ index.ts core/ barrel
β βββ json-line-stream.ts createJsonLineStream: shared JSON-line transport
βββ acp/
β βββ index.ts acp/ barrel
β βββ types.ts Shared ACP types and interfaces
β βββ constants.ts Protocol constants (method names, timeouts)
β βββ json.ts JSON-line parsing helpers for ACP stdout
β βββ models.ts normalizeModels, detectAcpModels
β βββ rpc.ts Low-level RPC send/receive helpers
β βββ session-params.ts buildAcpSessionNewParams
β βββ session.ts attachAcpSession: session lifecycle
β βββ updates.ts ACP update-event handling
βββ pi-rpc/
βββ index.ts pi-rpc/ barrel
βββ internal.ts Shared primitives: JsonRecord, SendAgentEvent, TokenUsage, guards
βββ events.ts mapPiRpcEvent: pure pi RPC β daemon event mapper
βββ models.ts parsePiModels: `pi --list-models` parser
βββ session.ts attachPiRpcSession: session lifecycle, image forwarding, abort
| Consumer | What it imports |
|---|---|
runtimes/defs/shared.ts |
detectAcpModels, parsePiModels |
connectionTest.ts |
attachAcpSession |
server.ts |
attachAcpSession, attachPiRpcSession |