2026-01-05 05:55:02 +01:00
---
summary: "Agent loop lifecycle, streams, and wait semantics"
read_when:
- You need an exact walkthrough of the agent loop or lifecycle events
2026-01-31 16:04:03 -05:00
title: "Agent Loop"
2026-01-05 05:55:02 +01:00
---
2026-01-31 21:13:13 +09:00
2026-01-30 03:15:10 +01:00
# Agent Loop (OpenClaw)
2026-01-05 05:55:02 +01:00
2026-01-18 14:43:35 +00:00
An agentic loop is the full “real” run of an agent: intake → context assembly → model inference →
tool execution → streaming replies → persistence. It’ s the authoritative path that turns a message
into actions and a final reply, while keeping session state consistent.
2026-01-30 03:15:10 +01:00
In OpenClaw, a loop is a single, serialized run per session that emits lifecycle and stream events
2026-01-18 14:43:35 +00:00
as the model thinks, calls tools, and streams output. This doc explains how that authentic loop is
wired end-to-end.
2026-01-05 05:55:02 +01:00
## Entry points
2026-01-31 21:13:13 +09:00
2026-01-08 23:06:56 +01:00
- Gateway RPC: `agent` and `agent.wait` .
- CLI: `agent` command.
2026-01-05 05:55:02 +01:00
2026-01-18 14:43:35 +00:00
## How it works (high-level)
2026-01-31 21:13:13 +09:00
1. `agent` RPC validates params, resolves session (sessionKey/sessionId), persists session metadata, returns `{ runId, acceptedAt }` immediately.
2. `agentCommand` runs the agent:
2026-01-05 05:55:02 +01:00
- 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
2026-01-31 21:13:13 +09:00
3. `runEmbeddedPiAgent` :
2026-01-18 14:30:04 +00:00
- 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
2026-01-05 05:55:02 +01:00
- enforces timeout -> aborts run if exceeded
- returns payloads + usage metadata
2026-01-31 21:13:13 +09:00
4. `subscribeEmbeddedPiSession` bridges pi-agent-core events to OpenClaw `agent` stream:
2026-01-05 05:55:02 +01:00
- tool events => `stream: "tool"`
- assistant deltas => `stream: "assistant"`
- lifecycle events => `stream: "lifecycle"` (`phase: "start" | "end" | "error"` )
2026-01-31 21:13:13 +09:00
5. `agent.wait` uses `waitForAgentJob` :
2026-01-05 05:55:02 +01:00
- waits for **lifecycle end/error** for `runId`
- returns `{ status: ok|error|timeout, startedAt, endedAt, error? }`
2026-01-18 14:30:04 +00:00
## Queueing + concurrency
2026-01-31 21:13:13 +09:00
2026-01-18 14:30:04 +00:00
- 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
2026-01-31 21:13:13 +09:00
2026-01-18 14:30:04 +00:00
- 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
2026-01-31 21:13:13 +09:00
2026-01-30 03:15:10 +01:00
- System prompt is built from OpenClaw’ s base prompt, skills prompt, bootstrap context, and per-run overrides.
2026-01-18 14:30:04 +00:00
- Model-specific limits and compaction reserve tokens are enforced.
- See [System prompt ](/concepts/system-prompt ) for what the model sees.
2026-01-18 14:43:35 +00:00
## Hook points (where you can intercept)
2026-01-31 21:13:13 +09:00
2026-01-30 03:15:10 +01:00
OpenClaw has two hook systems:
2026-01-31 21:13:13 +09:00
2026-01-18 14:43:35 +00:00
- **Internal hooks** (Gateway hooks): event-driven scripts for commands and lifecycle events.
- **Plugin hooks**: extension points inside the agent/tool lifecycle and gateway pipeline.
### Internal hooks (Gateway hooks)
2026-01-31 21:13:13 +09:00
2026-01-18 14:43:35 +00:00
- **`agent:bootstrap` **: runs while building bootstrap files before the system prompt is finalized.
Use this to add/remove bootstrap context files.
- **Command hooks**: `/new` , `/reset` , `/stop` , and other command events (see Hooks doc).
2026-02-07 15:40:35 -05:00
See [Hooks ](/automation/hooks ) for setup and examples.
2026-01-18 14:43:35 +00:00
### Plugin hooks (agent + gateway lifecycle)
2026-01-31 21:13:13 +09:00
2026-01-18 14:43:35 +00:00
These run inside the agent loop or gateway pipeline:
2026-01-31 21:13:13 +09:00
2026-02-17 03:28:10 +01:00
- **`before_model_resolve` **: runs pre-session (no `messages` ) to deterministically override provider/model before model resolution.
2026-03-06 02:06:59 +08:00
- **`before_prompt_build` **: runs after session load (with `messages` ) to inject `prependContext` , `systemPrompt` , `prependSystemContext` , or `appendSystemContext` before prompt submission. Use `prependContext` for per-turn dynamic text and system-context fields for stable guidance that should sit in system prompt space.
2026-02-17 03:28:10 +01:00
- **`before_agent_start` **: legacy compatibility hook that may run in either phase; prefer the explicit hooks above.
2026-01-18 14:43:35 +00:00
- **`agent_end` **: inspect the final message list and run metadata after completion.
- **`before_compaction` / `after_compaction` **: observe or annotate compaction cycles.
- **`before_tool_call` / `after_tool_call` **: intercept tool params/results.
2026-01-19 13:11:31 +01:00
- **`tool_result_persist` **: synchronously transform tool results before they are written to the session transcript.
2026-01-18 14:43:35 +00:00
- **`message_received` / `message_sending` / `message_sent` **: inbound + outbound message hooks.
- **`session_start` / `session_end` **: session lifecycle boundaries.
- **`gateway_start` / `gateway_stop` **: gateway lifecycle events.
2026-02-07 15:40:35 -05:00
See [Plugins ](/tools/plugin#plugin-hooks ) for the hook API and registration details.
2026-01-18 14:43:35 +00:00
2026-01-18 14:30:04 +00:00
## Streaming + partial replies
2026-01-31 21:13:13 +09:00
2026-01-18 14:30:04 +00:00
- 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
2026-01-31 21:13:13 +09:00
2026-01-18 14:30:04 +00:00
- 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
2026-01-31 21:13:13 +09:00
2026-01-18 14:30:04 +00:00
- 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
2026-01-31 21:13:13 +09:00
2026-01-18 14:30:04 +00:00
- 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.
2026-01-05 05:55:02 +01:00
## Event streams (today)
2026-01-31 21:13:13 +09:00
2026-01-05 05:55:02 +01:00
- `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
2026-01-13 08:11:59 +00:00
## Chat channel handling
2026-01-31 21:13:13 +09:00
2026-01-08 23:06:56 +01:00
- Assistant deltas are buffered into chat `delta` messages.
- A chat `final` is emitted on **lifecycle end/error** .
2026-01-05 05:55:02 +01:00
## Timeouts
2026-01-31 21:13:13 +09:00
2026-01-05 05:55:02 +01:00
- `agent.wait` default: 30s (just the wait). `timeoutMs` param overrides.
2026-01-09 12:44:23 +00:00
- Agent runtime: `agents.defaults.timeoutSeconds` default 600s; enforced in `runEmbeddedPiAgent` abort timer.
2026-01-05 05:55:02 +01:00
## Where things can end early
2026-01-31 21:13:13 +09:00
2026-01-05 05:55:02 +01:00
- Agent timeout (abort)
- AbortSignal (cancel)
- Gateway disconnect or RPC timeout
- `agent.wait` timeout (wait-only, does not stop agent)