The OpenClaw agent loop
From an accepted run id to a final reply, the writer claim, the hooks, the streams, and the timeouts
Every reply an OpenClaw agent produces comes out of the same loop: the serialised, per-session run that turns a message into actions and a reply through intake, context assembly, model inference, tool execution, streaming and persistence. The docs walk it step by step. Here is the sequence from an accepted run id to a terminal event, the fence that keeps a superseded run from writing stale transcript data, the hooks a plugin can attach, what streams where, the timeouts, and the diagnostics that classify a run that looks stuck.
The run sequence
- The agent RPC validates its parameters, resolves the session, persists session metadata and returns a run id immediately; the CLI agent command is the other entry point.
- The command layer resolves the model and the thinking, verbose and trace defaults, loads the skills snapshot, runs the embedded agent, and emits a fallback lifecycle end or error if the loop did not.
- The embedded run serialises through the per-session and global lanes, resolves the model and auth profile, builds the session, subscribes to runtime events, streams assistant and tool deltas, enforces the run timeout and returns payloads with usage metadata; tool events go to a tool stream, assistant deltas to an assistant stream, and lifecycle events to a lifecycle stream with start, finishing, end and error phases.
- The wait RPC waits for a lifecycle end or error on a run id and returns ok, error or timeout with start and end times, plus the terminal reply and, when available, a receipt confirming the final reply reached the source conversation.
The agent loop is the serialized, per-session run that turns a message into actions and a reply: intake, context assembly, model inference, tool execution, streaming, persistence.
The writer claim and preparation
Runs are serialised per session key and optionally through a global lane. Before streaming, an admitted run records its durable active-writer claim; every transcript append or rewrite supplies the expected writer id, and the synchronous commit transaction verifies it still matches, so a superseded run cannot commit stale transcript data; the SQLite writer queue orders per-agent mutations and a state-directory lock keeps a second Gateway or local agent process from owning the same directory. Preparation resolves and creates the workspace, redirecting sandboxed runs to a sandbox root, loads skills or reuses a snapshot, resolves bootstrap files into the system prompt, and prepares the transcript target and writer claim before streaming starts. The system prompt is built from the base prompt, the skills prompt, bootstrap context and per-run overrides, with model limits and compaction reserve tokens enforced.
Hooks, streams, reply shaping
- Two hook systems: internal hook scripts for command and lifecycle events, bootstrap plus the new, reset and stop commands, and typed plugin hooks inside the loop, before model resolve, before prompt build, before agent reply, agent end, around compaction, around tool calls, before install, tool result persist, message received, sending and sent, session start and end, Gateway start and stop. A block or cancel decision is terminal and stops lower-priority handlers, while a false is a no-op that does not clear an earlier block.
- Assistant deltas stream as assistant events, block streaming can emit partial replies at text or message end, reasoning can be its own stream, tool start, update and end events emit on the tool stream with results sanitised for size and images, and messaging-tool sends are tracked to suppress duplicate confirmations; the Gateway projects lifecycle and tool events into a metadata-only audit ledger.
- Final payloads assemble assistant text, optional reasoning, inline tool summaries when verbose allows, and error text when the model errors; the exact silent token is filtered, messaging duplicates removed, a fallback tool-error warning appears only when a run ends on a tool failure with no reply, and a required-reply turn that ends after a settled tool batch without an answer can get one tool-free finalisation pass that never repeats completed tools.
The OpenClaw command queue is the lane system this loop serialises through, and OpenClaw hooks and webhooks the automation side of the hook story.
Timeouts and stuck runs
The wait call times out after thirty seconds without stopping the run; the agent runtime budget is forty-eight hours of elapsed execution, zero for unlimited, with approval waits pausing the unused budget; the CLI no-output watchdog belongs to the backend plugin; cron owns its own timer; a model request is aborted when no chunks arrive for two minutes on cloud providers or five on self-hosted ones, extendable per provider; and the provider HTTP timeout covers connect, headers and body. A terminal timeout is a failed turn, recorded for the sidebar immediately. With diagnostics on, a two-minute threshold classifies a long processing session as long-running when progress is recent, stalled when it is not, and stuck for stale bookkeeping, with an abort threshold of at least five minutes and three times the warning; a Codex attempt waiting on native work is the exception and is never aborted merely for being quiet. A run can end early on the agent timeout, an abort signal, a Gateway disconnect, or the wait-only timeout. OpenClaw context window and compaction explains the retry the loop triggers when context overflows, and OpenClaw streaming and chunking how the deltas reach a channel.
On Diali
On Diali this is the loop your assistant runs inside its instance, with the forty-eight-hour budget and the idle watchdogs at their upstream defaults, and the dashboard chat shows the lifecycle as it streams. Hosted OpenClaw on Diali is the assistant and The OpenClaw Gateway explained the process that runs the loop.
- A run id first, a lifecycle end last.
- One writer per session, verified at every commit.
- Forty-eight hours of budget; two or five minutes of silence.
Stop reading about it, build one
Set up an agent, pick a channel, and have it working inside the app you already keep open.
