{"id":608,"date":"2026-03-21T22:52:56","date_gmt":"2026-03-21T14:52:56","guid":{"rendered":"https:\/\/pa.yingzhi8.cn\/index.php\/2026\/03\/21\/concepts-context-engine\/"},"modified":"2026-03-21T23:08:54","modified_gmt":"2026-03-21T15:08:54","slug":"concepts-context-engine","status":"publish","type":"post","link":"https:\/\/pa.yingzhi8.cn\/index.php\/2026\/03\/21\/concepts-context-engine\/","title":{"rendered":"Context Engine"},"content":{"rendered":"<h1>Context Engine<\/h1>\n<p>A <strong>context engine<\/strong> controls how OpenClaw builds model context for each run.<br \/>\nIt decides which messages to include, how to summarize older history, and how<br \/>\nto manage context across subagent boundaries.<\/p>\n<p>OpenClaw ships with a built-in <code>legacy<\/code> engine. Plugins can register<br \/>\nalternative engines that replace the active context-engine lifecycle.<\/p>\n<h2>Quick start<\/h2>\n<p>Check which engine is active:<\/p>\n<p>&#8220;`bash  theme={&#8220;theme&#8221;:{&#8220;light&#8221;:&#8221;min-light&#8221;,&#8221;dark&#8221;:&#8221;min-dark&#8221;}}<br \/>\nopenclaw doctor<\/p>\n<h1>or inspect config directly:<\/h1>\n<p>cat ~\/.openclaw\/openclaw.json | jq &#8216;.plugins.slots.contextEngine&#8217;<\/p>\n<pre><code>\n### Installing a context engine plugin\n\nContext engine plugins are installed like any other OpenClaw plugin. Install\nfirst, then select the engine in the slot:\n\n```bash  theme={&quot;theme&quot;:{&quot;light&quot;:&quot;min-light&quot;,&quot;dark&quot;:&quot;min-dark&quot;}}\n# Install from npm\nopenclaw plugins install @martian-engineering\/lossless-claw\n\n# Or install from a local path (for development)\nopenclaw plugins install -l .\/my-context-engine\n<\/code><\/pre>\n<p>Then enable the plugin and select it as the active engine in your config:<\/p>\n<p>&#8220;`json5  theme={&#8220;theme&#8221;:{&#8220;light&#8221;:&#8221;min-light&#8221;,&#8221;dark&#8221;:&#8221;min-dark&#8221;}}<br \/>\n\/\/ openclaw.json<br \/>\n{<br \/>\n  plugins: {<br \/>\n    slots: {<br \/>\n      contextEngine: &#8220;lossless-claw&#8221;, \/\/ must match the plugin&#8217;s registered engine id<br \/>\n    },<br \/>\n    entries: {<br \/>\n      &#8220;lossless-claw&#8221;: {<br \/>\n        enabled: true,<br \/>\n        \/\/ Plugin-specific config goes here (see the plugin&#8217;s docs)<br \/>\n      },<br \/>\n    },<br \/>\n  },<br \/>\n}<\/p>\n<pre><code>\nRestart the gateway after installing and configuring.\n\nTo switch back to the built-in engine, set `contextEngine` to `&quot;legacy&quot;` (or\nremove the key entirely \u2014 `&quot;legacy&quot;` is the default).\n\n## How it works\n\nEvery time OpenClaw runs a model prompt, the context engine participates at\nfour lifecycle points:\n\n1. **Ingest** \u2014 called when a new message is added to the session. The engine\n   can store or index the message in its own data store.\n2. **Assemble** \u2014 called before each model run. The engine returns an ordered\n   set of messages (and an optional `systemPromptAddition`) that fit within\n   the token budget.\n3. **Compact** \u2014 called when the context window is full, or when the user runs\n   `\/compact`. The engine summarizes older history to free space.\n4. **After turn** \u2014 called after a run completes. The engine can persist state,\n   trigger background compaction, or update indexes.\n\n### Subagent lifecycle (optional)\n\nOpenClaw currently calls one subagent lifecycle hook:\n\n* **onSubagentEnded** \u2014 clean up when a subagent session completes or is swept.\n\nThe `prepareSubagentSpawn` hook is part of the interface for future use, but\nthe runtime does not invoke it yet.\n\n### System prompt addition\n\nThe `assemble` method can return a `systemPromptAddition` string. OpenClaw\nprepends this to the system prompt for the run. This lets engines inject\ndynamic recall guidance, retrieval instructions, or context-aware hints\nwithout requiring static workspace files.\n\n## The legacy engine\n\nThe built-in `legacy` engine preserves OpenClaw's original behavior:\n\n* **Ingest**: no-op (the session manager handles message persistence directly).\n* **Assemble**: pass-through (the existing sanitize \u2192 validate \u2192 limit pipeline\n  in the runtime handles context assembly).\n* **Compact**: delegates to the built-in summarization compaction, which creates\n  a single summary of older messages and keeps recent messages intact.\n* **After turn**: no-op.\n\nThe legacy engine does not register tools or provide a `systemPromptAddition`.\n\nWhen no `plugins.slots.contextEngine` is set (or it's set to `&quot;legacy&quot;`), this\nengine is used automatically.\n\n## Plugin engines\n\nA plugin can register a context engine using the plugin API:\n\n```ts  theme={&quot;theme&quot;:{&quot;light&quot;:&quot;min-light&quot;,&quot;dark&quot;:&quot;min-dark&quot;}}\nexport default function register(api) {\n  api.registerContextEngine(&quot;my-engine&quot;, () =&gt; ({\n    info: {\n      id: &quot;my-engine&quot;,\n      name: &quot;My Context Engine&quot;,\n      ownsCompaction: true,\n    },\n\n    async ingest({ sessionId, message, isHeartbeat }) {\n      \/\/ Store the message in your data store\n      return { ingested: true };\n    },\n\n    async assemble({ sessionId, messages, tokenBudget }) {\n      \/\/ Return messages that fit the budget\n      return {\n        messages: buildContext(messages, tokenBudget),\n        estimatedTokens: countTokens(messages),\n        systemPromptAddition: &quot;Use lcm_grep to search history...&quot;,\n      };\n    },\n\n    async compact({ sessionId, force }) {\n      \/\/ Summarize older context\n      return { ok: true, compacted: true };\n    },\n  }));\n}\n<\/code><\/pre>\n<p>Then enable it in config:<\/p>\n<p>&#8220;`json5  theme={&#8220;theme&#8221;:{&#8220;light&#8221;:&#8221;min-light&#8221;,&#8221;dark&#8221;:&#8221;min-dark&#8221;}}<br \/>\n{<br \/>\n  plugins: {<br \/>\n    slots: {<br \/>\n      contextEngine: &#8220;my-engine&#8221;,<br \/>\n    },<br \/>\n    entries: {<br \/>\n      &#8220;my-engine&#8221;: {<br \/>\n        enabled: true,<br \/>\n      },<br \/>\n    },<br \/>\n  },<br \/>\n}<\/p>\n<pre><code>\n### The ContextEngine interface\n\nRequired members:\n\n| Member             | Kind     | Purpose                                                  |\n| ------------------ | -------- | -------------------------------------------------------- |\n| `info`             | Property | Engine id, name, version, and whether it owns compaction |\n| `ingest(params)`   | Method   | Store a single message                                   |\n| `assemble(params)` | Method   | Build context for a model run (returns `AssembleResult`) |\n| `compact(params)`  | Method   | Summarize\/reduce context                                 |\n\n`assemble` returns an `AssembleResult` with:\n\n* `messages` \u2014 the ordered messages to send to the model.\n* `estimatedTokens` (required, `number`) \u2014 the engine's estimate of total\n  tokens in the assembled context. OpenClaw uses this for compaction threshold\n  decisions and diagnostic reporting.\n* `systemPromptAddition` (optional, `string`) \u2014 prepended to the system prompt.\n\nOptional members:\n\n| Member                         | Kind   | Purpose                                                                                                         |\n| ------------------------------ | ------ | --------------------------------------------------------------------------------------------------------------- |\n| `bootstrap(params)`            | Method | Initialize engine state for a session. Called once when the engine first sees a session (e.g., import history). |\n| `ingestBatch(params)`          | Method | Ingest a completed turn as a batch. Called after a run completes, with all messages from that turn at once.     |\n| `afterTurn(params)`            | Method | Post-run lifecycle work (persist state, trigger background compaction).                                         |\n| `prepareSubagentSpawn(params)` | Method | Set up shared state for a child session.                                                                        |\n| `onSubagentEnded(params)`      | Method | Clean up after a subagent ends.                                                                                 |\n| `dispose()`                    | Method | Release resources. Called during gateway shutdown or plugin reload \u2014 not per-session.                           |\n\n### ownsCompaction\n\n`ownsCompaction` controls whether Pi's built-in in-attempt auto-compaction stays\nenabled for the run:\n\n* `true` \u2014 the engine owns compaction behavior. OpenClaw disables Pi's built-in\n  auto-compaction for that run, and the engine's `compact()` implementation is\n  responsible for `\/compact`, overflow recovery compaction, and any proactive\n  compaction it wants to do in `afterTurn()`.\n* `false` or unset \u2014 Pi's built-in auto-compaction may still run during prompt\n  execution, but the active engine's `compact()` method is still called for\n  `\/compact` and overflow recovery.\n\n`ownsCompaction: false` does **not** mean OpenClaw automatically falls back to\nthe legacy engine's compaction path.\n\nThat means there are two valid plugin patterns:\n\n* **Owning mode** \u2014 implement your own compaction algorithm and set\n  `ownsCompaction: true`.\n* **Delegating mode** \u2014 set `ownsCompaction: false` and have `compact()` call\n  `delegateCompactionToRuntime(...)` from `openclaw\/plugin-sdk\/core` to use\n  OpenClaw's built-in compaction behavior.\n\nA no-op `compact()` is unsafe for an active non-owning engine because it\ndisables the normal `\/compact` and overflow-recovery compaction path for that\nengine slot.\n\n## Configuration reference\n\n```json5  theme={&quot;theme&quot;:{&quot;light&quot;:&quot;min-light&quot;,&quot;dark&quot;:&quot;min-dark&quot;}}\n{\n  plugins: {\n    slots: {\n      \/\/ Select the active context engine. Default: &quot;legacy&quot;.\n      \/\/ Set to a plugin id to use a plugin engine.\n      contextEngine: &quot;legacy&quot;,\n    },\n  },\n}\n<\/code><\/pre>\n<p>The slot is exclusive at run time \u2014 only one registered context engine is<br \/>\nresolved for a given run or compaction operation. Other enabled<br \/>\n<code>kind: \"context-engine\"<\/code> plugins can still load and run their registration<br \/>\ncode; <code>plugins.slots.contextEngine<\/code> only selects which registered engine id<br \/>\nOpenClaw resolves when it needs a context engine.<\/p>\n<h2>Relationship to compaction and memory<\/h2>\n<ul>\n<li><strong>Compaction<\/strong> is one responsibility of the context engine. The legacy engine<br \/>\n  delegates to OpenClaw&#8217;s built-in summarization. Plugin engines can implement<br \/>\n  any compaction strategy (DAG summaries, vector retrieval, etc.).<\/li>\n<li><strong>Memory plugins<\/strong> (<code>plugins.slots.memory<\/code>) are separate from context engines.<br \/>\n  Memory plugins provide search\/retrieval; context engines control what the<br \/>\n  model sees. They can work together \u2014 a context engine might use memory<br \/>\n  plugin data during assembly.<\/li>\n<li><strong>Session pruning<\/strong> (trimming old tool results in-memory) still runs<br \/>\n  regardless of which context engine is active.<\/li>\n<\/ul>\n<h2>Tips<\/h2>\n<ul>\n<li>Use <code>openclaw doctor<\/code> to verify your engine is loading correctly.<\/li>\n<li>If switching engines, existing sessions continue with their current history.<br \/>\n  The new engine takes over for future runs.<\/li>\n<li>Engine errors are logged and surfaced in diagnostics. If a plugin engine<br \/>\n  fails to register or the selected engine id cannot be resolved, OpenClaw<br \/>\n  does not fall back automatically; runs fail until you fix the plugin or<br \/>\n  switch <code>plugins.slots.contextEngine<\/code> back to <code>\"legacy\"<\/code>.<\/li>\n<li>For development, use <code>openclaw plugins install -l .\/my-engine<\/code> to link a<br \/>\n  local plugin directory without copying.<\/li>\n<\/ul>\n<p>See also: <a href=\"\/concepts\/compaction\">Compaction<\/a>, <a href=\"\/concepts\/context\">Context<\/a>,<br \/>\n<a href=\"\/tools\/plugin\">Plugins<\/a>, <a href=\"\/plugins\/manifest\">Plugin manifest<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Context Engine A context engine controls how OpenClaw b [&hellip;]<\/p>\n","protected":false},"author":0,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[],"class_list":["post-608","post","type-post","status-publish","format-standard","hentry","category-docs"],"_links":{"self":[{"href":"https:\/\/pa.yingzhi8.cn\/index.php\/wp-json\/wp\/v2\/posts\/608","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pa.yingzhi8.cn\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/pa.yingzhi8.cn\/index.php\/wp-json\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/pa.yingzhi8.cn\/index.php\/wp-json\/wp\/v2\/comments?post=608"}],"version-history":[{"count":2,"href":"https:\/\/pa.yingzhi8.cn\/index.php\/wp-json\/wp\/v2\/posts\/608\/revisions"}],"predecessor-version":[{"id":714,"href":"https:\/\/pa.yingzhi8.cn\/index.php\/wp-json\/wp\/v2\/posts\/608\/revisions\/714"}],"wp:attachment":[{"href":"https:\/\/pa.yingzhi8.cn\/index.php\/wp-json\/wp\/v2\/media?parent=608"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/pa.yingzhi8.cn\/index.php\/wp-json\/wp\/v2\/categories?post=608"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/pa.yingzhi8.cn\/index.php\/wp-json\/wp\/v2\/tags?post=608"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}