Skip to content

Per-User Supervisor Process for Background Agent Sessions

A per-user daemon hosts background agent sessions as detached children, reconnects via an on-disk roster, evicts idle ones, and restarts onto updated binaries in place.

A per-user supervisor owns the lifecycle of background agent sessions: it spawns each session as its own process, survives terminal close, restarts onto an updated binary, stops idle non-pinned sessions to reclaim resources, and itself exits when no sessions remain. The pattern decouples three lifecycles the naive "terminal owns the agent" model conflates — the agent loop, the user's terminal, and the binary version.

What the Supervisor Owns

Claude Code's reference implementation states the role exactly: "Background sessions are hosted by a per-user supervisor process, separate from your terminal and from agent view. The supervisor starts automatically the first time you background a session or open agent view, and you don't manage it directly" (Claude Code: agent view). Five responsibilities sit with the supervisor and nowhere else:

  • Detached child sessions. "Each background session is its own Claude Code process, managed by the supervisor rather than tied to your terminal" (agent view docs). Closing the terminal does not signal the session.
  • Roster-backed reconnect. Running sessions are listed in ~/.claude/daemon/roster.json — "List of running background sessions, used to reconnect after a restart" (agent view docs). The supervisor can crash or be replaced; the roster lets it re-attach rather than treat children as orphans.
  • Idle eviction with pinning exemption. "Once a session finishes and sits unattached for about an hour, the supervisor stops its process to free resources. A session you have pinned with Ctrl+T is exempt and keeps its process running while idle" (agent view docs). The user expresses "keep this hot" via pinning — no per-session lifetime API.
  • Memory-pressure tier ladder. "When the host runs low on memory, the supervisor stops idle non-pinned sessions first and stops idle pinned ones only if that freed nothing" (agent view docs).
  • Binary-on-disk watch as rolling-upgrade trigger. "The supervisor watches the installed Claude Code binary on disk and restarts into the new version after the regular auto-updater replaces it. This is a local file watch, not a network check. Background sessions are detached processes, so they keep running through the restart and the new supervisor reconnects to them" (agent view docs).

When every session finishes and no terminal is attached, the supervisor itself exits and starts again on next demand — daemon-managed, but not always-on.

State Lives Where the Supervisor Can Find It

Three on-disk locations carry the supervisor's authoritative state, independent of process memory (Claude Code: Where state is stored):

Path Purpose
~/.claude/daemon.log Supervisor log
~/.claude/daemon/roster.json Running-sessions list for reconnect
~/.claude/jobs/<id>/state.json Per-session state

A session's short ID is its directory name under ~/.claude/jobs/. claude daemon status reports reachability, PID, version, socket directory, and live session count.

Why It Works

The pattern works because it decouples three lifecycles with structurally different churn rates — the agent loop (long-lived, expensive to restart with hot context), the terminal (short-lived, dies on shell exit), and the binary version (changes whenever the auto-updater runs). Glued together in a single process, closing the terminal kills the loop and an auto-update mid-task crashes it. The supervisor is the indirection that lets each evolve independently: the loop lives in a detached child, the terminal becomes a UI attached at will, and binary updates become in-place restart with roster-driven reconnect.

This is session, harness, and sandbox separation projected onto local desktop tooling. Anthropic's case for splitting along layers whose churn rates differ by orders of magnitude argues that "a monolithic agent process couples model inference, session state, and execution environment. When any one churns — models shift, execution targets multiply, or a crash forces recovery — the others pay the cost" (Anthropic: managed agents).

The shape is not Claude-specific. The cocoindex semantic-code tool independently moved "from a monolithic per-session process to a persistent daemon with a thin CLI" with "auto-start on first use via Unix socket probe, version handshake on every connection for transparent upgrades" (cocoindex: Building an Invisible Daemon). An open Codex request proposes the same shape — codex run --bg, codex sessions list/attach/logs — explicitly rejecting tmux/screen as adding "complexity and reduces portability" and nohup as offering "limited functionality without lifecycle management" (openai/codex#3968).

When This Backfires

The pattern is per-user desktop scoping. Several conditions invert the value:

  • Single-session, single-machine usage. One agent in one terminal pays the daemon's roster, socket, and log complexity for zero parallelism benefit. The pattern earns its complexity at two-plus concurrent sessions.
  • Multi-user shared host. A per-user supervisor scales by user count, not session count. Shared dev boxes give you N supervisors competing for the same binary watch and memory budget. The architecture is deliberately not multi-tenant — see Codex's open multi-user gap: "to serve N users, you need N processes" (openai/codex#14916).
  • Restricted environments where daemons cannot persist. Sandboxed CI runners and ephemeral containers where the supervisor cannot outlive the shell make roster and idle-eviction guarantees meaningless. The pattern presupposes the process survives the dispatcher's exit.
  • Long-lived elevated permissions. A supervisor holding bypassPermissions across reattach has a wider blast radius than a per-invocation foreground session, because it outlives user attention.
  • Power-cycle vs sleep. "Shutting down or restarting your machine stops running background sessions, so they show as failed when you next open agent view" (agent view docs). Sleep is recovered; shutdown is not.

Why Not Just tmux or systemd --user

The steelman is "wrap the agent CLI in tmux or systemd --user and reuse decades of supervision tooling." Both ship pieces the supervisor reimplements — detach/reattach, restart policies, log capture, resource limits — but not the combination:

  • Binary-on-disk watch with in-place restart and session reconnect. systemd --user can restart on file change via systemd.path units, but the agent-process and session-reconnect protocol are still your code.
  • Idle eviction with pinning exemption. Two-tier ladder driven by one keystroke. tmux has no concept of session importance; systemd --user units are uniformly long-lived per unit.
  • No-install assumption. systemd --user instances "will be killed as soon as the last session for the user is closed" by default and need loginctl enable-linger plus admin setup (Arch Wiki: systemd/User). A self-reviving per-user supervisor with a roster file resurrects on next CLI invocation with zero admin setup — required for a tool installed into ~/.local/bin/.

The Beyond process supervisors essay makes the strongest opposite case: general-purpose supervisors often subsume responsibilities that belong elsewhere. A domain-specific supervisor for one process class avoids that trap by keeping its responsibilities narrow.

Example

The shell-facing surface, taken from Claude Code's documented commands (agent view: Manage sessions from the shell):

# Spawn a detached background session — supervisor starts if not running
claude --bg "investigate the flaky SettingsChangeDetector test"
# → backgrounded · 7c5dcf5d · investigate-flaky-test

claude daemon status        # reachability, PID, version, socket dir, session count
claude attach 7c5dcf5d      # reattach to a detached session
claude respawn --all        # roll every running session onto an updated binary
claude stop 7c5dcf5d        # supervisor exits when none remain

The supervisor is invisible until inspected. The user-facing surface is the agent view TUI and per-session commands; the daemon's existence is an implementation detail of "this session survives my shell exit and the next auto-update."

Key Takeaways

  • A per-user supervisor decouples three lifecycles — agent loop, user terminal, binary version — that the naive "terminal owns the agent" model conflates.
  • Detached child processes plus an on-disk roster (~/.claude/daemon/roster.json) let the supervisor crash, restart, or be replaced without orphaning sessions.
  • Idle eviction with pinning exemption is the resource-management lever; the user expresses "keep this hot" with one keystroke instead of a per-session lifetime API.
  • Binary-on-disk watch is the rolling-upgrade lever: in-place restart through detached child sessions, no manual respawn step.
  • The pattern is per-user desktop scoping — multi-user shared hosts, backend services, and ephemeral CI runners need different architecture.
  • tmux and systemd --user ship pieces of supervision but not the combination that makes background agent sessions feel ambient.
Feedback