From 200bab233ef34c2df554c4f15c44b0673e7831ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Boschi?= Date: Thu, 26 Mar 2026 16:09:25 +0100 Subject: [PATCH] feat(openclaw): add recallInjectionPosition config to preserve prompt cache (#710) * feat(openclaw): add recallInjectionPosition config to preserve prompt cache Add configurable injection position for recalled memories to avoid breaking prefix-based prompt caching (Anthropic/Google) when agents have large static system prompts. Options: 'prepend' (default, current behavior), 'append' (end of system prompt, preserves cache), 'user' (before user message). Closes #703 * docs(openclaw): document all plugin config flags Add missing config options to the OpenClaw docs: recallTopK, recallTypes, recallContextTurns, recallMaxQueryChars, recallPromptPreamble, recallInjectionPosition, recallRoles, retainEveryNTurns, retainOverlapTurns, and debug. --- .../docs/sdks/integrations/openclaw.md | 10 ++++++++++ hindsight-integrations/openclaw/src/index.ts | 16 +++++++++++++--- hindsight-integrations/openclaw/src/types.ts | 2 ++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/hindsight-docs/docs/sdks/integrations/openclaw.md b/hindsight-docs/docs/sdks/integrations/openclaw.md index 540b4912..3bb3eda3 100644 --- a/hindsight-docs/docs/sdks/integrations/openclaw.md +++ b/hindsight-docs/docs/sdks/integrations/openclaw.md @@ -102,6 +102,16 @@ Optional settings in `~/.openclaw/openclaw.json`: - `retainRoles` - Which message roles to retain (default: `["user", "assistant"]`). Options: `user`, `assistant`, `system`, `tool` - `recallBudget` - Recall effort: `"low"`, `"mid"`, or `"high"` (default: `"mid"`). Higher budgets use more retrieval strategies for better results. - `recallMaxTokens` - Max tokens for recall response (default: `1024`). Controls how much memory context is injected per turn. +- `recallTopK` - Max number of memories to inject per turn (default: unlimited). +- `recallTypes` - Memory types to recall (default: `["world", "experience"]`). Options: `world`, `experience`, `observation`. +- `recallContextTurns` - Number of prior user turns to include in the recall query (default: `1`). +- `recallMaxQueryChars` - Max characters for the composed recall query (default: `800`). +- `recallPromptPreamble` - Custom preamble text placed above recalled memories. Overrides the built-in guidance text. +- `recallInjectionPosition` - Where to inject recalled memories: `"prepend"` (default), `"append"`, or `"user"`. Use `"append"` to preserve prompt caching with large static system prompts. Use `"user"` to inject before the user message instead of in the system prompt. +- `recallRoles` - Which message roles to include when composing the contextual recall query (default: `["user", "assistant"]`). +- `retainEveryNTurns` - Retain every Nth turn (default: `1` = every turn). Values > 1 enable chunked retention. +- `retainOverlapTurns` - Extra prior turns included when chunked retention fires (default: `0`). +- `debug` - Enable debug logging (default: `false`). ### Memory Isolation diff --git a/hindsight-integrations/openclaw/src/index.ts b/hindsight-integrations/openclaw/src/index.ts index e7900443..6fa48ae8 100644 --- a/hindsight-integrations/openclaw/src/index.ts +++ b/hindsight-integrations/openclaw/src/index.ts @@ -726,6 +726,7 @@ function getPluginConfig(api: MoltbotPluginAPI): PluginConfig { typeof config.recallPromptPreamble === 'string' && config.recallPromptPreamble.trim().length > 0 ? config.recallPromptPreamble : DEFAULT_RECALL_PROMPT_PREAMBLE, + recallInjectionPosition: typeof config.recallInjectionPosition === 'string' && ['prepend', 'append', 'user'].includes(config.recallInjectionPosition) ? config.recallInjectionPosition as PluginConfig['recallInjectionPosition'] : undefined, debug: config.debug ?? false, }; } @@ -1141,9 +1142,18 @@ ${memoriesFormatted} debug(`[Hindsight] Auto-recall: Injecting ${results.length} memories from bank ${bankId}`); - // Inject recalled memories into system prompt space so they stay hidden from - // the end-user transcript/UI while still being available to the model. - return { prependSystemContext: contextMessage }; + // Inject recalled memories. Position is configurable to preserve prompt caching + // when agents have large static system prompts. + const position = pluginConfig.recallInjectionPosition || 'prepend'; + switch (position) { + case 'append': + return { appendSystemContext: contextMessage }; + case 'user': + return { prependContext: contextMessage }; + case 'prepend': + default: + return { prependSystemContext: contextMessage }; + } } catch (error) { if (error instanceof DOMException && error.name === 'TimeoutError') { console.warn(`[Hindsight] Auto-recall timed out after ${RECALL_TIMEOUT_MS}ms, skipping memory injection`); diff --git a/hindsight-integrations/openclaw/src/types.ts b/hindsight-integrations/openclaw/src/types.ts index 5ce8f7b3..06cb0e62 100644 --- a/hindsight-integrations/openclaw/src/types.ts +++ b/hindsight-integrations/openclaw/src/types.ts @@ -3,6 +3,7 @@ export interface PluginPromptHookResult { prependContext?: string; prependSystemContext?: string; + appendSystemContext?: string; } export interface MoltbotPluginAPI { @@ -71,6 +72,7 @@ export interface PluginConfig { recallContextTurns?: number; // Number of user turns to include in recall query context. Default: 1 (latest only) recallMaxQueryChars?: number; // Max chars for composed recall query. Default: 800 recallPromptPreamble?: string; // Prompt preamble placed above recalled memories. Default: built-in guidance text. + recallInjectionPosition?: 'prepend' | 'append' | 'user'; // Where to inject recalled memories. 'prepend' = start of system prompt (default), 'append' = end of system prompt (preserves prompt cache), 'user' = before user message. debug?: boolean; // Enable debug logging (default: false) }