# Run daemon-backed sessions

> Background daemon client modes, active session state, detach and reattach, resume selectors, and worker recovery verification.

- Repository: PrimeIntellect-ai/prime-agent
- GitHub: https://github.com/PrimeIntellect-ai/prime-agent
- Human docs: https://grok-wiki.com/public/docs/primeintellect-ai-prime-agent-3367c32760b1
- Complete Markdown: https://grok-wiki.com/public/docs/primeintellect-ai-prime-agent-3367c32760b1/llms-full.txt

## Source Files

- `packages/coding-agent/src/modes/daemon/active-session-state.ts`
- `packages/coding-agent/docs/agent-connection.md`
- `packages/coding-agent/test/agent-connection-daemon.test.ts`
- `packages/coding-agent/test/suite/regressions/4685-daemon-client-modes.test.ts`
- `packages/coding-agent/test/suite/regressions/4656-resume-active-session.test.ts`
- `packages/coding-agent/test/suite/regressions/4603-worker-recovery.test.ts`

---

---
title: "Run daemon-backed sessions"
description: "Background daemon client modes, active session state, detach and reattach, resume selectors, and worker recovery verification."
---

Daemon-backed sessions run agent execution in resident worker processes under a local supervisor. Terminal UIs, print/JSON/RPC clients, and CLI lifecycle commands attach over the daemon protocol (`prime-agent.daemon` protocol v7) while the worker owns the session, queue, kernel, and persisted transcript.

## Architecture

```mermaid
flowchart LR
  subgraph clients["Clients"]
    tui["Interactive TUI"]
    print["print / json / rpc"]
    cli["list · attach · stop · send"]
  end

  subgraph supervisor["Daemon supervisor"]
    socket["Unix socket / named pipe"]
    route["attach · detach · reattach · list"]
    journal["command journal + ownership"]
  end

  subgraph worker["Resident worker"]
    runtime["AgentSessionRuntime"]
    session["AgentSession + JSONL"]
    kernel["IPython kernel"]
  end

  tui --> socket
  print --> socket
  cli --> socket
  socket --> route
  route --> runtime
  runtime --> session
  runtime --> kernel
  journal -.-> route
```

| Layer | Responsibility |
|---|---|
| Client | Presentation, input, local preferences; talks through `AgentConnection` / `DaemonClient` |
| Supervisor | Socket accept, session routing, multi-client attach sets, ownership, recovery coordination |
| Worker | Provider calls, tools, queues, compaction, schedules, RLM descendants, persistence |

Workers are process-isolated for lifecycle and failure containment. They normally run with the same OS permissions as the client (not a security sandbox). Provider credentials stay BYOK: the worker uses the same auth surfaces as interactive sessions.

Default socket path:

| Platform | Path |
|---|---|
| Unix | `$TMPDIR/prime-agent-<uid>/daemon.sock` |
| Windows | `\\.\pipe\prime-agent-daemon` |

Override with `--daemon-socket <path>` (also accepted as `--socket` on internal daemon subcommands).

## Client modes

Interactive, print, JSON, RPC, piped-stdin, and `--no-session` clients share the daemon-owned worker runtime. Output protocols and exit semantics stay mode-specific; execution ownership does not.

| Mode | Entry | Execution owner | Client lifecycle |
|---|---|---|---|
| Interactive TUI | `prime-agent` (TTY) | Resident worker | Detach on UI exit; worker keeps running |
| Print | `--print` / `-p` | Resident worker | One-shot prompt; worker may remain for other clients |
| JSON | `--mode json` | Resident worker | JSONL events; daemon-backed |
| RPC | `--mode rpc` | Resident worker | Stdio JSON RPC; drains accepted commands before EOF |
| Piped stdin | non-TTY stdin | Resident worker | Same path as other headless clients |
| No session | `--no-session` | Resident worker (`noSession`) | Ephemeral session file behavior |
| Legacy rollback | `PRIME_AGENT_INTERNAL_LEGACY_OWNED_WORKER_FRONTEND=1` | In-process (not daemon) | Escape hatch only; does not create the daemon socket |

Headless runtime services (extensions that load agent tools, kernels, and related worker-side wiring) load in the worker process, not in the thin client frontend.

Client-owned (short-lived) workers advertise the `client_owned_sessions` capability and use `lifecycle: "client_owned"` on `create`. Resident sessions omit client ownership and survive client exit. Promotion from owned to resident commits durable worker state before best-effort peer sync; persistence failure rolls the promotion back.

## Active session state

Each live agent has an **active session id** (`activeSessionId`) distinct from the durable `sessionId` / session file. Supervisor state tracks:

| Field | Role |
|---|---|
| `activeSessionId` | Public attach target (12-char display form of a UUID by default) |
| `runtime` | `AgentSessionRuntime` for the worker-hosted session |
| `clients` | Set of attached `DaemonSocketClient` sockets |
| `pendingAttaches` | In-flight attach snapshot reservations (counts toward busyness) |
| `eventGeneration` / `lastEventSequence` | Event cursor generation + monotonic sequence |
| `clientEnv` | Allowlisted client env (e.g. Herdr pane identity), bound once at create/adopt |
| `summaryState` | Background status for the agents view |

Socket clients also track per-socket attach sets, optional catch-up after backpressure, snapshot streaming handles, and capability negotiation (`attach_snapshot`, `event_sequence`, `extension_ui`, `slim_attach`, `chunked_snapshot`, `client_owned_sessions`).

Session list rows (`SessionSummary`) expose:

| Field | Values / notes |
|---|---|
| `lifecycle` | `draft` (no message yet) · `live` (agents view) · `archived` (resume-only) |
| `activity` | `working` · `idle` |
| `workerState` | `starting` · `ready` · `recovering` · `failed` |
| `workerPid` | Diagnostic only — not a stable session id |
| `attachedClients` | Count of attached sockets |
| `sessionName` | Optional human name for selectors |

## Lifecycle commands

Public agent lifecycle surface (preferred):

```bash
prime-agent list [--all] [--json]
prime-agent attach <agent>
prime-agent stop <agent> [--json]
prime-agent rename <agent> <name> [--json]
prime-agent send [--from <agent>] <agent> <message>
prime-agent agents
prime-agent status [--json]
prime-agent doctor [--fix] [--json]
prime-agent shutdown [--force] [--json]
```

| Command | Behavior |
|---|---|
| `list` | Live agents by default; `--all` includes saved/non-live rows |
| `attach <agent>` | Rewrites to interactive `--resume <agent>` (cannot combine with `--resume` / `--continue` / `--fork`) |
| `stop <agent>` | Daemon `kill` for that active session |
| `rename` | Sets durable `sessionName` |
| `send` | Direct inter-agent message (`--steer` / `--follow-up` modes) |
| `agents` | Opens the agents view |
| `status` | Background service / process inventory |
| `doctor` | Inspect (and optionally `--fix`) stale sockets / idle orphans |
| `shutdown` | Stop all agents and services; `--force` skips confirm and kills unresponsive workers |

Internal `prime-agent daemon …` remains available for development; public `daemon` as a top-level command is rejected in favor of the agent commands above.

### Start and open

`daemon start` (or `daemon open` when no supervisor is listening) spawns:

```bash
prime-agent --mode daemon --daemon-socket <path> [session flags…]
```

as a detached child, then waits until the socket accepts connections (10s timeout). `open` then `create`s a session (auto name `1`, `2`, … when unnamed) and attaches a lightweight readline terminal.

### Create, attach, detach (protocol)

| Command | Purpose |
|---|---|
| `create` | Spawn/register a session; optional `sessionPath`, `continueRecent`, `name`, `config`, `lifecycle` |
| `attach` | Bind a client to `activeSessionId`; optional `resumeCursor`, capabilities, adopt-only client env |
| `detach` | Unbind this client (`activeSessionId` optional for detach-all-on-socket) |
| `reattach` | Move one client from `activeSessionId` to `targetActiveSessionId` without stopping peers |
| `kill` | Stop the worker/session |
| `retry_worker` | CLI `daemon retry <session>` — request worker restart/retry |
| `promote_owned_session` / `complete_owned_session` | Client-owned lifecycle transitions |

Attach returns a coherent `DaemonAttachResult`: protocol info, snapshot (state + messages + last event cursor), replay status (`complete` | `partial` | `unavailable`), optional chunked `snapshotStream`, and negotiated client id/capabilities.

Large transcripts stream as `session_snapshot_begin` / `session_snapshot_chunk` / `session_snapshot_end`. Live events carry generation-aware cursors `{ generation, sequence }`. Bare sequence numbers are not comparable across worker generations.

## Detach and reattach

Closing the TUI or ending a headless attach **detaches the client**. It does not stop the resident worker. Queue, schedules, goals, heartbeats, kernel, and RLM descendants keep running under the supervisor.

```bash
prime-agent list
prime-agent attach <activeSessionId|sessionId|name|suffix>
```

From a lightweight daemon attach terminal, `/detach`, `/quit`, and `/exit` send `detach` and print `Detached.`.

### Multi-client attach

Multiple clients may attach independently to the same active session. Detaching one client does not detach peers.

### `/resume` onto an already-live worker

When `switch_session` targets a session path already resident under another active id, the supervisor returns `session_already_active` with `activeSessionId`. Non-owned clients then issue `reattach` from the source active id to the target, apply a replacement snapshot (inline or streamed), and emit `session_replaced`. Peer clients on source and target stay attached.

Client-owned headless workers do **not** silently reattach across ownership; `ownedSession` surfaces rethrow `session_already_active` instead of reattaching.

### Socket loss

After a transient socket drop, `DaemonAgentConnection` reconnects with the same client identity and last cursor, reattaches, and emits `session_resynced`. If incremental replay is unavailable, the attach snapshot is the recovery baseline. Optional `recoverDaemon` hooks run during reconnect for supervisor recovery waits.

## Resume selectors

Selectors resolve against live sessions (CLI and supervisor) in this order:

1. Exact `activeSessionId` map key
2. Exact `sessionId` or `sessionName`
3. Unambiguous hex **suffix** match on `activeSessionId` or `sessionId` (normalized hex; display ids are 12 chars)

| Outcome | Error / result |
|---|---|
| One match | That session’s `activeSessionId` |
| Multiple matches | `Ambiguous active session "<selector>": matches …` / CLI `Ambiguous active session "<selector>"` |
| No match | `Unknown active session: <selector>` |

Saved-session resume (disk picker) uses path or id forms:

```bash
prime-agent --resume
prime-agent --resume <path|id>
prime-agent --resume -- "continue this work"
prime-agent --continue
prime-agent attach <live-agent>   # live daemon target → interactive resume
```

| Selector context | Accepts |
|---|---|
| Live daemon (`list` / `attach` / `stop` / messaging) | Active id, session id, session name, unambiguous suffix |
| Saved resume (`--resume` / `/resume`) | Session file path, session id (prefix/suffix heuristics for saved ids), interactive picker |
| Archived lifecycle | Not shown in default live list; reachable via saved resume |

`attach` cannot be combined with `--resume`, `--continue`, or `--fork`.

## Worker recovery

Resident workers are replaceable under the **current supervisor generation**. Recovery properties enforced by protocol and tests:

| Mechanism | Behavior |
|---|---|
| Supervisor generation | Only the current owner generation may replace a crashed worker; stale supervisors are displaced |
| Worker auth + commands | Stale generation commands fail with `supervisor_generation_stale` |
| Public command journal | Mutating commands journaled by `clientId + commandId`; completed retries return recorded results; uncertain in-flight results are not blindly replayed |
| Ownership lease | Displaced owner cannot insert new public journal entries (`no longer owns`) |
| Worker recovery journal | Append-only busy/idle operation records per `activeSessionId` (`version: 1`, fsync, compact when all idle) |
| `workerState` | Surfaces `recovering` / `failed` on list rows while replacement runs |
| `daemon retry <session>` | Explicit retry/restart request for a live session |
| Update restart | Daemon update path can restore/resume interrupted sessions with counts for total/restored/resumed/failed |

After replacement, clients reattach by `activeSessionId`, take a fresh snapshot, and continue prompts. Event generation changes; clients must treat post-recovery cursors as a new generation.

Worker recovery journal record shape:

```json
{
  "version": 1,
  "activeSessionId": "…",
  "sessionId": "…",
  "sessionFile": "…",
  "busy": true,
  "operation": "…",
  "recordedAt": "2026-…"
}
```

## Protocol notes

| Constant | Value |
|---|---|
| Protocol name | `prime-agent.daemon` |
| Protocol version | `7` |
| Schema revision | `13` |
| Framing | JSONL (public); private framed transport for worker links |
| Mutating commands | Journaled; stable `clientId` + `commandId` for idempotency |
| Client env allowlist | `HERDR_ENV`, `HERDR_PANE_ID`, `HERDR_SOCKET_PATH`, `HERDR_TAB_ID`, `HERDR_WORKSPACE_ID` (create/adopt only; attach does not rebind identity) |

Classify wire changes as backward-compatible, capability-gated, or incompatible. Capability-gated features require clients to check server capabilities before sending (e.g. `client_owned_sessions`, extension UI, chunked snapshots).

## Verification

<Steps>
  <Step title="Start or reuse a daemon-backed session">
    From a project directory with providers configured:

```bash
prime-agent --print --model <provider/model> "ping"
```

    Expect exit code 0 and no `Timed out waiting for daemon worker` on stderr. The default daemon socket should exist under the platform path above (or the path passed via `--daemon-socket`).
  </Step>
  <Step title="List live agents">
```bash
prime-agent list
prime-agent list --json
```

    Expect a row with `lifecycle: live`, an `activeSessionId`, and `workerState: ready` when healthy.
  </Step>
  <Step title="Detach and reattach">
    Start an interactive session, exit the UI (detach), then:

```bash
prime-agent list
prime-agent attach <activeSessionId-or-name>
```

    Expect the interactive UI to reconnect to the same worker without recreating the session file from scratch.
  </Step>
  <Step title="Confirm multi-mode headless path">
```bash
prime-agent --mode rpc --daemon-socket <path> …
# send: {"id":"state","type":"get_state"}
```

    Expect a successful `get_state` response. EOF should drain already-accepted RPC commands before the connection releases.
  </Step>
  <Step title="Service health">
```bash
prime-agent status
prime-agent doctor
```

    Use `doctor --fix` only when intentional cleanup of stale sockets/orphans is desired. Use `shutdown --force` to stop the whole local daemon fabric.
  </Step>
</Steps>

## Failure modes

| Symptom | Likely cause | Action |
|---|---|---|
| `Unknown active session: …` | Bad selector or session not live | `list`; use exact id/name or longer suffix |
| `Ambiguous active session "…"` | Suffix matches multiple lives | Use full `activeSessionId` or unique `sessionName` |
| `Session is already active in …` | Target already resident | Non-owned UI should reattach; owned workers must not steal |
| `supervisor_generation_stale` | Worker still bound to displaced supervisor | Wait for current generation replacement; reattach |
| `Timed out waiting for daemon worker` | Worker launch failed | Check model/auth, extensions, socket path, `status` / `doctor` |
| Daemon socket already in use | Another supervisor owns the path | Reuse it, or `shutdown` then restart |
| `no longer owns` on mutate | Client talking to displaced supervisor | Reconnect; do not retry against stale owner |
| Attach works but model missing in UI | Client lacks credentials the worker has | Prefer daemon summary `modelFallbackMessage`; fix worker env/auth |
| SIGINT in print mode leaves work running | Detach ≠ kill | `stop <agent>` or `shutdown` if the session should end |

## Related pages

<CardGroup>
  <Card title="Agent connection modes" href="/agent-connection">
    Daemon vs in-process vs snapshot paths, capabilities, and transfer constraints.
  </Card>
  <Card title="Sessions and runtime" href="/sessions-runtime">
    Session lifecycle, services, events, queueing, and tree navigation.
  </Card>
  <Card title="Long-running tasks" href="/long-running-tasks">
    Goals, compaction, heartbeats, and work that continues across detach.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Invalid resume selectors, worker recovery probes, and connection failures.
  </Card>
</CardGroup>
