Skip to content

Commit 3c7ee45

Browse files
committed
feat: context re-injection with periodic and keyword triggers
Previously, supermemory context was only injected on the first message of a session. This meant topic changes mid-session lost relevant memory context. Changes: - Add reinjectEveryN config (default 0 = first only) Set to e.g. 10 to re-inject context every 10 messages - Add recall keyword patterns (English + Japanese) 'recall', '思い出して', 'メモリ確認' etc. trigger immediate context re-injection on that message - Clean up session state on session.deleted - Track per-session message counts instead of boolean set Config example (supermemory.jsonc): { "reinjectEveryN": 10 }
1 parent db96f73 commit 3c7ee45

2 files changed

Lines changed: 46 additions & 4 deletions

File tree

src/config.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ interface SupermemoryConfig {
2323
filterPrompt?: string;
2424
keywordPatterns?: string[];
2525
compactionThreshold?: number;
26+
/** Re-inject context every N messages. 0 = first message only (default). */
27+
reinjectEveryN?: number;
28+
/** Keywords that trigger immediate context re-injection. */
29+
recallKeywordPatterns?: string[];
2630
}
2731

2832
const DEFAULT_KEYWORD_PATTERNS = [
@@ -57,6 +61,19 @@ const DEFAULT_KEYWORD_PATTERNS = [
5761
"ノートして",
5862
];
5963

64+
const DEFAULT_RECALL_KEYWORD_PATTERNS = [
65+
// English
66+
"recall",
67+
"what\\s+do\\s+you\\s+remember",
68+
"check\\s+memory",
69+
"search\\s+memory",
70+
// Japanese
71+
"思い出して",
72+
"記憶を?検索",
73+
"メモリ[ーを]?確認",
74+
"何か覚えてる",
75+
];
76+
6077
const DEFAULTS: Required<Omit<SupermemoryConfig, "apiKey" | "userContainerTag" | "projectContainerTag">> = {
6178
similarityThreshold: 0.6,
6279
maxMemories: 5,
@@ -67,6 +84,8 @@ const DEFAULTS: Required<Omit<SupermemoryConfig, "apiKey" | "userContainerTag" |
6784
filterPrompt: "You are a stateful coding agent. Remember all the information, including but not limited to user's coding preferences, tech stack, behaviours, workflows, and any other relevant details.",
6885
keywordPatterns: [],
6986
compactionThreshold: 0.80,
87+
reinjectEveryN: 0,
88+
recallKeywordPatterns: [],
7089
};
7190

7291
function isValidRegex(pattern: string): boolean {
@@ -127,6 +146,11 @@ export const CONFIG = {
127146
...(fileConfig.keywordPatterns ?? []).filter(isValidRegex),
128147
],
129148
compactionThreshold: validateCompactionThreshold(fileConfig.compactionThreshold),
149+
reinjectEveryN: fileConfig.reinjectEveryN ?? DEFAULTS.reinjectEveryN,
150+
recallKeywordPatterns: [
151+
...DEFAULT_RECALL_KEYWORD_PATTERNS,
152+
...(fileConfig.recallKeywordPatterns ?? []).filter(isValidRegex),
153+
],
130154
};
131155

132156
export function isConfigured(): boolean {

src/index.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const CODE_BLOCK_PATTERN = /```[\s\S]*?```/g;
1717
const INLINE_CODE_PATTERN = /`[^`]+`/g;
1818

1919
const MEMORY_KEYWORD_PATTERN = new RegExp(`\\b(${CONFIG.keywordPatterns.join("|")})\\b`, "i");
20+
const RECALL_KEYWORD_PATTERN = new RegExp(`\\b(${CONFIG.recallKeywordPatterns.join("|")})\\b`, "i");
2021

2122
const MEMORY_NUDGE_MESSAGE = `[MEMORY TRIGGER DETECTED]
2223
The user wants you to remember something. You MUST use the \`supermemory\` tool with \`mode: "add"\` to save this information.
@@ -37,10 +38,15 @@ function detectMemoryKeyword(text: string): boolean {
3738
return MEMORY_KEYWORD_PATTERN.test(textWithoutCode);
3839
}
3940

41+
function detectRecallKeyword(text: string): boolean {
42+
const textWithoutCode = removeCodeBlocks(text);
43+
return RECALL_KEYWORD_PATTERN.test(textWithoutCode);
44+
}
45+
4046
export const SupermemoryPlugin: Plugin = async (ctx: PluginInput) => {
4147
const { directory } = ctx;
4248
const tags = getTags(directory);
43-
const injectedSessions = new Set<string>();
49+
const sessionMessageCount = new Map<string, number>();
4450
log("Plugin init", { directory, tags, configured: isConfigured() });
4551

4652
if (!isConfigured()) {
@@ -171,10 +177,20 @@ export const SupermemoryPlugin: Plugin = async (ctx: PluginInput) => {
171177
output.parts.push(nudgePart);
172178
}
173179

174-
const isFirstMessage = !injectedSessions.has(input.sessionID);
180+
// Determine whether to inject context
181+
const count = (sessionMessageCount.get(input.sessionID) || 0) + 1;
182+
sessionMessageCount.set(input.sessionID, count);
183+
184+
const isFirstMessage = count === 1;
185+
const recallTriggered = detectRecallKeyword(userMessage);
186+
const periodicReinject = CONFIG.reinjectEveryN > 0 && count % CONFIG.reinjectEveryN === 0;
187+
const shouldInjectContext = isFirstMessage || recallTriggered || periodicReinject;
175188

176-
if (isFirstMessage) {
177-
injectedSessions.add(input.sessionID);
189+
if (shouldInjectContext) {
190+
log("chat.message: injecting context", {
191+
reason: isFirstMessage ? "first-message" : recallTriggered ? "recall-keyword" : "periodic",
192+
messageCount: count,
193+
});
178194

179195
const [profileResult, userMemoriesResult, projectMemoriesListResult] = await Promise.all([
180196
supermemoryClient.getProfile(tags.user, userMessage),
@@ -220,6 +236,7 @@ export const SupermemoryPlugin: Plugin = async (ctx: PluginInput) => {
220236
log("chat.message: context injected", {
221237
duration,
222238
contextLength: memoryContext.length,
239+
reason: isFirstMessage ? "first-message" : recallTriggered ? "recall-keyword" : "periodic",
223240
});
224241
}
225242
}
@@ -540,6 +557,7 @@ export const SupermemoryPlugin: Plugin = async (ctx: PluginInput) => {
540557
const sessionInfo = props?.info as { id?: string } | undefined;
541558
if (sessionInfo?.id) {
542559
await saveSessionSummary(sessionInfo.id);
560+
sessionMessageCount.delete(sessionInfo.id);
543561
}
544562
}
545563

0 commit comments

Comments
 (0)