# Agent connection modes

> Daemon, in-process, and snapshot connection paths; agents-view mode; active session state and transfer idempotency constraints.

- 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/docs/agent-connection.md`
- `packages/coding-agent/test/agent-connection-daemon.test.ts`
- `packages/coding-agent/test/agent-connection-in-process.test.ts`
- `packages/coding-agent/test/agent-connection-snapshot.test.ts`
- `packages/coding-agent/src/modes/daemon/active-session-state.ts`
- `packages/coding-agent/src/modes/agents-view/agents-view-mode.ts`

---

---
title: "Agent connection modes"
description: "Daemon, in-process, and snapshot connection paths; agents-view mode; active session state and transfer idempotency constraints."
---

`AgentConnection` is the client boundary between UI surfaces and the process that owns agent execution. Interactive, agents-view, print, RPC, ACP, and SDK paths talk to the same TypeScript interface; only the adapter changes.

It is **not** the daemon wire protocol and not a hosted gateway API. Adapters own framing, versioning, reconnect, and DTO translation. The UI must not hold `AgentSessionRuntime`, `AgentSession`, `SessionManager`, daemon sockets, or executable callbacks through this boundary.

## Architecture

```mermaid
flowchart LR
  ui["InteractiveMode / agents-view / print / RPC / ACP"]
  iface["AgentConnection"]
  daemonAdapter["DaemonAgentConnection"]
  inprocAdapter["InProcessAgentConnection"]
  protocol["prime-agent.daemon protocol"]
  worker["Session worker"]
  runtime["AgentSessionRuntime"]

  ui --> iface
  iface --> daemonAdapter
  iface --> inprocAdapter
  daemonAdapter --> protocol --> worker --> runtime
  inprocAdapter --> runtime
```

| Path | Adapter | When it is used | Execution owner |
| --- | --- | --- | --- |
| Daemon (default interactive) | `DaemonAgentConnection` | Normal local interactive, agents view, most client modes when the daemon is available | Session worker under the daemon supervisor |
| In-process | `InProcessAgentConnection` | SDK/local fallback, process-local extension factories, owned-session worker, ACP/RPC/print when not on the daemon client path | Same process as the client |
| Snapshot transfer | Shared snapshot types + chunked stream | Attach, replacement, and resync of coherent session state (including large transcripts) | Produced by the execution owner; assembled by the client adapter |

Startup composition lives in `main.ts`. `InteractiveMode` only depends on `AgentConnection`.

## Startup routing

Client modes prefer the daemon unless startup is excluded (daemon process itself, startup benchmark, help, list-models, owned-session worker, or process-local extension factories).

| Decision helper | Effect |
| --- | --- |
| `shouldUseDaemonClient` | Client modes use the daemon transport when not the daemon process, not benchmarking, not help, not list-models |
| `shouldUseDaemonClientRuntime` | Same as above, but also false for owned-session workers and process-local extension factories |
| `shouldUseDaemonInteractive` | Interactive daemon path requires interactive mode, not `--no-session`, not list-models |
| `shouldOpenAgentsViewForDaemonInteractive` | Opens agents view only for explicit `agents`, without onboarding and without resume/continue/fork selectors |

Default interactive launch opens a chat session. The unified agents view is reached with `prime-agent agents` or by returning from a session (for example left-arrow when `returnToAgentsView` is set).

When the daemon interactive path cannot run (for example process-local extensions), interactive mode falls back to `InProcessAgentConnection` with `InteractiveModeLocalSessionHost` so callback-bearing local extensions still work.

## `AgentConnection` surface

Core operations exposed to clients:

- Prompt, steer, follow-up, abort, idle wait, bash
- Model / thinking / service tier / transport / queue settings
- Compaction, refine, retry abort, reload
- Session replace ops: new, switch, fork, import/export, tree navigation
- State, transcript, tree, context, stats, resources, model catalog
- Saved-session list/rename/delete
- Heartbeats and cron jobs
- Extension UI request/response (serializable only)
- RLM child cancel, agent messaging, optional `watchSession` for another live session

Selected event types:

| Event | Meaning |
| --- | --- |
| `session_event` | Live session activity (stream, tools, bash, refine, RLM children, …) |
| `session_replaced` | Runtime behind the connection was replaced; apply new state + messages |
| `session_resynced` | Full coherent snapshot after attach/reconnect/catch-up |
| `extension_ui_request` | Serializable extension UI op (select, confirm, input, editor, …) |
| `connection_status` | `reconnecting` / `connected` |
| `closed` | Terminal connection loss |

## Daemon connection path

### Adapter responsibilities

`DaemonAgentConnection` owns:

- A `DaemonClient` and stable client id (`daemon-agent-connection:<uuid>`)
- Active session id and latest snapshot cache
- Last event cursor / sequence and retired generations
- Streamed snapshot assembly (`session_snapshot_begin` / `chunk` / `end` / `failed`)
- Reconnect with the same identity and resume cursor when `recoverDaemon` is provided

Attach advertises client capabilities, including:

- `attach_snapshot`
- `event_sequence`
- `extension_ui` (interactive clients)
- `slim_attach`
- `chunked_snapshot`
- `client_owned_sessions` when the session is client-owned

### Protocol constants

| Constant | Current value / role |
| --- | --- |
| Protocol name | `prime-agent.daemon` |
| `DAEMON_PROTOCOL_VERSION` | `7` |
| `DAEMON_SCHEMA_REVISION` | `13` |
| Framing | JSONL (optional private-framed transport for some clients) |
| Command envelopes | Protocol metadata + optional `clientId` + command id (envelope min protocol 7) |

Wire changes must be classified as backward-compatible, capability-gated, or incompatible, with schema/protocol metadata and old/new client coverage updated together.

### Session lifecycle on create

| Lifecycle | Behavior |
| --- | --- |
| `resident` | Default interactive fleet sessions; survive detach; listed in agents view while live |
| `client_owned` | Headless/print/RPC-style owned workers; dispose can complete the worker instead of only detaching; require server capability `client_owned_sessions` |

Client env allowlist (`HERDR_*` keys) is sent on **create**, and on attach only for the primary interactive connection with `sendClientEnv`. Attach is adopt-if-absent: watchers must not rebind session identity.

### Reconnect and replay

1. Client keeps last cursor `{ generation, sequence }`.
2. On transient socket loss with recovery enabled, emit `connection_status: reconnecting`, probe/restart supervisor (`recoverDaemon`), reconnect, reattach with `resumeCursor`.
3. Default reconnect budget: `DAEMON_RECONNECT_TIMEOUT_MS` (60s).
4. Successful recovery emits `session_resynced` then `connection_status: connected`.
5. Replay status is `complete` | `partial` | `unavailable`. Generation changes make bare sequences non-comparable; `event_generation_changed` forces snapshot-as-truth.
6. The protocol does **not** guarantee infinite event history. Durable state + a fresh snapshot are the recovery baseline; replay is an optimization.

Duplicate or retired-generation events are dropped. Sequence numbers are only meaningful inside their generation.

## In-process connection path

`InProcessAgentConnection` wraps an `AgentSessionRuntime` in the same process.

Use cases:

- Interactive fallback when daemon client runtime is disabled
- SDK embedding
- ACP, RPC, and print when constructed with a local runtime
- Local extension binding via `InteractiveModeLocalSessionHost` / `bindHeadlessExtensions`

Behavior notes:

- Snapshots are built directly with `createAgentConnectionSnapshot` / `createAgentConnectionState`
- Session rebind emits `session_replaced` and rebinds session event listeners
- Tool definitions are serializable metadata only (no execute/renderer callbacks)
- Resource snapshots include remote-friendly `AgentConnectionArtifactReference` ids
- There is no daemon command journal or chunked socket transfer; process lifetime is the reliability domain

Executable callbacks and renderers never cross the generic `AgentConnection` boundary; they stay on local service hosts when needed.

## Snapshot path

An `AgentConnectionSnapshot` is the coherent UI view of execution state:

| Field | Role |
| --- | --- |
| `state` | Cached `AgentConnectionState` (model, streaming, queues, goal, recap, …) |
| `messages` | Finalized transcript |
| `streamingMessage` | In-progress assistant message (separate from finalized messages) |
| `sessionContext` / `sessionTree` | Context and branch tree |
| `parent` / `children` | Parent metadata and live RLM child snapshots |
| `lastEventSequence` / `lastEventCursor` | Resume markers |
| `replay` | Attach-time replay coverage |

### Chunked transfer

Clients advertising `chunked_snapshot` receive large transcripts as:

1. `session_snapshot_begin` — snapshot without messages, `snapshotId`, counts, purpose (`attach` \| `replacement` \| `resync`)
2. `session_snapshot_chunk` — ordered message batches
3. `session_snapshot_end` — chunk count + final cursor
4. or `session_snapshot_failed`

`slim_attach` omits redundant top-level attach fields; clients read `snapshot.summary` / `snapshot.messages` instead.

### Transfer identity and idempotency

Supervisor snapshot cache rules:

- Transfer identity is the stable stream envelope (`snapshotId`, message count, target chunk bytes, last event sequence/cursor)
- Live summary fields (activity, attached client counts) can change without advancing transcript sequence and must not alone invalidate a completed transfer
- Duplicate chunks/end records for a completed matching transfer are accepted; mismatched completed transfers fail the cache
- Failed transfers free the client to request a fresh snapshot
- Worker disconnect during transfer fails the in-flight generation; clients recover via resync/reattach

Client assemblies time out when a stream is incomplete; dispose rejects open assemblies.

## Active session state

Daemon-side `ActiveSessionState` is the supervisor’s record for a live session:

| Field | Role |
| --- | --- |
| `activeSessionId` | Display-oriented runtime id (unique among active sessions) |
| `runtime` | `AgentSessionRuntime` owner |
| `clients` | Attached socket clients |
| `pendingAttaches` | In-flight attaches (busyness without event fan-out yet) |
| `extensionUiRequests` | Outstanding serializable UI request resolvers |
| `eventGeneration` / `lastEventSequence` | Event cursor domain |
| `summaryState` | Latest background status for agents-view labels |
| `clientEnv` | Adopted allowlisted client env for `pi.exec` subprocesses; never rebound after set |

### Selector resolution

`resolveActiveSessionState(sessions, selector)` resolves:

1. Direct `activeSessionId` map hit
2. Exact `sessionId` or `sessionName` (unique only)
3. Suffix match on active session id or session id (unique only)

Ambiguous matches throw `AmbiguousActiveSessionError`. Unknown selectors throw `Unknown active session: …`.

Socket clients track attach/catch-up/backpressure:

- Events can be dropped while backpressured and replaced with one catch-up snapshot on drain
- Replacement catch-up takes precedence over ordinary resync for the same client/session
- Snapshot streaming can be aborted per session

### Multi-client attach

Multiple clients may attach to the same active session (primary TUI, agents-view open, subagent watchers). Switching to a session already owned by another resident worker reattaches; client-owned headless workers do not silently transfer ownership.

## Agents-view mode

`runAgentsViewMode` is the fleet browser for daemon-backed sessions.

Characteristics:

- Requires a daemon socket (`socketPath`)
- Lists live + passive + saved sessions with search, scopes, and slash commands (`name`, `kill`, …)
- Opening a row attaches with `DaemonAgentConnection.attach` when `activeSessionId` exists
- If the active runtime is gone but a session file remains, `create` with `sessionPath` resumes; create-with-path is **idempotent for that client** (already-resident sessions are reused)
- Missing original `cwd` falls back to the agents-view config cwd with a notice
- Opened chats set `returnToAgentsView: true` and `agentsViewOwnsStartupNotices: true`
- Scope frames support parent/child navigation (scoped agents view / scope back with optional return chat)
- Session crash tears down the chat TUI cleanly, then returns to the list with a status message

Without the daemon, `prime-agent agents` cannot run the full view; interactive fallback warns and opens a normal chat.

## Command lifecycle and mutation idempotency

Mutating daemon commands use a durable supervisor journal keyed by `clientId + commandId`:

| Journal status | Client result |
| --- | --- |
| `new` | Command is dispatched |
| `complete` | Recorded response is returned (safe retry) |
| `pending` | Response is **not** replayed; client gets `command_result_uncertain` |

Rules:

- Received is journaled **before** dispatch
- Missing result after crash is treated as uncertain, never blindly re-executed
- Clients acknowledge durable results so old journal rows can compact
- `AgentConnection` method promises are client convenience, not a general remote workflow state machine
- Attach is intentionally read-only and not fence-gated against idle eviction races; if the worker is gone, attach fails cleanly and the client can take the saved-session path

Prompt admission has its own cancellation statuses (`cancelled` \| `owned` \| `unknown` \| `unsupported`). Only confirmed `cancelled` is safely retryable as a fresh admission (`AgentConnectionPromptAdmissionError`).

## Extension and UI boundaries

Daemon-owned extensions may request serializable UI only (select, confirm, input, editor, notification, status, widget, title, editor-text). Clients validate and return serializable responses.

Never cross the connection:

- Tool `execute`, argument prep, custom renderers
- Extension runner callbacks
- Local completion functions
- Runtime / session-manager objects

Terminal rendering, keybindings, themes, clipboard, and local credential UI stay in `InteractiveModeUiServices` (or equivalent). If an action changes execution or persisted session state, it goes through `AgentConnection`.

Saved-session and import/export paths intentionally keep local filesystem shapes. Hosted transports should use opaque ids, string timestamps, and explicit artifact handles instead.

## Capability gating examples

| Capability | Client requirement |
| --- | --- |
| `side_question_transcript` | Required before multi-turn side-question previous turns |
| `transient_bash` | Required for unrecorded side-conversation bash + `runId` correlation |
| `prompt_admission_cancellation` | Required for signal-backed prompt cancel semantics |
| `heartbeat_catalog` / `heartbeat_management` | Heartbeat list/manage; degrade when absent |
| `model_catalog` | Full model catalog fetch |
| `client_owned_sessions` | Create/dispose owned workers |
| `delete_rlm_subagent` | Delete RLM subagent sessions |

Always check negotiated server capabilities before sending optional commands.

## Failure modes and verification

| Symptom | Likely cause | What to check |
| --- | --- | --- |
| `Unknown active session` | Stale id after worker passivation | Resume via session file / create-with-path |
| `Ambiguous active session` | Selector matches multiple live sessions | Use full active session id or unique name |
| `command_result_uncertain` | Mutation received then lost before durable result | Inspect external side effects; do not assume no-op or full success |
| Snapshot transfer failure | Worker disconnect or mismatched stream | Reattach; expect `session_resynced` |
| Reconnect timeout | Supervisor not recoverably starting | Daemon logs, `recoverDaemon` path, socket path |
| Agents view unavailable | No daemon / non-daemon interactive fallback | Start daemon path; avoid process-local-only startup if fleet UI is required |
| Extension UI missing | Client without `extension_ui` | Attach with interactive supportsExtensionUi |

Verification signals of a healthy daemon attach:

- Attach returns a snapshot (inline or completed stream)
- `getState()` / `getInitialSnapshot()` reflect session id and model
- Live events advance cursor within the same generation
- After forced socket loss with recovery, UI receives `connection_status` then `session_resynced`

## Related pages

<CardGroup cols={2}>
  <Card title="Run daemon-backed sessions" href="/daemon-sessions">
    Detach, reattach, resume selectors, and worker recovery.
  </Card>
  <Card title="Sessions and runtime" href="/sessions-runtime">
    Session lifecycle, events, queues, and tree navigation.
  </Card>
  <Card title="Long-running tasks" href="/long-running-tasks">
    Goals, compaction, heartbeats, and retained subagents across disconnects.
  </Card>
  <Card title="Subagents and messaging" href="/subagents-messaging">
    RLM children, agent messaging, and multi-agent constraints.
  </Card>
  <Card title="ACP mode" href="/acp-mode">
    Editor-facing protocol mode and connection composition.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Resume selectors, worker recovery, and connection-mode failure probes.
  </Card>
</CardGroup>
