Files
openclaw/docs/agent-runtime-architecture.md
Peter Steinberger 0042f8a570 perf(agents): keep file edits from blocking other chats (#158443)
* perf(agents): keep file-tool computation off the main thread

* test(agents): check complete file plans and patch receipts

* fix(agents): package file planning for portable workers

* refactor(agents): avoid redundant diff options copy

* test(workers): consolidate portable artifact assertions

* test(workers): distinguish artifact paths from install input

* fix(ci): register deployed file planning worker for Knip

Exact-head CI reported the sealed worker entry as unused because it is built and launched by path. Register it alongside the existing deployed workers without excluding it from dependency or export analysis.

* test(build): classify deployed file planner as a worker

The package-output contract treated the newly registered sealed worker as a normal .js package entry. Include it in the existing worker cases and bundled-dependency assertions. All 45 tsdown configuration tests pass in 272.39 seconds wall time; focused lint and independent review pass.

* test(build): cover planner worker schema inlining

Register the file planning worker in the existing executable graph assertion. All 32 runtime-config tests pass (59.81 seconds wall, 0.261 seconds test time); targeted lint and independent review are clean.

* test(pr): avoid executing a writable fixture inode

Launch the copied PR wrapper through the fixture Bash binary. Hold a writable descriptor across spawn to deterministically exercise Linux ETXTBSY without retries. The old direct-exec variant fails with ETXTBSY; all 146 operation-lock tests pass after the fix on Linux Testbox in 56.53 seconds wall time. Lint and independent P0-P2 review pass.
2026-09-26 03:55:55 -05:00

8.1 KiB

title, summary
title summary
Agent runtime architecture How OpenClaw structures the built-in agent runtime: code layout, boundaries, resource manifests, and runtime selection.

OpenClaw owns the built-in agent runtime. Runtime code lives under src/agents/, model/provider transport lives under src/llm/, and openclaw/plugin-sdk/* barrels expose the plugin-facing contracts.

Runtime Layout

Path Owns
src/agents/embedded-agent-runner/ Built-in attempt loop (run.ts, run/), model selection and provider normalization (model*.ts), per-provider request params (extra-params.*), compaction, transcript and session wiring.
src/agents/sessions/ Session persistence (session-manager.ts), resource discovery (package-manager.ts, resource-loader.ts), in-session extensions loading, prompt templates, skills, themes, and TUI-backed tool renderers (tools/).
packages/agent-core/ Reusable agent core (@openclaw/agent-core): agent loop, harness types, messages, compaction helpers, prompt templates, skills, and session storage contracts.
src/agents/runtime/ OpenClaw facade that wires @openclaw/agent-core to the plugin SDK LLM runtime and re-exports it plus local proxy utilities.
src/agents/agent-tools*.ts OpenClaw-owned tool definitions, parameter schemas, tool policy, before/after tool-call adapters, and host/sandbox edit tools.
src/agents/agent-hooks/ Built-in runtime hooks: compaction safeguard, compaction instructions, context pruning.
src/agents/harness/ Harness registry, selection policy, and lifecycle for the built-in and plugin-registered harnesses.
src/llm/ Model/provider registry, transport helpers, and provider-specific stream implementations (src/llm/providers/).

Boundaries

Core calls the built-in runtime through OpenClaw modules and SDK barrels. No external agent framework packages remain. Plugins use documented openclaw/plugin-sdk/* entrypoints and do not import src/** internals.

@earendil-works/pi-tui remains a third-party dependency: a terminal component toolkit used by the local TUI and session tool renderers. Internalizing it would be a separate vendoring effort.

Manifests

Resource packages declare OpenClaw resources in package.json metadata. Entries are file paths or globs relative to the package root:

{
  "openclaw": {
    "extensions": ["extensions/index.ts"],
    "skills": ["skills/*.md"],
    "prompts": ["prompts/*.md"],
    "themes": ["themes/*.json"]
  }
}

Resource types not listed in a manifest fall back to discovery of conventional extensions/, skills/, prompts/, and themes/ directories.

Runtime Selection

  • The built-in runtime id is openclaw. The legacy alias pi normalizes to openclaw. The alias codex-app-server normalizes to codex.
  • Plugin harnesses register additional runtime ids (for example codex).
  • Runtime policy is model/provider-scoped agentRuntime.id config (model entry wins over provider entry). Unset or default resolves to auto.
  • auto selects a registered plugin harness that supports the effective provider route, otherwise the built-in OpenClaw runtime. A provider or model prefix alone never selects a harness.
  • OpenAI may select codex implicitly. This happens only for an exact official HTTPS Platform Responses or ChatGPT Responses route with no authored request override. Completions adapters, custom endpoints, and routes with authored request behavior stay on openclaw. Plaintext official HTTP endpoints are rejected. See OpenAI implicit agent runtime.

Model Runtime Generations

Gateway startup and config, plugin, or auth publication build one prepared model runtime generation per configured agent. Each generation owns the discovered auth template, model registry, and projected model catalog as one atomic snapshot. Agent runs fork mutable auth and registry stores from that snapshot. Browse, status, cron, doctor, TUI, PDF, and image paths read the published catalog instead of repeating filesystem discovery.

Standalone embedded runtimes publish the same snapshot shape at their activation boundary. A failed or stale generation is never served alongside a newer partial generation. The lifecycle owner must publish a complete replacement first.

Runtime selections resolve in the requesting agent's scope before becoming owner keys. Lease admission carries that prepared choice forward and reads the exact owner's snapshot. Retries must observe a changed owner or publication gate; unchanged publication state fails with a retryable error instead of blocking the Gateway event loop.

Compute workers

Code-mode execution, compaction planning, and file-tool planning use the reusable WorkerTaskPool. Their pools share a CPU admission limit of max(1, availableParallelism() - 1) within the calling isolate, reserving a CPU where possible for the Gateway. Ordered database and model-generation workers keep their existing independent limits.

File-tool workers perform pure edit matching, Unicode normalization, and diff computation. One prepared patch supplies both display and unified-patch receipts, including previews. The file-tool caller keeps the mutation queue, filesystem access, persisted-byte verification, and authority checks; it revalidates authority and cancellation after planning before changing files. Write receipts retain their existing size and edit-distance limits. The shared runtime-process registry resolves the planning worker in both the installed package and the sealed portable-worker bundle.

Admission includes queued, preparing, and running tasks. Each pool defaults to 128 pending tasks and 256 MiB of producer-reported retained input; compute pools also share those pending limits. Producers supply known input sizes without an extra serialization pass. This bounds reported input retention, not total worker heap usage. Excess work fails with WorkerTaskError.code = "overloaded". Cancellation retains the execution permit and input reservation until both the worker stops and asynchronous input preparation settles. If the initial stop succeeds, result rejection follows its execution receipt without waiting for preparation. A failed stop can reject earlier while retaining native custody and the pending receipt for retry. Successful pool closure joins the remaining preparation and input cleanup; graceful rotation can finish before canceled preparation settles.

Waiting compute pools request checkpoints from code-mode host exchanges so that nested work can progress. Idle workers release CPU admission and retire after the pool's idle timeout. Local node:diagnostics_channel subscribers to openclaw.worker.task can observe queue, preparation, execution wall time, message transfer time, and pending task/input counts. These events contain no task inputs; execution wall time includes worker startup and host waits.