Session 1: OpenClaw Hook/Event Lifecycle — When Can Context Be Injected?¶
Date: 2026-03-21 16:03 PST Focus: Understanding OpenClaw's hook and event lifecycle to determine when and how a Hippocampus sidecar could inject context into the conversation stream.
Key Finding: There Are TWO Viable Injection Points¶
After deep-diving into OpenClaw's docs (hooks.md, agent-loop.md, context.md, plugin.md, system-prompt.md), I've identified the entire injection surface. The news is good — there are real, documented ways to do what we need.
Injection Point 1: before_prompt_build Plugin Hook (⭐ PRIMARY)¶
This is the golden path. It's a plugin lifecycle hook that runs: - After session load (conversation history is available) - Before the prompt is submitted to the model - On every turn
What it can return:
api.on("before_prompt_build", (event, ctx) => {
return {
prependContext: "...", // Prepends to user prompt (per-turn dynamic content)
prependSystemContext: "...", // Prepends to system prompt (stable guidance)
appendSystemContext: "...", // Appends to system prompt
systemPrompt: "...", // Full system prompt override (nuclear option)
};
}, { priority: 10 });
Why this is perfect for Hippocampus:
- prependContext is explicitly designed for "per-turn dynamic text" — exactly what a relevance-triggered memory injection is
- It runs on every agent turn, so the sidecar can decide per-turn what to inject
- The hook has access to messages (conversation history), so it can read what was just said
- Priority ordering means we can control when our hook runs relative to other plugins
- It's a synchronous return — no async waiting for the model to start
Critical constraint: The hook runs in-process with the Gateway. The Hippocampus sidecar would need to either: 1. BE a plugin (run in-process), or 2. Communicate with a plugin that acts as a bridge (plugin calls sidecar via IPC/HTTP)
Injection Point 2: message:preprocessed Hook (SECONDARY — for indexing)¶
This hook fires for every inbound message after all media understanding completes but before the agent sees it:
// message:preprocessed context
{
body?: string, // Raw inbound body
bodyForAgent?: string, // Final enriched body after media/link understanding
transcript?: string, // Transcript when audio was present
channelId: string,
conversationId?: string,
messageId?: string,
}
Why this matters: This is how the sidecar observes the conversation for indexing — not for injection. Every message passes through here, fully enriched, before the agent loop starts.
Injection Point 3: message:sent Hook (SECONDARY — for indexing outbound)¶
// message:sent context
{
to: string,
content: string,
success: boolean,
channelId: string,
conversationId?: string,
}
Why this matters: Indexes the agent's responses too. The sidecar needs both sides of the conversation for accurate retrieval.
Architecture Implication: Plugin, Not Standalone Process¶
The original THINKING.md framing assumed a "sidecar process" watching conversations externally. The research shows that's the wrong architecture. Here's why:
- Hooks run in-process — there's no external event stream to tap into. The gateway doesn't broadcast events to external listeners.
before_prompt_buildis the only way to inject context before the model responds — and it requires being a registered plugin.- Message hooks (
message:received,message:sent,message:preprocessed) are also in-process — external processes can't listen to them.
Revised Architecture: OpenClaw Plugin with Optional External Index¶
┌─────────────────────────────────────────────┐
│ OpenClaw Gateway │
│ │
│ ┌─────────────────────────────────────┐ │
│ │ Hippocampus Plugin (in-process) │ │
│ │ │ │
│ │ message:preprocessed → Index │ │
│ │ message:sent → Index │ │
│ │ before_prompt_build → Retrieve │ │
│ │ │ │
│ │ ┌───────────────────────┐ │ │
│ │ │ SQLite FTS5 / Index │ │ │
│ │ │ (local file) │ │ │
│ │ └───────────────────────┘ │ │
│ └─────────────────────────────────────┘ │
│ │
│ User msg → preprocessed → agent loop → │
│ before_prompt_build (inject!) → model │
└─────────────────────────────────────────────┘
The "sidecar" concept becomes a plugin that:
1. Observes: Listens to message:preprocessed and message:sent to index all conversation turns
2. Retrieves: On before_prompt_build, analyzes the current message, queries the index, and returns relevant context via prependContext
3. Stores: Maintains a local SQLite database (or similar) for the index
Timing Analysis: Can Injection Happen Fast Enough?¶
The Good News¶
before_prompt_build runs BEFORE the model starts generating. This is synchronous prompt assembly. If the retrieval is fast (which SQLite FTS5 would be — sub-millisecond for local queries), the injection adds negligible latency.
The Sequence¶
1. User sends message
2. message:received fires (raw)
3. message:preprocessed fires (enriched) ← INDEX THIS
4. Session loaded, history available
5. before_prompt_build fires ← INJECT HERE
6. Prompt assembled with injected context
7. Model starts generating
Steps 2-6 happen in milliseconds. The bottleneck is step 7 (model inference), not our injection. Latency is a non-issue for the core flow.
The Interesting Edge Case¶
What about indexing the current message AND retrieving in the same turn? The message:preprocessed hook fires before before_prompt_build. So:
1. message:preprocessed → index the new message → done
2. before_prompt_build → query index (which now includes the just-indexed message) → return relevant context
This means the system can use the current message as a query against historical conversations. That's exactly the hippocampus pattern — "what do I remember that's relevant to what I'm hearing right now?"
Security Consideration: allowPromptInjection¶
OpenClaw has a per-plugin config: plugins.entries.<id>.hooks.allowPromptInjection. If set to false, before_prompt_build is blocked for that plugin.
This is a feature, not a bug — it means operators can disable Hippocampus injection without uninstalling the plugin. Good for debugging, A/B testing, or "quiet mode."
Open Questions for Next Session¶
- What conversation data is accessible locally? The hooks give us message content, but what about full session transcripts? Where does OpenClaw store them? Can the plugin access historical sessions, not just the current one?
- Memory interaction: OpenClaw already has
memory-coreandmemory-lancedbplugins occupying thememoryslot. Hippocampus is NOT a memory plugin — it's a context injection plugin. Different slot, different purpose. But there may be overlap to manage. - Plugin packaging: What's the minimal viable plugin structure? Can we prototype in
<workspace>/.openclaw/extensions/?
Session 1 Verdict¶
The concept is architecturally viable. OpenClaw's plugin hook system provides exactly the injection points needed:
- message:preprocessed for observation/indexing
- before_prompt_build for retrieval/injection
- Both are in-process, low-latency, and documented
Key pivot from original concept: Not a sidecar process — a plugin. This is actually better because:
- No IPC overhead
- Direct access to conversation stream
- Native integration with OpenClaw's lifecycle
- Distributable via openclaw plugins install
- Configurable via standard OpenClaw config
The compute-on-the-machine principle still holds — the plugin runs locally, the index is local SQLite, and the retrieval is pure code (no LLM needed for the core path).