--- summary: "Context engine: pluggable context assembly, compaction, and subagent lifecycle" read_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 title: "Context engine" --- A **context engine** controls how OpenClaw builds model context for each run: which messages to include, how to summarize older history, and how to manage context across subagent boundaries. OpenClaw ships with a built-in `legacy` engine and uses it by default — most users never need to change this. Install and select a plugin engine only when you want different assembly, compaction, or cross-session recall behavior. ## Quick start Check which engine is active: ```bash openclaw doctor # or inspect config directly: cat ~/.openclaw/openclaw.json | jq '.plugins.slots.contextEngine' ``` ### Installing a context engine plugin Context engine plugins are installed like any other OpenClaw plugin. Install first, then select the engine in the slot: ```bash # Install from npm openclaw plugins install @martian-engineering/lossless-claw # Or install from a local path (for development) openclaw plugins install -l ./my-context-engine ``` Then enable the plugin and select it as the active engine in your config: ```json5 // 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) }, }, }, } ``` Restart the gateway after installing and configuring. To switch back to the built-in engine, set `contextEngine` to `"legacy"` (or remove the key entirely — `"legacy"` is the default). ## How it works Every time OpenClaw runs a model prompt, the context engine participates at four lifecycle points: 1. **Ingest** — called when a new message is added to the session. The engine can store or index the message in its own data store. 2. **Assemble** — called before each model run. The engine returns an ordered set of messages (and an optional `systemPromptAddition`) that fit within the token budget. 3. **Compact** — called when the context window is full, or when the user runs `/compact`. The engine summarizes older history to free space. 4. **After turn** — called after a run completes. The engine can persist state, trigger background compaction, or update indexes. ### Subagent lifecycle (optional) OpenClaw calls two optional subagent lifecycle hooks: - **prepareSubagentSpawn** — prepare shared context state before a child run starts. The hook receives parent/child session keys, `contextMode` (`isolated` or `fork`), available transcript ids/files, and optional TTL. If it returns a rollback handle, OpenClaw calls it when spawn fails after preparation succeeds. - **onSubagentEnded** — clean up when a subagent session completes 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 lets engines inject dynamic recall guidance, retrieval instructions, or context-aware hints without requiring static workspace files. ## The legacy engine The built-in `legacy` engine preserves OpenClaw's original behavior: - **Ingest**: no-op (the session manager handles message persistence directly). - **Assemble**: pass-through (the existing sanitize → validate → limit pipeline in the runtime handles context assembly). - **Compact**: delegates to the built-in summarization compaction, which creates a single summary of older messages and keeps recent messages intact. - **After turn**: no-op. The legacy engine does not register tools or provide a `systemPromptAddition`. When no `plugins.slots.contextEngine` is set (or it's set to `"legacy"`), this engine is used automatically. ## Plugin engines A plugin can register a context engine using the plugin API: ```ts import { buildMemorySystemPromptAddition } from "openclaw/plugin-sdk/core"; export default function register(api) { api.registerContextEngine("my-engine", () => ({ 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, 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, }), }; }, async compact({ sessionId, force }) { // Summarize older context return { ok: true, compacted: true }; }, })); } ``` Then enable it in config: ```json5 { 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` — the ordered messages to send to the model. - `estimatedTokens` (required, `number`) — the engine's estimate of total tokens in the assembled context. OpenClaw uses this for compaction threshold decisions and diagnostic reporting. - `systemPromptAddition` (optional, `string`) — prepended to the system prompt. Optional members: | Member | Kind | Purpose | | ------------------------------ | ------ | --------------------------------------------------------------------------------------------------------------- | | `bootstrap(params)` | Method | Initialize engine state for a session. Called once when the engine first sees a session (e.g., import history). | | `ingestBatch(params)` | Method | Ingest a completed turn as a batch. Called after a run completes, with 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. | ### ownsCompaction `ownsCompaction` controls whether Pi's built-in in-attempt auto-compaction stays enabled for the run: - `true` — the engine owns compaction behavior. OpenClaw disables Pi's built-in auto-compaction for that run, and the engine's `compact()` implementation is responsible for `/compact`, overflow recovery compaction, and any proactive compaction it wants to do in `afterTurn()`. - `false` or unset — Pi'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. `ownsCompaction: false` does **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** — set `ownsCompaction: false` and have `compact()` call `delegateCompactionToRuntime(...)` from `openclaw/plugin-sdk/core` to use OpenClaw's built-in compaction behavior. A no-op `compact()` is unsafe for an active non-owning engine because it disables the normal `/compact` and overflow-recovery compaction path for that engine slot. ## Configuration reference ```json5 { plugins: { slots: { // Select the active context engine. Default: "legacy". // Set to a plugin id to use a plugin engine. contextEngine: "legacy", }, }, } ``` The slot is exclusive at run time — only one registered context engine is resolved for a given run or compaction operation. Other enabled `kind: "context-engine"` plugins can still load and run their registration code; `plugins.slots.contextEngine` only selects which registered engine id OpenClaw resolves when it needs a context engine. ## Relationship to compaction and memory - **Compaction** is one responsibility of the context engine. The legacy engine delegates to OpenClaw's built-in summarization. Plugin engines can implement any compaction strategy (DAG summaries, vector retrieval, etc.). - **Memory plugins** (`plugins.slots.memory`) are separate from context engines. Memory plugins provide search/retrieval; context engines control what the model sees. They can work together — a context engine might use memory plugin data during assembly. Plugin engines that want the active memory prompt path should prefer `buildMemorySystemPromptAddition(...)` from `openclaw/plugin-sdk/core`, which converts the active memory prompt sections into a ready-to-prepend `systemPromptAddition`. If an engine needs lower-level control, it can still pull raw lines from `openclaw/plugin-sdk/memory-host-core` via `buildActiveMemoryPromptSection(...)`. - **Session pruning** (trimming old tool results in-memory) still runs regardless of which context engine is active. ## Tips - Use `openclaw doctor` to verify your engine is loading correctly. - If switching engines, existing sessions continue with their current history. The new engine takes over for future runs. - Engine errors are logged and surfaced in diagnostics. If a plugin engine fails to register or the selected engine id cannot be resolved, OpenClaw does not fall back automatically; runs fail until you fix the plugin or switch `plugins.slots.contextEngine` back to `"legacy"`. - For development, use `openclaw plugins install -l ./my-engine` to link a local plugin directory without copying. See also: [Compaction](/concepts/compaction), [Context](/concepts/context), [Plugins](/tools/plugin), [Plugin manifest](/plugins/manifest). ## Related - [Context](/concepts/context) — how context is built for agent turns - [Plugin Architecture](/plugins/architecture) — registering context engine plugins - [Compaction](/concepts/compaction) — summarizing long conversations