--- summary: "Agent loop lifecycle, streams, and wait semantics" read_when: - You need an exact walkthrough of the agent loop or lifecycle events --- # Agent Loop (Clawdbot) Short, exact flow of one agent run, plus the important internal stages. ## Entry points - Gateway RPC: `agent` and `agent.wait`. - CLI: `agent` command. ## High-level flow 1) `agent` RPC validates params, resolves session (sessionKey/sessionId), persists session metadata, returns `{ runId, acceptedAt }` immediately. 2) `agentCommand` runs the agent: - resolves model + thinking/verbose defaults - loads skills snapshot - calls `runEmbeddedPiAgent` (pi-agent-core runtime) - emits **lifecycle end/error** if the embedded loop does not emit one 3) `runEmbeddedPiAgent`: - serializes runs via per-session + global queues - resolves model + auth profile and builds the pi session - subscribes to pi events and streams assistant/tool deltas - enforces timeout -> aborts run if exceeded - returns payloads + usage metadata 4) `subscribeEmbeddedPiSession` bridges pi-agent-core events to Clawdbot `agent` stream: - tool events => `stream: "tool"` - assistant deltas => `stream: "assistant"` - lifecycle events => `stream: "lifecycle"` (`phase: "start" | "end" | "error"`) 5) `agent.wait` uses `waitForAgentJob`: - waits for **lifecycle end/error** for `runId` - returns `{ status: ok|error|timeout, startedAt, endedAt, error? }` ## Queueing + concurrency - Runs are serialized per session key (session lane) and optionally through a global lane. - This prevents tool/session races and keeps session history consistent. - Messaging channels can choose queue modes (collect/steer/followup) that feed this lane system. See [Command Queue](/concepts/queue). ## Session + workspace preparation - Workspace is resolved and created; sandboxed runs may redirect to a sandbox workspace root. - Skills are loaded (or reused from a snapshot) and injected into env and prompt. - Bootstrap/context files are resolved and injected into the system prompt report. - A session write lock is acquired; `SessionManager` is opened and prepared before streaming. ## Prompt assembly + system prompt - System prompt is built from Clawdbot’s base prompt, skills prompt, bootstrap context, and per-run overrides. - Model-specific limits and compaction reserve tokens are enforced. - See [System prompt](/concepts/system-prompt) for what the model sees. ## Streaming + partial replies - Assistant deltas are streamed from pi-agent-core and emitted as `assistant` events. - Block streaming can emit partial replies either on `text_end` or `message_end`. - Reasoning streaming can be emitted as a separate stream or as block replies. - See [Streaming](/concepts/streaming) for chunking and block reply behavior. ## Tool execution + messaging tools - Tool start/update/end events are emitted on the `tool` stream. - Tool results are sanitized for size and image payloads before logging/emitting. - Messaging tool sends are tracked to suppress duplicate assistant confirmations. ## Reply shaping + suppression - Final payloads are assembled from: - assistant text (and optional reasoning) - inline tool summaries (when verbose + allowed) - assistant error text when the model errors - `NO_REPLY` is treated as a silent token and filtered from outgoing payloads. - Messaging tool duplicates are removed from the final payload list. - If no renderable payloads remain and a tool errored, a fallback tool error reply is emitted (unless a messaging tool already sent a user-visible reply). ## Compaction + retries - Auto-compaction emits `compaction` stream events and can trigger a retry. - On retry, in-memory buffers and tool summaries are reset to avoid duplicate output. - See [Compaction](/concepts/compaction) for the compaction pipeline. ## Event streams (today) - `lifecycle`: emitted by `subscribeEmbeddedPiSession` (and as a fallback by `agentCommand`) - `assistant`: streamed deltas from pi-agent-core - `tool`: streamed tool events from pi-agent-core ## Chat channel handling - Assistant deltas are buffered into chat `delta` messages. - A chat `final` is emitted on **lifecycle end/error**. ## Timeouts - `agent.wait` default: 30s (just the wait). `timeoutMs` param overrides. - Agent runtime: `agents.defaults.timeoutSeconds` default 600s; enforced in `runEmbeddedPiAgent` abort timer. ## Where things can end early - Agent timeout (abort) - AbortSignal (cancel) - Gateway disconnect or RPC timeout - `agent.wait` timeout (wait-only, does not stop agent)