How Prime Agent Handles Session Attachment and Reconnection: A Deep Dive into the Daemon Architecture
Prime Agent uses a long-running daemon process paired with two TypeScript classes—DaemonClient and DaemonAgentConnection—to manage session attachment, snapshot streaming, and automatic recovery after daemon disconnections.
The prime-agent CLI communicates with a persistent background daemon through a JSON-L socket protocol. This architecture separates state management (daemon) from user interaction (CLI/TUI), enabling features like session resumption and seamless reconnections after crashes or updates. Understanding how session attachment and reconnection work is essential for building reliable automations or debugging connectivity issues.
Core Components: DaemonClient and DaemonAgentConnection
Prime Agent's reconnection logic spans two coordinated layers:
| Component | File Path | Responsibility |
|---|---|---|
| DaemonClient | packages/coding-agent/src/modes/daemon/daemon-client.ts |
Low-level JSON-L socket wrapper with auto-reconnect capability |
| DaemonAgentConnection | packages/coding-agent/src/modes/agent-connection/daemon-agent-connection.ts |
High-level session lifecycle manager that handles attachment, snapshot assembly, and re-attachment |
The DaemonClient manages request queuing and socket state. The DaemonAgentConnection builds on this to implement the AgentConnection interface, translating daemon-level events into session-level state machines.
Attaching to a Session
When you run prime-agent attach <session-id> or use --resume, the CLI instantiates a DaemonClient, connects to the daemon socket, then delegates to DaemonAgentConnection.attach().
The attach() Flow
// packages/coding-agent/src/modes/agent-connection/daemon-agent-connection.ts
static async attach(
client: DaemonClient,
activeSessionId: string,
options?: DaemonAgentConnectionOptions,
): Promise<DaemonAgentConnection> {
const connection = new DaemonAgentConnection(client, activeSessionId, options);
await connection.attach(); // Sends "attach" command to daemon
return connection;
}
The attach() method constructs a daemon attach request containing:
activeSessionId— the target session to join- Capabilities array — including
"attach_snapshot","event_sequence","chunked_snapshot", and UI or ownership flags (lines 1003–1013) - Optional environment payloads (
env,launchEnv) to propagate client environment variables to the daemon (lines 1013–1015)
Snapshot Assembly
The daemon responds with either a complete snapshot or a snapshot stream. For streamed responses, DaemonAgentConnection invokes waitForSnapshot() to reassemble fragments:
// Snapshot stream event types handled:
// - session_snapshot_begin
// - session_snapshot_chunk
// - session_snapshot_end
This produces a complete AgentConnectionSnapshot (lines 1038–1048). Upon success, the connection caches:
attachedSessionId— the definitive session identifier returned by the daemon (lines 1019–1022)latestSnapshot— the current session state, marked as fresh (lines 1018–1021)
Handling Session Re-attachment After Daemon Restart
When resuming a session whose worker was recreated, the daemon may return a "reattach" response. DaemonAgentConnection detects this in switchSession() and automatically invokes reattachSession() (lines 1125–1130).
Re-attachment Steps
- Preserve state — Save event cursor, snapshot cache, and connection metadata
- Send reattach request —
type: "reattach"with original capabilities mirrored (lines 1150–1158) - Receive fresh snapshot — Replace old snapshot; emit
session_replacedevent (lines 1181–1187) - Rollback on failure — Restore original state for retry or error reporting (lines 1190–1199)
This graceful degradation ensures users can recover sessions even when the underlying daemon worker identity changes.
Automatic Reconnection After Daemon Disconnection
Both classes cooperate to survive daemon crashes, updates, or network interruptions.
DaemonClient Recovery Mechanism
The DaemonClient monitors socket closure via socket.on("close") and triggers notifyClosed. Key behaviors:
- Request recovery — If
requestRecoveryEnabledis set (byDaemonAgentConnectionviaoptions.recoverDaemon), pending requests enterawaitingReconnect = truestate and will be resent after reconnection (lines 43–49, 62–68) - Auto-reconnect loop —
enableAutoReconnect()repeatedly calls the user-providedrecoverDaemoncallback, reconnects the socket, awaits the daemon's hello message, then resolves pending requests (lines 97–108, 118–126)
DaemonAgentConnection Recovery Strategy
The DaemonAgentConnection interprets close reasons and selects appropriate recovery:
| Close Reason | Recovery Action |
|---|---|
"update" |
Invoke reconnectAfterUpdate() — deliberate disconnect, wait, then reconnect transport (lines 69–73) |
| Other (crash, network) | Invoke reconnect(error) — full re-attachment flow with recoverDaemon callback (lines 63–71, 123–130) |
During reconnection, the connection:
- Uses
options.reconnectTimeoutMsor defaults to 60 seconds (DAEMON_RECONNECT_TIMEOUT_MS) (lines 1030–1034) - Re-sends the original attach request
- Emits
"session_resynced"when the fresh snapshot is ready - Emits
"closed"if reconnection ultimately fails (lines 78–80)
Snapshot Failure Recovery
If snapshot streaming fails (session_snapshot_failed), DaemonAgentConnection examines the purpose ("attach", "replacement", "resync") and may call recoverFailedSnapshot() to retry or fallback to a full snapshot. This prevents partial or corrupt session states (lines 61–81).
Complete Working Example
Attaching with Auto-Reconnection Enabled
import { DaemonClient } from "./daemon-client.js";
import { DaemonAgentConnection } from "./daemon-agent-connection.js";
// Initialize and connect to daemon socket
const client = new DaemonClient("/tmp/prime-agent.sock");
await client.connect();
await client.waitForHello();
// Enable auto-reconnect at the socket layer
client.enableAutoReconnect({
recoverDaemon: async () => {
// Restart daemon via systemd, launchctl, or direct process spawn
await execa("systemctl", ["restart", "prime-agent-daemon"]);
},
onStatus: (status) => console.log("Daemon status:", status),
});
// Attach to existing session with recovery capabilities
const connection = await DaemonAgentConnection.attach(
client,
"session-abc-123",
{
supportsExtensionUi: true,
recoverDaemon: async () => {
// Same recovery logic used for session-level reconnection
await execa("systemctl", ["restart", "prime-agent-daemon"]);
},
ownedSession: false,
},
);
// Connection now survives daemon restarts transparently
const state = await connection.getState();
await connection.prompt("Continue from where we left off?");
Manual Re-attachment After Session ID Change
// Daemon was updated, session migrated to new worker ID
await connection.reattachSession(
"old-session-id", // sourceActiveSessionId
"new-session-id", // targetActiveSessionId
);
Key Source Files
| File | Purpose |
|---|---|
packages/coding-agent/src/modes/daemon/daemon-client.ts |
Socket client with request queuing, auto-reconnect, and hello protocol handling |
packages/coding-agent/src/modes/agent-connection/daemon-agent-connection.ts |
Session lifecycle: attach, snapshot assembly, re-attachment, recovery orchestration |
packages/coding-agent/src/modes/daemon/daemon-protocol.ts |
Command type definitions (attach, reattach, capability constants) |
packages/coding-agent/src/modes/agent-connection/types.ts |
AgentConnection interface definitions |
Summary
- Prime Agent session attachment uses
DaemonAgentConnection.attach()with capability negotiation and snapshot streaming to establish a live view of daemon-managed state. - Re-attachment (
reattachSession) handles cases where the daemon recreates a session worker, preserving client state while acquiring a fresh snapshot. - Automatic reconnection spans both
DaemonClient(socket-level) andDaemonAgentConnection(session-level), with configurable timeouts and user-provided recovery callbacks. - Snapshot integrity is guaranteed through streaming reassembly and failure recovery, preventing partial state corruption.
- All timeout defaults (60 seconds) and event sequences are defined in
daemon-agent-connection.tsanddaemon-client.tsaccording to the Prime Intellectprime-agentsource code.
Frequently Asked Questions
How does Prime Agent recover if the daemon crashes during an active session?
The DaemonClient detects socket closure and—if enableAutoReconnect() was configured—enters a retry loop calling your recoverDaemon callback to restart the daemon process. Once the socket reconnects and receives a hello message, pending requests are re-sent. Meanwhile, DaemonAgentConnection detects the close and invokes reconnect(), which re-issues the original attach request and emits "session_resynced" when the fresh snapshot arrives.
What happens to my session state if I use --resume after a long disconnect?
If the daemon preserved the session (or recreated its worker), DaemonAgentConnection.attach() returns a connection with the latest snapshot. If the session ID changed, the daemon sends a "reattach" response; the connection automatically calls reattachSession() to migrate to the new session ID while preserving your event cursor and UI state. If re-attachment fails, the original state is restored for retry or error handling.
Can I customize the reconnection timeout?
Yes. Pass reconnectTimeoutMs in DaemonAgentConnectionOptions when calling attach(). The default is 60 seconds (DAEMON_RECONNECT_TIMEOUT_MS defined at lines 1030–1034 of daemon-agent-connection.ts). The DaemonClient also respects timeout configurations in its auto-reconnect settings.
What's the difference between attach and reattach in the protocol?
Attach (type: "attach") establishes initial connection to a session, sending capabilities and environment. Reattach (type: "reattach") is used when a session's underlying worker has been replaced; it mirrors the original capabilities but targets a potentially different activeSessionId, allowing seamless migration without losing client-side state.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →