How Prime Agent's Daemon Supervisor Architecture Manages Worker Processes

Prime Agent's daemon supervisor acts as a central controller that owns a Unix-domain socket, persists worker state to JSON descriptors, routes client commands to session workers via JSON-L RPC, and manages lifecycle events including idle eviction and zero-downtime updates.

The PrimeIntellect-ai/prime-agent repository implements a resilient multi-process architecture where a daemon supervisor orchestrates background session workers. This daemon supervisor architecture ensures fault tolerance across process crashes, supports seamless updates without losing session state, and efficiently manages system resources through intelligent worker lifecycle controls.

Core Components of the Daemon Supervisor Architecture

Socket Ownership and Singleton Enforcement

The supervisor claims exclusive control of the communication socket through a leasing mechanism. During initialization in DaemonSupervisor.start(), the system invokes acquireDaemonSocketPathLease() and acquireDaemonSupervisorOwnership() to write a lease file recording the supervisor's PID【/packages/coding-agent/src/modes/daemon/daemon-supervisor.ts#L42-L47】. This prevents multiple supervisor instances from interfering with each other, establishing a singleton pattern for the daemon process.

Worker Descriptor Persistence

Worker processes are stateful entities tracked via JSON descriptors stored in <agentDir>/daemon-workers/<hash>/. Each descriptor contains the worker's PID, private socket path, authentication token, and lifecycle state. When the supervisor starts, loadWorkerDescriptors() reads these files to build an in-memory workers map【/packages/coding-agent/src/modes/daemon/daemon-supervisor.ts#L115-L130】.

Command Routing and RPC Mediation

Clients connect to the supervisor's Unix-domain (or Windows named-pipe) socket and communicate via JSON-L lines. The handleConnection() method registers a JSON-L reader, while handleLine() parses incoming commands against the DAEMON_COMMAND_TYPES whitelist【/packages/coding-agent/src/modes/daemon/daemon-supervisor.ts#L104-L117】. For validated commands, forwardToWorker() transmits the request to the appropriate worker through a DaemonWorkerClient and returns the response to the client【/packages/coding-agent/src/modes/daemon/daemon-supervisor.ts#L260-L280】.

Worker Process Lifecycle Management

Spawning and Recovery

The runDaemonSupervisorMode() function creates the supervisor instance and initiates the recovery flow, while adoptOrRecoverWorker() either launches new workers or reconnects to existing ones via their private sockets【/packages/coding-agent/src/modes/daemon/daemon-supervisor.ts#L72-L78】. This allows the system to survive supervisor restarts without losing active sessions.

Prompt Admission Handling

Commands such as prompt and prompt_and_wait require a two-phase admission process. The parseCommandAndRegisterPromptAdmission() method creates a SupervisorPromptAdmission object, registering it in the promptAdmissions map before the worker assumes ownership of the session【/packages/coding-agent/src/modes/daemon/daemon-supervisor.ts#L181-L210】. This ensures orderly handoff between client requests and worker session execution.

Idle Eviction Strategy

To conserve resources, the supervisor implements a configurable idle eviction mechanism. The scheduleIdleEvictionSweep() method initiates a periodic check, while runIdleEvictionSweep() iterates through workers, refreshing activity summaries and identifying candidates for eviction based on lastActivityAt timestamps【/packages/coding-agent/src/modes/daemon/daemon-supervisor.ts#L88-L118】【/packages/coding-agent/src/modes/daemon/daemon-supervisor.ts#L145-L162】. Workers exceeding the configured idle timeout are passively shut down after requesting they passivate idle children.

Update-Restart Coordination

During updates, the supervisor executes a drain → fence → prepared sequence to achieve zero-downtime restarts. The updateRestartPhase field and mutationDrain object coordinate this process, using await this.mutationDrain.waitForDrain() to pause new mutations, gracefully stop workers, and hand off the socket to a fresh supervisor instance【/packages/coding-agent/src/modes/daemon/daemon-supervisor.ts#L90-L102】【/packages/coding-agent/src/modes/daemon/daemon-supervisor.ts#L270-L285】. This architecture supports command-recovery journaling to guarantee at-least-once delivery of mutating commands across restarts.

Signal Handling and Cleanup

Robust shutdown behavior is implemented through registerSignalHandlers(), installed during start() to catch SIGINT and SIGTERM events【/packages/coding-agent/src/modes/daemon/daemon-supervisor.ts#L71-L84】. The cleanupSupervisorResources() method releases the socket lease, terminates worker processes, and deletes descriptor files to ensure clean state removal.

Implementation Examples

Below are practical code patterns for interacting with the daemon supervisor architecture.

Starting the Daemon Supervisor

// packages/coding-agent/src/modes/daemon/daemon-mode.ts
import { runDaemonSupervisorMode } from "./daemon-supervisor.js";

export async function startDaemonSupervisor(socketPath?: string) {
  // Build a default session config – normally read from the agent's settings
  const defaultConfig = { agentDir: "/var/lib/prime-agent" };
  await runDaemonSupervisorMode({ socketPath, defaultSessionConfig: defaultConfig });
}

Source: runDaemonSupervisorMode() instantiates DaemonSupervisor and invokes start()【/packages/coding-agent/src/modes/daemon/daemon-supervisor.ts#L72-L78】.

Worker Recovery Loop

// Inside DaemonSupervisor.start()
const workersToAdopt = [...this.workers.values()];
await Promise.all(workersToAdopt.map(w => this.adoptOrRecoverWorker(w)));

Source: The adoption loop processes existing worker descriptors during startup【/packages/coding-agent/src/modes/daemon/daemon-supervisor.ts#L86-L98】.

Forwarding Commands to Workers

// From DaemonSupervisor.handleCommand()
const response = await this.forwardToWorker(worker, {
  type: "get_messages",
  activeSessionId: "session-123",
});

Source: forwardToWorker() handles JSON-L request transmission via DaemonWorkerClient【/packages/coding-agent/src/modes/daemon/daemon-supervisor.ts#L490-L505】.

Idle Eviction Implementation

private async runIdleEvictionSweep(now = Date.now()) {
  // Refresh summaries for all workers
  await Promise.all([...this.workers.values()].map(w => this.refreshWorkerSummaries(w)));

  // Pick candidates based on canEvictWorker()
  const candidates = [...this.workers.values()].filter(w =>
    canEvictWorker(this.workerEvictionSnapshot(w), this.settingsManager.getIdleEvictionMinutes(), now)
  );

  // Stop each idle worker
  await Promise.all(candidates.map(w => this.stopWorker(w, true)));
}

Source: Full sweep logic in runIdleEvictionSweep()【/packages/coding-agent/src/modes/daemon/daemon-supervisor.ts#L88-L118】.

Worker-Side Client Connection

// packages/coding-agent/src/modes/daemon/daemon-worker-client.ts
const client = new DaemonWorkerClient("/var/run/prime-agent/daemon.sock");
await client.connect();
await client.authenticateWorker(token, { workerId, socketPath, authenticationToken });
const hello = await client.waitForHello(); // receives daemon_hello
const resp = await client.request({ type: "get_state", activeSessionId: "sess-42" });

Source: DaemonWorkerClient methods for worker authentication and communication【/packages/coding-agent/src/modes/daemon/daemon-worker-client.ts#L45-L88】.

Summary

  • Singleton Enforcement: The supervisor uses acquireDaemonSupervisorOwnership() and PID-based lease files to ensure only one instance controls the socket at a time.
  • Stateful Recovery: Worker processes persist their state to JSON descriptors, enabling the supervisor to recover or adopt workers after crashes or restarts via adoptOrRecoverWorker().
  • RPC Mediation: Client commands are validated against DAEMON_COMMAND_TYPES, then forwarded to workers through DaemonWorkerClient using JSON-L framing.
  • Resource Optimization: Configurable idle eviction via runIdleEvictionSweep() automatically terminates inactive workers based on lastActivityAt timestamps.
  • Zero-Downtime Updates: The mutationDrain and updateRestartPhase mechanism supports graceful handoffs during updates without losing session state or in-flight commands.

Frequently Asked Questions

How does the daemon supervisor prevent multiple instances from running?

The supervisor calls acquireDaemonSocketPathLease() and acquireDaemonSupervisorOwnership() during startup in DaemonSupervisor.start(). These methods write a lease file containing the supervisor's PID to the agent directory. If another instance attempts to start, it detects the existing lease and aborts, ensuring singleton enforcement across the system.

What happens to worker processes when the supervisor restarts?

When restarting, the supervisor executes loadWorkerDescriptors() to read persisted JSON state from <agentDir>/daemon-workers/<hash>/. It then calls adoptOrRecoverWorker() for each descriptor to either reconnect to existing workers via their private sockets or cleanly replace them. This recovery mechanism preserves session state and active admissions across supervisor restarts.

How are client commands routed to the correct worker?

Clients send JSON-L commands to the supervisor's Unix socket. The supervisor's handleConnection() and handleLine() methods parse these requests, validate them against the DAEMON_COMMAND_TYPES whitelist, and determine the target worker based on the session ID. The forwardToWorker() method then relays the command via a DaemonWorkerClient instance and returns the worker's response to the client.

How does idle eviction manage system resources?

The supervisor schedules periodic sweeps via scheduleIdleEvictionSweep(), which executes runIdleEvictionSweep() to check worker activity. Workers exceeding the configured idle timeout (determined by canEvictWorker() checking lastActivityAt) are passively shut down after requesting they passivate idle children. This prevents resource exhaustion while maintaining hot workers for active sessions.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →