Context Engine: Pluggable Assembly, Compaction, and Subagent Lifecycle
Learn how OpenClaw's context engine governs model context construction, including message assembly, conversation compaction, and subagent lifecycle. This page explains the default legacy engine and how to install plugin engines.
Read this when
- You want to understand how OpenClaw assembles model context
- You are switching between the legacy engine and a plugin engine
- You are building a context engine plugin
A context engine governs how OpenClaw constructs model context during each execution: which messages are included, how older conversation history gets condensed, and how context is handled across subagent boundaries.
OpenClaw includes a default legacy engine that is active out of the box. Only install and choose a plugin engine if you need different message assembly, compaction strategies, or cross-session recall behavior.
Quick start
Check which engine is active
openclaw doctor
# or inspect config directly:
cat ~/.openclaw/openclaw.json | jq '.plugins.slots.contextEngine'
Install a plugin engine
Context engine plugins follow the same installation process as any other OpenClaw plugin.
From npm
openclaw plugins install @martian-engineering/lossless-claw
From a local path
openclaw plugins install -l ./my-context-engine
Enable and select the engine
// openclaw.json
{
plugins: {
slots: {
contextEngine: "lossless-claw", // must match the plugin's registered engine id
},
entries: {
"lossless-claw": {
enabled: true,
// Plugin-specific config goes here (see the plugin's docs)
},
},
},
}
After installation and configuration, restart the gateway.
Switch back to legacy (optional)
Set contextEngine to "legacy" (or omit the key entirely, since "legacy" is the default).
How it works
Each time OpenClaw runs a model prompt, the context engine engages at four lifecycle stages:
1. Ingest
Triggered when a new message enters the session. The engine may store or index the message in its own data store.
2. Assemble
Invoked before each model run. The engine provides an ordered collection of messages (and an optional systemPromptAddition) that stays within the token limit.
3. Compact
Called when the context window reaches capacity, or when the user triggers /compact. The engine condenses older history to reclaim space.
4. After turn
Executed after a run finishes. The engine can persist state, launch background compaction, or update indexes.
Engines may also implement an optional maintain() method for transcript maintenance (safe rewrites through runtimeContext.rewriteTranscriptEntries()) after bootstrap, a successful turn, or compaction. Configure info.turnMaintenanceMode: "background" to run this as deferred work instead of blocking the response.
For the bundled non-ACP Codex harness, OpenClaw applies the same lifecycle by mapping assembled context into Codex developer instructions and the current turn prompt. Codex retains its own native thread history and native compactor.
Subagent lifecycle (optional)
OpenClaw invokes two optional subagent lifecycle hooks:
-
prepareSubagentSpawn(method), Prepare shared context state before a child run begins. The hook receives parent/child session keys,contextMode(isolatedorfork), available transcript ids/files, and an optional TTL. If it returns a rollback handle, OpenClaw calls it when spawn fails after preparation succeeds. Native subagent spawns that requestlightContextand resolve tocontextMode="isolated"intentionally bypass this hook so the child starts from the lightweight bootstrap context without context-engine-managed pre-spawn state. -
onSubagentEnded(method), Perform cleanup when a subagent session ends or is swept.
System prompt addition
The assemble method can return a systemPromptAddition string. OpenClaw prepends this to the system prompt for the run. This allows engines to inject dynamic recall guidance, retrieval instructions, or context-aware hints without needing static workspace files.
The legacy engine
The built-in legacy engine maintains OpenClaw's original behavior:
- Ingest: does nothing (the session manager handles message persistence directly).
- Assemble: passes through (the existing sanitize → validate → limit pipeline in the runtime handles context assembly).
- Compact: delegates to the built-in summarization compaction, which produces a single summary of older messages while keeping recent messages intact.
- After turn: does nothing.
The legacy engine does not register tools or provide a systemPromptAddition.
When no plugins.slots.contextEngine is configured (or it's set to "legacy"), this engine is used automatically.
Plugin engines
A plugin can register a context engine through the plugin API:
import { buildMemorySystemPromptAddition } from "openclaw/plugin-sdk/core";
export default function register(api) {
api.registerContextEngine("my-engine", (ctx) => ({
info: {
id: "my-engine",
name: "My Context Engine",
ownsCompaction: true,
},
async ingest({ sessionId, message, isHeartbeat }) {
// Store the message in your data store
return { ingested: true };
},
async assemble({
sessionId,
sessionKey,
messages,
tokenBudget,
availableTools,
citationsMode,
}) {
// Return messages that fit the budget
return {
messages: buildContext(messages, tokenBudget),
estimatedTokens: countTokens(messages),
systemPromptAddition: buildMemorySystemPromptAddition({
availableTools: availableTools ?? new Set(),
citationsMode,
agentSessionKey: sessionKey,
}),
};
},
async compact({ sessionId, force }) {
// Summarize older context
return { ok: true, compacted: true };
},
}));
}
The factory ctx includes optional config, agentDir, and workspaceDir
values so plugins can initialize per-agent or per-workspace state before the
first lifecycle call. Before a non-legacy assemble() call, the host completes
registered async memory prompt preparation. The synchronous
buildMemorySystemPromptAddition(...) helper reads that immutable run snapshot;
pass the supplied tool, citation, agent, and session context through unchanged.
Then enable it in configuration:
{
plugins: {
slots: {
contextEngine: "my-engine",
},
entries: {
"my-engine": {
enabled: true,
},
},
},
}
The ContextEngine interface
Required members:
| Member | Kind | Purpose |
|---|---|---|
info | Property | Engine id, name, version, and whether it owns compaction |
ingest(params) | Method | Store a single message |
assemble(params) | Method | Build context for a model run (returns AssembleResult) |
compact(params) | Method | Summarize/reduce context |
assemble returns an AssembleResult with:
-
messages(Message[], required), The ordered messages to send to the model. -
estimatedTokens(number, required), The engine's estimate of total tokens in the assembled context. OpenClaw uses this for compaction threshold decisions and diagnostic reporting. -
systemPromptAddition(string), Prepended to the system prompt. -
promptAuthority(assembled" | "preassembly_may_overflow), Determines which token count the runner uses when deciding whether to compact preemptively. The default is"assembled", meaning only the assembled prompt's estimate is checked for engines that do not handle compaction themselves. Engines withownsCompaction: truetake charge of their own prompt admission, so OpenClaw skips the standard pre-prompt precheck by default. Set"preassembly_may_overflow"only if your assembled view could conceal overflow risk within the underlying transcript; in that case the runner keeps the generic precheck active and uses the larger of the assembled estimate and the pre-assembly (unwindowed) session-history estimate when deciding on preemptive compaction. Either way, the messages you return are exactly what the model sees,promptAuthorityonly influences the precheck. -
contextProjection(ContextEngineProjection), An optional projection lifecycle for hosts that maintain persistent backend threads (for example, the Codex app-server).mode: "thread_bootstrap"paired with a stableepochtells the host to inject the assembled context once per epoch and reuse that backend thread until the epoch changes, avoiding a re-projection on every turn. Leave this field out for the usual per-turn projection.
compact returns a CompactResult. When compaction changes the active session identity, result.sessionTarget (a typed ContextEngineSessionTarget containing the session identity and store scope) identifies the successor session that the next retry or turn must use; result.sessionId mirrors that successor id.
Optional members:
| Member | Kind | Purpose |
|---|---|---|
bootstrap(params) | Method | Initialize engine state for a session. Called once when the engine first encounters a session (e.g., import history). |
maintain(params) | Method | Transcript maintenance after bootstrap, a successful turn, or compaction. Use runtimeContext.rewriteTranscriptEntries() for safe rewrites. |
ingestBatch(params) | Method | Ingest a completed turn as a batch. Called after a run finishes, receiving all messages from that turn at once. |
afterTurn(params) | Method | Post-run lifecycle work (persist state, trigger background compaction). |
prepareSubagentSpawn(params) | Method | Set up shared state for a child session before it starts. |
onSubagentEnded(params) | Method | Clean up after a subagent ends. |
dispose() | Method | Release resources. Called during gateway shutdown or plugin reload, not per-session. |
Runtime settings
Lifecycle hooks running inside OpenClaw receive an optional runtimeSettings object. This is a versioned, read-only internal producer/consumer API surface: OpenClaw produces it for the chosen context engine, and the context engine consumes it within lifecycle hooks. It is not rendered directly to users and does not create a dedicated reporting surface.
schemaVersion: currently1runtime: OpenClaw host, runtime mode (normal,fallback, ordegraded), and optional harness/runtime idscontextEngineSelection: selected context engine id and selection sourceexecutionHost: host id and label for the surface invoking the hookmodel: requested model, resolved model, provider, and optional model familylimits: prompt token budget and max output tokens when knowndiagnostics: closed fallback and degraded reason codes when known
Fields that may be unknown appear as null; discriminator fields such as runtime mode and selection source are always non-nullable. Older engines stay compatible: if a strict legacy engine rejects runtimeSettings as an unknown property, OpenClaw retries the lifecycle call without it instead of quarantining the engine.
Host requirements
Context engines can declare host capability requirements on info.hostRequirements. OpenClaw checks these requirements before starting the operation and fails closed with a descriptive error when the selected runtime cannot meet them.
For agent runs, declare assemble-before-prompt when the engine must control the actual model prompt through assemble():
info: {
id: "my-context-engine",
name: "My Context Engine",
hostRequirements: {
"agent-run": {
requiredCapabilities: ["assemble-before-prompt"],
unsupportedMessage:
"Use the native Codex or OpenClaw embedded runtime, or select the legacy context engine.",
},
},
}
Native Codex and OpenClaw embedded agent runs satisfy assemble-before-prompt. Generic CLI backends do not, so engines that require it are rejected before the CLI process starts.
Failure isolation
OpenClaw isolates the selected plugin engine from the core reply path. If a non-legacy engine is missing, fails contract validation, throws during factory creation, or throws from a lifecycle method, OpenClaw quarantines that engine for the current Gateway process and downgrades context-engine work to the built-in legacy engine. The error is logged with the failed operation so the operator can repair, update, or disable the plugin without the agent going silent.
Host requirement failures work differently: when an engine declares that a runtime lacks a required capability, OpenClaw fails closed before starting the run. This protects engines that would corrupt state if they ran in an unsupported host.
ownsCompaction
ownsCompaction controls whether OpenClaw runtime's built-in in-attempt auto-compaction stays enabled for the run:
ownsCompaction: true
The engine owns compaction behavior. OpenClaw disables OpenClaw runtime's built-in auto-compaction and generic pre-prompt overflow precheck for that run, and the engine's compact() implementation is responsible for /compact, provider overflow recovery compaction, and any proactive compaction it wants to do in afterTurn(). OpenClaw still runs the pre-prompt overflow safeguard when the engine returns promptAuthority: "preassembly_may_overflow" from assemble().
ownsCompaction: false or unset
OpenClaw runtime's built-in auto-compaction may still run during prompt execution, but the active engine's compact() method is still called for /compact and overflow recovery.
Warning
ownsCompaction: falsedoes not mean OpenClaw automatically falls back to the legacy engine's compaction path.
That means there are two valid plugin patterns:
Owning mode
Implement your own compaction algorithm and set ownsCompaction: true.
Delegating mode
To use OpenClaw's default compaction behavior, set ownsCompaction: false and have compact() invoke delegateCompactionToRuntime(...) from openclaw/plugin-sdk/core.
For an active non-owning engine, a no-op compact() is unsafe because it turns off the standard /compact and overflow-recovery compaction path for that engine slot.
Configuration reference
{
plugins: {
slots: {
// Select the active context engine. Default: "legacy".
// Set to a plugin id to use a plugin engine.
contextEngine: "legacy",
},
},
}
Note
At runtime the slot is exclusive. Only one registered context engine gets resolved per run or compaction operation. Other enabled
kind: "context-engine"plugins can still load and execute their registration code;plugins.slots.contextEnginesimply determines which registered engine id OpenClaw resolves when a context engine is needed.
Note
Plugin uninstall: When you uninstall the plugin currently selected as
plugins.slots.contextEngine, OpenClaw resets that slot back to the default (legacy). The same reset behavior applies toplugins.slots.memory. No manual configuration changes are needed.
Relationship to compaction and memory
Compaction
Compaction is one task handled by the context engine. The legacy engine delegates to OpenClaw's built-in summarization. Plugin engines can implement any compaction strategy they choose, such as DAG summaries or vector retrieval.
Memory plugins
Memory plugins (plugins.slots.memory) are distinct from context engines. Memory plugins provide search and retrieval capabilities, while context engines govern what the model sees. They can operate together. For instance, a context engine might use data from a memory plugin during assembly. Plugin engines that need the active memory prompt path should use buildMemorySystemPromptAddition(...) from openclaw/plugin-sdk/core. This method turns the host-prepared memory prompt sections into a ready-to-prepend systemPromptAddition without exposing the memory-plugin layout.
Session pruning
Trimming old tool results in memory still runs regardless of which context engine is active.
Tips
- Use
openclaw doctorto confirm your engine loads correctly. - When switching engines, existing sessions keep their current history. The new engine takes over for subsequent runs.
- Engine errors are logged, and the selected plugin engine is quarantined for the current Gateway process. OpenClaw falls back to
legacyfor user turns so replies can continue, but you should still repair, update, disable, or uninstall the broken plugin. - For development, use
openclaw plugins install -l ./my-engineto link a local plugin directory without copying.
Related
- Compaction - summarizing long conversations
- Context - how context is built for agent turns
- Plugin Architecture - registering context engine plugins
- Plugin manifest - plugin manifest fields
- Plugins - plugin overview