-
Notifications
You must be signed in to change notification settings - Fork 0
packages coding agent daemon
Active contributors: kt, Sebastian Müller, Seth Karten
The daemon is the headless background service that keeps AgentSession instances alive when no terminal is attached. It listens on a per-user Unix socket (or a Windows named pipe), owns live session runtimes, and exposes a versioned JSONL wire protocol so clients can create, attach to, detach from, prompt, and administer sessions without disposing the underlying agent loop. A global supervisor owns the socket and spawns one isolated worker process per root session; each worker runs the same AgentDaemon in worker mode. Sessions keep running when the terminal disconnects and can be reattached later, and scheduled jobs (cron, heartbeats) keep firing without any client connected.
packages/coding-agent/src/modes/daemon/
active-session-state.ts # ActiveSessionState + DaemonSocketClient bookkeeping
command-recovery-journal.ts # Idempotency journal at the supervisor boundary
compact-session-stream.ts # Slim attach: compact assistant deltas + chunked snapshots
daemon-catalog-process.ts # Separate process that scans on-disk sessions
daemon-client-env.ts # HERDR_* client env allowlist, exec env pinning
daemon-client.ts # DaemonClient: connect, hello, request, auto-reconnect
daemon-errors.ts # Serialize/deserialize structured command errors
daemon-extension-binding.ts # Binds extensions inside the daemon context
daemon-mode.ts # AgentDaemon: socket server, sessions, command handlers
daemon-protocol.ts # Wire contract: commands, events, versions, capabilities
daemon-runtime-identity.ts # Build ID + executable paths for stale detection
daemon-session-id.ts # Display session ids and suffix matching
daemon-session-list.ts # SessionSummary, lifecycle/activity, session lists
daemon-session-summarizer.ts # Background recap/verdict generation per session
daemon-socket.ts # Socket path lease, 0600 socket, cleanup
daemon-supervisor-ownership.ts # Durable owner registry, startup fences, shutdown admission
daemon-supervisor.ts # Global supervisor: socket owner, worker lifecycle
daemon-worker-client.ts # Supervisor-side client for worker channels
daemon-worker-protocol.ts # Worker auth, subscribe, passivation, update commands
heartbeat-catalog.ts # Heartbeat listing helpers for the agents view
mutation-drain-latch.ts # Drains mutating commands during update restart
saved-session-catalog.ts # Daemon-side saved-session list/rename/delete
saved-session-info.ts # Serializes SessionInfo for the wire
snapshot-transcript-cache.ts # Chunked snapshot transcript generation
worker-recovery-journal.ts # Worker-side recovery journal
| Type | Path | One-line description |
|---|---|---|
DAEMON_PROTOCOL_VERSION (8), DAEMON_SCHEMA_REVISION (15) |
packages/coding-agent/src/modes/daemon/daemon-protocol.ts |
Wire version and schema revision; every change is classified and dual-compat tested |
DaemonCommand, DaemonOutbound, DAEMON_COMMAND_COMPATIBILITY
|
packages/coding-agent/src/modes/daemon/daemon-protocol.ts |
The command/event union and per-command min-version/schema/capability gates |
DaemonClient, DaemonHello
|
packages/coding-agent/src/modes/daemon/daemon-client.ts |
Client transport: JSONL over a socket, hello handshake, request correlation, reconnection |
AgentDaemon |
packages/coding-agent/src/modes/daemon/daemon-mode.ts |
Socket server, session map, command dispatch, event broadcast |
ActiveSessionState, DaemonSocketClient
|
packages/coding-agent/src/modes/daemon/active-session-state.ts |
Per-session runtime plus attached client set, event generation/sequence |
SessionSummary, SessionLifecycle, SessionActivity
|
packages/coding-agent/src/modes/daemon/daemon-session-list.ts |
Durable per-session view used by list, attach, and the agents view |
bindActiveSessionState |
packages/coding-agent/src/modes/daemon/daemon-extension-binding.ts |
Binds extensions to a session in daemon context; dialogs travel as extension_ui_request
|
DaemonSupervisor |
packages/coding-agent/src/modes/daemon/daemon-supervisor.ts |
Global supervisor: owns the socket, spawns/recovers per-session workers |
CommandRecoveryJournal |
packages/coding-agent/src/modes/daemon/command-recovery-journal.ts |
Durable received/result/ack records so uncertain commands are never replayed |
DaemonSessionSummarizer |
packages/coding-agent/src/modes/daemon/daemon-session-summarizer.ts |
Periodic + debounced recap/verdict generation with a cheap model |
DaemonCatalogClient |
packages/coding-agent/src/modes/daemon/daemon-catalog-process.ts |
Out-of-process saved-session scanning with progress streaming |
defaultDaemonSocketPath() (packages/coding-agent/src/modes/daemon/daemon-socket.ts) returns $TMPDIR/prime-agent-<uid>/daemon.sock (0600, dir 0700). The supervisor binds it; prepareDaemonSocketPath uses a proper-lockfile lease so concurrent launchers cannot race. When a client connects, the server immediately sends a daemon_hello frame carrying the protocol name/version, schemaId (protocol-8-schema-15-d28eaade1789), schemaRevision (15), app version, runtime identity, a per-connection clientId, and the serverCapabilities list.
Command framing is JSONL. At protocol >= 7 commands are wrapped in a DaemonCommandEnvelope (type: "command", id, protocol, clientId, nested command); events use a parallel envelope (type: "event", id, activeSessionId, sequence, cursor, emittedAt). The client advertises its own capabilities in attach (attach_snapshot, event_sequence, extension_ui, slim_attach, chunked_snapshot, client_owned_sessions). Before sending anything, DaemonClient.request checks the negotiated hello against DAEMON_COMMAND_COMPATIBILITY (min protocol, min schema revision, or a DaemonServerCapability) and throws DaemonCapabilityUnavailableError when the server cannot honor the command. Every wire change is classified backward-compatible, capability-gated, or incompatible, and both new-client/old-daemon and old-client/new-daemon combinations are covered by tests (see how-to-contribute/patterns-and-conventions.md).
Attach (case "attach" in packages/coding-agent/src/modes/daemon/daemon-mode.ts) hydrates or reuses the ActiveSessionState, adopts client env, and returns a DaemonAttachResult: a DaemonSessionSnapshot (state, messages, session tree, RLM child snapshots), lastEventSequence/lastEventCursor, and a DaemonReplayInfo computed from the client's resumeCursor. Events carry monotonic sequences plus a per-session generation; a client that reconnects with a cursor gets a complete, partial, or unavailable replay verdict. Slim clients (slim_attach) skip the legacy session_attached frame and chunked clients stream the transcript as session_snapshot_begin/chunk/end. Terminal disconnect only detaches the client: the session stays resident (or in its worker) and keeps streaming to state.clients, so work is not lost.
sequenceDiagram
participant C as DaemonClient / DaemonAgentConnection
participant D as AgentDaemon (daemon-mode)
participant S as ActiveSessionState
C->>D: connect (unix socket)
D-->>C: daemon_hello (protocol 8, schema 15, serverCapabilities, clientId)
C->>D: attach { activeSessionId, capabilities, resumeCursor, env }
D->>S: getOrHydrateBoundSessionState
D-->>C: attach response (snapshot, replay info, lastEventSequence)
D->>D: register client in state.clients
loop while streaming
S-->>C: session_event (message_update, tool events, ...)
end
Note over C,S: terminal disconnects; client socket closes
D->>D: detachClient; session keeps running
C->>D: reconnect + attach with resumeCursor
D-->>C: attach (replay: complete / partial / unavailable)
S-->>C: session_attached state/messages resume
Reconnection is handled in DaemonClient.autoReconnect (packages/coding-agent/src/modes/daemon/daemon-client.ts): on socket close the client calls recoverDaemon (from main.ts this is ensureInteractiveDaemonRunning), reconnects, re-waits for the hello, re-checks command compatibilities against the new hello, and replays in-flight command envelopes that were awaitingReconnect. DaemonAgentConnection (packages/coding-agent/src/modes/agent-connection/daemon-agent-connection.ts) layers the interactive surface on top: attach(), snapshot assembly, and the closed event.
new_session gained an optional seedMessages field, gated by protocol >= 8, schema revision >= 15, and the seed_messages server capability (NEW_SESSION_SEED_MESSAGES_COMMAND in packages/coding-agent/src/modes/daemon/daemon-protocol.ts). DaemonAgentConnection.newSession checks assertSeedMessagesSupported before sending; the daemon appends the seed messages to the fresh SessionManager through a setup hook before the swap resolves (case "new_session" in daemon-mode.ts, via seedMessageToSessionMessage). Older daemons ignore the unknown field, so clients never send it unless the negotiated hello meets the floor.
AgentCronScheduler (from packages/coding-agent/src/core/cron-jobs.ts) runs scheduled prompts and heartbeats in the daemon; runCronJob wakes a session (even an idle, passivated one) and delivers a prompt with a followUpQueueKey such as heartbeat:<jobId>. Heartbeat changes broadcast a global heartbeats_changed event. DaemonSessionSummarizer (daemon-session-summarizer.ts) sweeps every 25 seconds and debounces turn-end activity, generating <recap>/<status> verdicts with a cheap prime-inference model (qwen/qwen3-30b-a3b-instruct-2507); only settled idle verdicts are persisted.
The supervisor (daemon-supervisor.ts) is the process that holds the socket, validates its durable owner record (daemon-supervisor-ownership.ts, keyed by socket and descriptor dir), and spawns one worker per root session. Each worker runs AgentDaemon in worker mode: it authenticates with worker_auth plus a supervisor generation claim, speaks a private-framed transport, and answers the same public DaemonCommands while the supervisor fronts the socket. Mutating commands are journaled in CommandRecoveryJournal before dispatch so a crash mid-command is answered as uncertain rather than replayed. The supervisor also handles idle-worker eviction, worker passivation (child passivation caps), update restart (drain, fence, publish via prepare_update_restart), and re-spawns workers whose descriptors say they died.
The worker transport helpers live in packages/coding-agent/src/modes/session-worker/private-framing.ts and packages/coding-agent/src/modes/daemon/daemon-worker-protocol.ts. DaemonWorkerDescriptor records the worker pid, socket path, recovery journal, root session, and lifecycle (starting/ready/recovering/failed); supervisors write these under ~/.prime/agent/daemon-workers/. daemon-worker-client.ts is the supervisor-side client, and worker-recovery-journal.ts persists worker-side recovery intent. Client-owned sessions (client_owned_sessions capability) get their own worker owned by the creating client, promotable to resident.
-
prime-agent statusrunsdiscoverDaemons+runPsinpackages/coding-agent/src/cli/daemon-ps.ts: it finds every daemon on the machine (parsingss -lxpon Linux orlsofon macOS, plus a sweep of the default socket dir), probes each with hello +list, and reports status (current/stale/unreachable/orphan-file). -
prime-agent doctor [--fix]runs the same discovery; with--fixit runsrunReap, which only touches clearly safe targets (orphan socket files, reachable idle daemons on non-default sockets) and re-probes before killing. -
prime-agent shutdown [--force]runsrunShutdownAll: without--forceit requires a TTY confirmation (daemon-stop-confirm.ts), takes a shutdown admission lease (acquireDaemonShutdownAdmission), gracefully shuts down each daemon, force-kills unreachable workers when asked, and removes stale sockets. -
prime-agent updatedrains mutations through the coordinator inpackages/coding-agent/src/cli/daemon-update-restart.ts, restarts the daemon, and restores sessions from the update-restart manifest.
- Clients reach the daemon exclusively through
DaemonClient(daemon-client.ts) andDaemonAgentConnection(packages/coding-agent/src/modes/agent-connection/daemon-agent-connection.ts); the interactive mode, print/json/rpc/acp modes, and the agents view all go through this seam. -
main.tsbootstraps the daemon viaensureInteractiveDaemonRunningandmaybeStartDaemonEarlyinpackages/coding-agent/src/cli/daemon-launch.ts;DaemonAgentConnectiongetsrecoverDaemonso reconnects re-spawn a missing daemon. - The agents view (
packages/coding-agent/src/modes/agents-view/) consumesSessionSummary, heartbeats, and saved-session catalogs over the same protocol. - Session state is preserved because the daemon never disposes the runtime on detach; JSONL transcripts under
~/.prime/agent/sessions/back--resumeand the saved-session catalog.
- Add a daemon command: extend the
DaemonCommandunion andDAEMON_COMMAND_COMPATIBILITYinpackages/coding-agent/src/modes/daemon/daemon-protocol.ts, handle it inAgentDaemon.handleCommandindaemon-mode.ts, and add both dual-compat tests plus a capability entry if it is optional. - Change session lifecycle or residency:
active-session-state.ts,daemon-session-list.ts, and the passivation/eviction logic indaemon-supervisor.ts. - Change the attach payload:
DaemonAttachResult, slim/chunked snapshot handling indaemon-mode.tsandsnapshot-transcript-cache.ts, andcompact-session-stream.tsfor the assistant delta compaction. - Bump the wire version:
DAEMON_PROTOCOL_VERSION/DAEMON_SCHEMA_REVISION/DAEMON_SCHEMA_IDindaemon-protocol.ts, then follow the compatibility maps and tests per the policy inhow-to-contribute/patterns-and-conventions.md.
| File | Why it matters |
|---|---|
packages/coding-agent/src/modes/daemon/daemon-protocol.ts |
The entire wire contract, versions, and compatibility maps |
packages/coding-agent/src/modes/daemon/daemon-mode.ts |
The AgentDaemon socket server and command handlers |
packages/coding-agent/src/modes/daemon/daemon-client.ts |
Client transport, hello, capability checks, reconnect |
packages/coding-agent/src/modes/daemon/daemon-supervisor.ts |
Supervisor: socket ownership, worker lifecycle, eviction, update restart |
packages/coding-agent/src/modes/daemon/daemon-supervisor-ownership.ts |
Durable owner registry, startup fences, shutdown admission |
packages/coding-agent/src/modes/daemon/daemon-session-list.ts |
Session summaries consumed by list/attach/agents view |
packages/coding-agent/src/modes/daemon/daemon-extension-binding.ts |
How extensions run in a headless daemon context |
packages/coding-agent/src/modes/daemon/command-recovery-journal.ts |
Command idempotency and crash semantics |
packages/coding-agent/src/modes/agent-connection/daemon-agent-connection.ts |
The client-side AgentConnection adapter over the daemon |
-
CLI : the commands that manage the daemon (
status,doctor,shutdown,attach) - Package overview : SDK surface and package layout
- Session runtime : what runs inside the daemon
- RLM runtime : recursive subagents the daemon hosts
- Architecture : the connection seam and client surfaces
- Daemon protocol : wire protocol reference
- Patterns and conventions : daemon protocol change policy