The daemon-mode file packages/coding-agent/src/modes/daemon/daemon-mode.ts owns live AgentSessionRuntime instances and exposes a JSONL protocol over a local socket, allowing clients to attach and detach from sessions without disposing the underlying agent loop.[1] daemon-mode.ts re-exports DaemonCommand, DaemonOutbound, and DaemonResponse from ./daemon-protocol.js; SessionActivity, SessionLifecycle, and SessionSummary from ./daemon-session-list.js; and defaultDaemonSocketPath from ./daemon-socket.js — protocol and wire types are covered in detail on Daemon protocol.[1] The agents-view and session-list do not surface workers in the stopping state; the daemon protocol reports worker lifecycle transitions accurately. The daemon root depth counter is reset on each new daemon context initialization, preventing depth-tracking drift across restarts. Without socket-path normalisation, symlinks or trailing slashes could produce different identity strings for the same physical socket, causing daemon-supervisor.ts to spawn a duplicate daemon or fail to claim ownership of an existing one. A centralized agent-status classifier in agents-view-state.ts, agent-roster.ts, and daemon-session-list.ts consolidates worker status derivation into one authoritative path, ensuring the TUI's subagent summary and daemon session list report consistent status values for the same worker. Extensions to daemon modes or new agent states must update the shared agent-status classifier in agents-view-state.ts, agent-roster.ts, and daemon-session-list.ts rather than patching individual UI components, to maintain consistency across all status-reporting surfaces. packages/coding-agent/src/core/session-lease.ts is the canonical location where daemon process identity is computed; the derivation must be timezone-independent and invariant to locale settings, ensuring consistent lease keys across system clock offset changes. test/suite/regressions/879-timezone-stable-process-identity.test.ts validates that the daemon process identity computed in session-lease.ts remains stable across timezone changes. Heartbeat-only sessions are classified as normal sessions in idle state rather than a distinct residency class, ensuring consistent display in agents-view and aligned eviction scheduling with other idle sessions. session-action-store.ts records a durable wake token for idle heartbeat sessions, enabling incoming tasks to restart them without a cold spawn. daemon-session-summarizer.ts re-publishes a roster-row update when the temporal currency of the idle verdict changes — even if other summary fields are structurally identical — ensuring agents-view subscribers display current idle/busy status rather than stale verdicts. The publication predicate for roster-row currency is defined there.
The DaemonModeOptions interface accepts an optional socketPath, a required defaultSessionConfig of type AgentSessionRuntimeConfig, a required createRuntime factory of type CreateAgentSessionRuntimeFactory, and an optional worker block containing a required authenticationToken and an optional restoreActiveSessionId.[1]
The complete set of daemon commands recognized by daemon-mode.ts spans session lifecycle (create, attach, detach, kill, rename, new_session, switch_session, fork), prompting (prompt, prompt_and_wait, steer, follow_up), agent messages (send_message, agent_messages_status, agent_messages_pause, agent_messages_resume, agent_messages_clear), cron and heartbeats (cron_list, cron_add, cron_cancel, heartbeat_get, heartbeat_set, heartbeat_update, heartbeats_list, heartbeat_manage), and update/restart operations (prepare_update_restart, retry_worker, restart, shutdown).[1] daemon-mode.ts tracks delivery state for remote agent messages and skips re-sending already-delivered messages during retry attempts, preventing duplicate message processing in long-running or multi-agent daemon sessions. In daemon-mode.ts, worker-mode sessions accept session renames that have been explicitly approved by the supervisor; unapproved rename requests are still rejected. daemon-supervisor.ts enforces session ownership validation when a new open request arrives while a previous one is in flight, preventing a different session from attaching to an in-flight result. test/daemon-supervisor-lazy-subagents.test.ts covers the race scenario where concurrent open requests could attach to shared in-flight results.
Three compile-time constants govern snapshot and update timing: WORKER_SNAPSHOT_TERMINAL_DRAIN_TIMEOUT_MS is 1,000 ms, UPDATE_RESTART_PREPARE_TIMEOUT_MS is 90,000 ms, and MAX_SESSION_SNAPSHOT_STABILIZATION_RETRIES is 3.[1] When a worker stop times out, daemon-supervisor.ts finalizes the registration instead of leaving it stranded, preventing agent-count drift and stop-command hangs. daemon-supervisor.ts retains the root process's kill-cleanup handler across all lifecycle transitions, preventing child processes from being orphaned during supervisor teardown. daemon-supervisor.ts filters out workers in terminal failure states when constructing the heartbeat catalog sent to the orchestrator, ensuring the catalog reflects only alive or pending workers. Failed workers are reported through a separate failure-reporting path, not the heartbeat catalog. The daemon shutdown path in daemon-mode.ts and active-session-state.ts explicitly awaits completion of any bash command still executing at close time, preventing close signals from abandoning running subprocesses and leaving output undelivered to the client. Any new teardown hooks must be inserted after this bash-drain await to avoid truncating in-flight bash output. When worker spawn fails in daemon-supervisor.ts (e.g., EMFILE: too many open files), the underlying OS error propagates through daemon-errors.ts and main.ts to the caller instead of an opaque fallback, enabling root-cause diagnosis. daemon-supervisor-monitor.test.ts and daemon-errors.test.ts validate this propagation path.
The RECOVERY_CHECKPOINT_EVENTS set defines the lifecycle moments at which the daemon records a recovery checkpoint: agent_start, agent_end, turn_start, turn_end, message_start, message_end, tool_execution_start, tool_execution_end, compaction_start, compaction_end, and auto_retry_start.[1] daemon-supervisor.ts detects and cleans up stale worker registrations on session resume, preventing ghost entries from blocking new workers. daemon-supervisor-ownership.ts stores supervisor ownership records in a persistent location rather than $TMPDIR, preventing loss of worker-ownership state across OS purges and reboots. daemon-supervisor.ts blocks session-reuse requests until a worker's restart sequence completes, preventing attachment to incompletely-recovered workers and ensuring reliable session state. Tests in test/daemon-supervisor-monitor.test.ts and test/daemon-supervisor-lazy-subagents.test.ts assert this ordering guarantee.
When an update-restart interrupts a session, daemon-mode.ts injects the fixed UPDATE_RESTART_MARKER message into the transcript: "<prime_agent_update_interrupted>\nPrime Agent was updated and intentionally interrupted this session. Continue from the saved transcript and restored tool/kernel state. Any running model, tool, bash, or child-agent work may have been partially completed.\n</prime_agent_update_interrupted>".[1]
Sources