Chapter 21: A Background Agent Fleet — Detaching from the Terminal and Daemon Supervision
The earlier chapters gave you autonomy along two axes. Chapter 17's
/loopis the time axis: the same session comes back to do another round after a while. Chapter 19's Dynamic Workflows is the space axis: one script fans out dozens to hundreds of subagents right now. But both still live inside a session bound to a terminal — close the terminal and they're gone. This chapter is about how Claude Code cuts that last cord: how a session detaches from the terminal and keeps running once the terminal is closed, and how, when you have several such background sessions at once, they get looked after as a fleet. What triggers it is one command,/bg; what manages it is one table,claude agents.The evidence for this chapter splits two ways, and very unevenly. The in-session substrate for detached sessions — the background-task UI,
run_in_backgroundinsideAgentTool— can be read line by line in the leaked snapshot (labeled v2.1.88) source. But the daemon supervisor that actually treats multiple background processes as a fleet never leaked with the snapshot: theregrep -rlF 'tengu_bg_'hits zero files, and it survives only as strings in the 2.1.202 binary. So this chapter keeps making one thing explicit: which parts are mechanisms readable in source, which are a feature map assembled from nearly eighty event names in the binary strings, which are logic inferred from those names, and which are the local process model — a blind spot that simply doesn't show through.
21.1 The third thing beyond the two axes: detaching from the terminal
Start by picking those two axes back up. /loop extends a session in time and a Workflow splits it in space, but both rest on one premise neither breaks: the session and the terminal are on the same cord. Your shell is alive, so the session is alive; the shell exits, the process group gets a signal, and the session goes with it. For a long task meant to run tens of minutes to hours — the kind whose launcher should long since have moved on to something else — that cord is in the way: you can't keep a terminal open forever just to keep it running.
The background fleet exists to solve exactly this, and the first move it introduces is "detach." /bg lifts the current session off the terminal and drops it into the background to persist; your terminal is freed at once for something else, while that session keeps running elsewhere. The line in the strings that states this most counterintuitively is "Sessions keep running if you close the terminal." — the session is no longer an appendage of the terminal; close the terminal and it lives on. This is orthogonal to the earlier two axes: /loop and Workflow are about "how work is arranged inside one session," while detaching is about "how the session itself survives your absence." Stack all three and you get the full meaning of "unattended" — you don't have to watch it, and you don't have to stay connected to it.
Seen from another angle, what this chapter describes is really the substrate under the earlier ones. /loop's round-after-round continuation needs something keeping it from dropping; the batch of subagents a Workflow fans out, and the parallel members of a multi-agent team, all likewise rest on a layer beneath where "processes don't just vanish." The earlier chapters each covered their own orchestration — when to dispatch, how many, how to collect — but none turned that layer over to look at it closely. This chapter is about it: how a detached session, with you absent and the terminal closed, still has something watching whether it lives or dies. Subagents belong to the same background-capability landscape as detached sessions; it's just that whether they are supervised by this /bg daemon is a boundary the evidence can't bridge (see 21.6) — so the chapter reads less like a new feature and more like turning over the ground the earlier chapters have been standing on without looking at closely.
21.2 /bg: lifting the session off the terminal
/bg's one-line self-introduction says everything it needs to. Verbatim in the strings: /bg detaches this session to run in the background, and then claude agents gathers every backgrounded session into one table, each row carrying a status color — a glance tells you which one is waiting on you; space to fire off a reply, enter to take it over. The companion onboarding hint is even blunter: /bg this session, then run claude agents in a new terminal. One move pushes the session to the background, the other fishes it back up to look at from a different terminal.
Once detached, the session doesn't go mute. It's still running and may still need you — say, to ask a question only you can rule on — you just aren't in front of it right now. So detaching isn't "fire and forget," it's "fire and switch to watching asynchronously": the session keeps advancing, flashes a status in that table when it needs you, and you go back when you're free. This also explains why the whole thing threads into the phone-remote-control note in this box's CLAUDE.md — a local /bg to detach, attach back from your phone, the process kept alive by the background the whole time; the same session passes among local, phone, and persistent, all on the strength of "the session isn't bound to a terminal."
21.3 claude agents: one table for all background sessions
Detaching solves "how one session stays alive"; claude agents solves "how to watch several at once." In the CLI it's a proper Commander subcommand, its description reading verbatim Manage background agents. It carries a small set of actions: claude agents lists every background session, claude attach \{id\} opens one in the current terminal to take over, claude logs \{id\} pages through its recent output. Together the three are a plain fleet panel — the list for the whole picture, attach for a single one, logs for reviewing the record after the fact.
The key to that table is the status color. It compresses "which session needs your involvement" into a single column you can scan at a glance — you don't have to attach into each one to look; a glance tells you who to deal with first. As for which color maps to which state, the strings say only that the status color is for spotting which ones need you; they don't pin down a color-to-state mapping, and neither will this text. There's also a rule buried here that bites: if a session is already running as a background agent, you can't just --resume it elsewhere. The strings put it plainly — it's a background agent now, open claude agents to attach to it, or stop it there first to resume here. The same session isn't allowed to be taken over in two places at once; attach and resume are mutually exclusive entrances. The constraint itself reveals that a background session isn't a piece of state you can copy freely, but a live process with a single owner that has to be taken over exclusively.
21.4 Below the surface: a process-fleet supervisor running locally
Everything up to here is still the layer the user can see. What actually holds up "close the terminal and it doesn't drop, crash and you can still come back" is a supervisor assembled from the nearly eighty event names prefixed tengu_bg_ in the binary strings. Say it up front: this section has the most lopsided evidence in the chapter. The event names themselves are read verbatim from the 2.1.202 strings, solid; but under what conditions each event fires, and how they chain into a state machine, has not one line of matching source in the snapshot (grep -rlF 'tengu_bg_' hits zero files) — it can only be inferred from the names plus the surrounding minified code. So the description below of "what it's doing" has hard names and inferred logic, and I write to that measure.
Cluster these events by name and five or six subsystems surface, each minding its own patch, together looking like a small-scale process supervisor. At the top is a single entry point bg_agent_action and a bg_dispatch family — responsible for dispatching a task, first running bg_classify to categorize it, and along the way it can be rescued (_dispatch_rescued), dropped for being too old (_stale_drop), escalated all the way to SIGKILL (_sigkill_escalate), or routed down a degraded path under memory pressure (_low_mem/_fallback). Below that is the daemon's life and death: _daemon_spawn_failed, _daemon_install, restarting a wedged one (_daemon_zombie_restart) without mistakenly killing a false zombie (_zombie_false_positive), asking you first on a cold start (_daemon_cold_start_ask), with a dedicated path each for Windows (_daemon_wmi_fallback) and macOS (_daemon_macos_aqua_wrap).
Further down it gets interesting. There's a spare family — _spare_spawn, _spare_claim, _spare_enable, _prewarm_per_sweep — which, going by the names, looks like it maintains a pool of pre-warmed spare workers: rather than spinning up a process only when a task arrives, it keeps a few warm ahead of time, and an arriving task claims one directly, saving the few seconds of cold start. The rise and fall of real workers is another family: _worker_spawn/_exit/_vanished/_stalled — born, exited cleanly, vanished for no clear reason, wedged and not moving, each with its own event. The most supervisor-like is the respawn / adopt / orphan group: a crashed worker respawns automatically (_respawn), guarding against duplicates before it does (_respawn_unconfirmed_bail); an ownerless process gets adopted (_adopt), respawning if its token was lost in the process (_adopt_token_lost_respawn); an unclaimed orphan gets reaped (_orphan_reap), while the fleet's whole roster — called just that — has orphans registered back in (_roster_orphan_adopted) and can also fail to parse (_roster_parse_failed). The attach/detach family minds the moment you take over — the first frame (_first_frame), respawn-on-stall (_stall_respawn), a kick (_attach_kick); and at the very bottom is a transport layer minding pipe-level mishaps like a PTY it can't get, auth that doesn't match, a protocol mismatch, a truncated bridge.
Walk one worker through the whole thing and you feel the density of this machinery. It's born (_worker_spawn) and entered into that roster; running along, if it disappears for no clear reason (_vanished) or wedges (_stalled), then — going by the names _respawn and _respawn_unconfirmed_bail — the supervisor doesn't pretend not to notice but judges whether to respawn it, and before respawning confirms it's really dead and not a network blip, so the same job doesn't get two copies. When the machine is short on memory it's a different set of moves: it retires workers that hold memory and aren't so critical (_retire_pinned_low_mem, _low_mem_mb), making room for the critical ones. If the supervisor has just taken over and finds a pile of ownerless processes still running (say the previous daemon didn't exit cleanly), it adopts these orphans back (_adopt, _orphan_reap, _roster_orphan_adopted) rather than letting them turn into zombies no one minds. Chain these and you have what a supervisor should be: keep the books, check for life, patch a crash, retire under pressure, take in the ownerless.
Put these patches together and the conclusion is clear: what's behind /bg isn't "spin up a background process," which would be light. It's a whole process-fleet supervisor with health checks, crash respawn, orphan adoption, memory-pressure retirement, and a pre-warmed spare pool — roughly a subset of systemd or pm2 in scale — except the whole machine runs inside your local client, so that the detached sessions have something watching whether they live or die while you're not looking. This supervisor is the chapter's hardest bone and also the part that can only speak through strings: the names are read verbatim from the binary, but what the state machine they chain into actually looks like is inferred by me from the names and surrounding logic, not read out of any source — a measure I'll keep for you.
21.5 Started on demand, exits when idle, killable with one switch
You might picture such a supervisor as a resident daemon, eating memory from boot. It's not. In the strings its stance is on-demand — started only when used. With no work holding it, it says of itself nothing holding this daemon open and then idle-exits; the next time you type claude agents or claude --bg, a new one is pulled up. This is a different temperament from Chapter 17's "resident, watching a goal" autonomy: the supervisor exists only when there really are background sessions to watch, walks off on its own when there's no work, and doesn't sit on resources.
Starting on demand also brings a pitfall to guard against: what if two daemons start at the same instant. The strings have a clean yielding rule — an on-demand daemon never displaces a running one, and only a transient one may be displaced. Flipped around: a daemon already properly watching a batch of sessions has priority, and a freshly on-demand one may not shove it aside, avoiding two supervisors fighting and scrambling the fleet's books.
The whole thing also keeps a master switch. A setting reads verbatim: Disable agent view (claude agents, --bg, /background, and that on-demand daemon, all off together), typically pushed down in managed settings, equivalent to the environment variable CLAUDE_CODE_DISABLE_AGENT_VIEW=1. That is, the entire block of detaching-plus-fleet-supervision can be pressed off with one switch — for an organization that doesn't want background resident processes cropping up on employee machines, this is a path to shut it all down uniformly from above.
21.6 Another layer of the same landscape: subagents default to the background
There's a layer to add here that sits alongside detached sessions but rests on entirely different evidence: the backgrounding of subagents. What you /bg out is a whole session; inside a single session, meanwhile, the subagents the main session dispatches also run in the background by default. This picks right back up on the run_in_background from Chapter 8 and Chapter 19: in the current version, subagents run in the background by default. The AgentTool description is readable in the snapshot source, and the strings also keep a few slightly worded variants, all meaning the same thing: Subagents run in the background by default, notifying you when done; only when you must have its result before going on do you pass run_in_background: false for a synchronous run. In the schema run_in_background defaults to true, and it carries an isolation: 'worktree' field governing filesystem isolation — this run_in_background appears in ten files in the snapshot (AgentTool, BashTool, PowerShellTool, a bundled batch, and more); this layer has source you can read line by line, not inference from strings.