Plugin maintainer reference
Context engines
How a plugin takes over session context orchestration through the
contextEngine slot, and what it must still delegate. Part of the Plugin
architecture internals guide.
Context engine plugins
Context engine plugins own session context orchestration for ingest, assembly,
and compaction. Register them from your plugin with
api.registerContextEngine(id, factory), then select the active engine with
plugins.slots.contextEngine.
Use this when your plugin needs to replace or extend the default context pipeline rather than just add memory search or hooks.
import { buildMemorySystemPromptAddition } from "openclaw/plugin-sdk/core"; export default function (api) { api.registerContextEngine("lossless-claw", (ctx) => ({ info: { id: "lossless-claw", name: "Lossless Claw", ownsCompaction: true, acceptedHostParams: ["sessionKey"], }, async ingest() { return { ingested: true }; }, async assemble({ messages, sessionKey, availableTools, citationsMode }) { return { messages, estimatedTokens: 0, systemPromptAddition: buildMemorySystemPromptAddition({ availableTools: availableTools ?? new Set(), citationsMode, agentSessionKey: sessionKey, }), }; }, async compact() { return { ok: true, compacted: false }; }, }));}The factory ctx exposes optional config, agentDir, and workspaceDir
values for construction-time initialization.
The host completes registered async memory prompt preparation before calling a
non-legacy engine's assemble(). buildMemorySystemPromptAddition(...) stays
synchronous and reads that immutable run snapshot while assemble() is active.
Pass the supplied tool and citation context through unchanged so the snapshot
cannot cross run boundaries.
assemble() may return contextProjection when the active harness has a
persistent backend thread. Omit it for legacy per-turn projection. Return
{ mode: "thread_bootstrap", epoch } when the assembled context should be
injected once into a backend thread and reused until the epoch changes. Change
the epoch after the engine's semantic context changes, such as after an
engine-owned compaction pass. Hosts may preserve tool-call metadata, input
shape, and redacted tool results in a thread-bootstrap projection so fresh
backend threads retain tool continuity without copying raw secret-bearing
payloads.
If your engine does not own the compaction algorithm, keep compact()
implemented and delegate it explicitly:
import { buildMemorySystemPromptAddition, delegateCompactionToRuntime,} from "openclaw/plugin-sdk/core"; export default function (api) { api.registerContextEngine("my-memory-engine", (ctx) => ({ info: { id: "my-memory-engine", name: "My Memory Engine", ownsCompaction: false, }, async ingest() { return { ingested: true }; }, async assemble({ messages, sessionKey, availableTools, citationsMode }) { return { messages, estimatedTokens: 0, systemPromptAddition: buildMemorySystemPromptAddition({ availableTools: availableTools ?? new Set(), citationsMode, agentSessionKey: sessionKey, }), }; }, async compact(params) { return await delegateCompactionToRuntime(params); }, }));}