@mingxy/cerebro 1.5.5 → 1.5.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/hooks.ts +44 -89
- package/src/index.ts +1 -1
package/package.json
CHANGED
package/src/hooks.ts
CHANGED
|
@@ -18,25 +18,6 @@ const firstMessages = new Map<string, string>();
|
|
|
18
18
|
const sessionMessages = new Map<string, Array<{ role: string; content: string }>>();
|
|
19
19
|
const profileInjectedSessions = new Set<string>();
|
|
20
20
|
|
|
21
|
-
function extractMemoryIds(result: unknown): string[] {
|
|
22
|
-
if (!result) return [];
|
|
23
|
-
if (Array.isArray(result)) {
|
|
24
|
-
return (result as Array<{ id?: string }>).map((m) => m.id).filter(Boolean) as string[];
|
|
25
|
-
}
|
|
26
|
-
if (typeof result === "object" && result !== null) {
|
|
27
|
-
const r = result as Record<string, unknown>;
|
|
28
|
-
if (Array.isArray(r.memories)) {
|
|
29
|
-
return (r.memories as Array<{ id?: string }>).map((m) => m.id).filter(Boolean) as string[];
|
|
30
|
-
}
|
|
31
|
-
if (Array.isArray(r.results)) {
|
|
32
|
-
return (r.results as Array<{ id?: string; memory?: { id?: string } }>)
|
|
33
|
-
.map((m) => m.id ?? m.memory?.id)
|
|
34
|
-
.filter(Boolean) as string[];
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
return [];
|
|
38
|
-
}
|
|
39
|
-
|
|
40
21
|
function formatRelativeAge(isoDate: string): string {
|
|
41
22
|
const diffMs = Date.now() - new Date(isoDate).getTime();
|
|
42
23
|
const minutes = Math.floor(diffMs / 60_000);
|
|
@@ -152,8 +133,6 @@ export function autoRecallHook(client: OmemClient, containerTags: string[], tui:
|
|
|
152
133
|
const query_text = userMessages[userMessages.length - 1]?.content || firstMessages.get(input.sessionID) || "";
|
|
153
134
|
const last_query_text = userMessages.length >= 2 ? userMessages[userMessages.length - 2].content : undefined;
|
|
154
135
|
|
|
155
|
-
if (!query_text.trim()) return;
|
|
156
|
-
|
|
157
136
|
const projectTags = containerTags.filter(t => t.startsWith("omem_project_"));
|
|
158
137
|
const shouldRecallRes = await client.shouldRecall(query_text, last_query_text, input.sessionID, similarityThreshold, maxRecallResults, projectTags.length > 0 ? projectTags : undefined);
|
|
159
138
|
|
|
@@ -280,7 +259,7 @@ export function autoRecallHook(client: OmemClient, containerTags: string[], tui:
|
|
|
280
259
|
};
|
|
281
260
|
}
|
|
282
261
|
|
|
283
|
-
export function keywordDetectionHook(
|
|
262
|
+
export function keywordDetectionHook(_client: OmemClient, _containerTags: string[], threshold: number, _tui: any, _ingestMode: "smart" | "raw" = "smart") {
|
|
284
263
|
return async (
|
|
285
264
|
input: { sessionID: string; messageID?: string },
|
|
286
265
|
output: { message: UserMessage; parts: Part[] },
|
|
@@ -309,38 +288,47 @@ export function keywordDetectionHook(client: OmemClient, containerTags: string[]
|
|
|
309
288
|
});
|
|
310
289
|
|
|
311
290
|
const messages = sessionMessages.get(input.sessionID)!;
|
|
291
|
+
// Ingest is now handled by sessionIdleHook (session.idle → sessionIngest API).
|
|
292
|
+
// This hook only collects messages and detects keywords for recall.
|
|
312
293
|
if (messages.length >= threshold) {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
}
|
|
336
|
-
showToast(tui, "🔴 Capture Record Failed", `Failed to save capture record · check API connection`, "error");
|
|
337
|
-
}
|
|
294
|
+
// Threshold reached — messages will be processed on next session.idle
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export function compactingHook(client: OmemClient, containerTags: string[], tui: any, ingestMode: "smart" | "raw" = "smart") {
|
|
300
|
+
return async (
|
|
301
|
+
input: { sessionID?: string },
|
|
302
|
+
output: { context: string[]; prompt?: string },
|
|
303
|
+
) => {
|
|
304
|
+
if (input.sessionID && sessionMessages.has(input.sessionID)) {
|
|
305
|
+
const messages = sessionMessages.get(input.sessionID)!;
|
|
306
|
+
if (messages.length > 0) {
|
|
307
|
+
try {
|
|
308
|
+
const result = await client.ingestMessages(messages, {
|
|
309
|
+
mode: ingestMode,
|
|
310
|
+
tags: [...containerTags, "auto-capture"],
|
|
311
|
+
sessionId: input.sessionID,
|
|
312
|
+
});
|
|
313
|
+
if (result === null) {
|
|
314
|
+
showToast(tui, "🔴 Archive Failed", "Session archive blocked · check spiritual realm status", "error");
|
|
315
|
+
} else {
|
|
316
|
+
showToast(tui, "📦 Session Archived", `${messages.length} residual dialogues archived · merged into the realm`, "success");
|
|
338
317
|
}
|
|
339
|
-
|
|
318
|
+
} catch {
|
|
319
|
+
showToast(tui, "🔴 Archive Failed", "Session archive blocked · spiritual pulse anomaly", "error");
|
|
340
320
|
}
|
|
341
|
-
|
|
342
|
-
|
|
321
|
+
sessionMessages.delete(input.sessionID);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
try {
|
|
326
|
+
const results = await client.searchMemories("*", 20, undefined, containerTags);
|
|
327
|
+
const block = buildContextBlock(results);
|
|
328
|
+
if (block) {
|
|
329
|
+
output.context.push(block);
|
|
343
330
|
}
|
|
331
|
+
} catch {
|
|
344
332
|
}
|
|
345
333
|
};
|
|
346
334
|
}
|
|
@@ -353,6 +341,7 @@ export function sessionIdleHook(
|
|
|
353
341
|
tui: any,
|
|
354
342
|
sdkClient: any,
|
|
355
343
|
_ingestMode: "smart" | "raw" = "smart",
|
|
344
|
+
getMainSessionId?: () => string | undefined,
|
|
356
345
|
) {
|
|
357
346
|
let idleTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
358
347
|
let isCapturing = false;
|
|
@@ -363,6 +352,11 @@ export function sessionIdleHook(
|
|
|
363
352
|
const sessionID = input.event.properties?.sessionID;
|
|
364
353
|
if (!sessionID) return;
|
|
365
354
|
|
|
355
|
+
if (getMainSessionId) {
|
|
356
|
+
const mainId = getMainSessionId();
|
|
357
|
+
if (mainId && sessionID !== mainId) return;
|
|
358
|
+
}
|
|
359
|
+
|
|
366
360
|
if (idleTimeout) clearTimeout(idleTimeout);
|
|
367
361
|
|
|
368
362
|
idleTimeout = setTimeout(async () => {
|
|
@@ -400,13 +394,11 @@ export function sessionIdleHook(
|
|
|
400
394
|
|
|
401
395
|
try {
|
|
402
396
|
await omemClient.sessionIngest(conversationMessages, sessionID);
|
|
403
|
-
// Only mark as processed after successful ingest to prevent message loss on server errors
|
|
404
397
|
for (const id of newMessageIds) {
|
|
405
398
|
processedMessageIds.add(id);
|
|
406
399
|
}
|
|
407
400
|
showToast(tui, "🧠 Memory Sealed", `${conversationMessages.length} dialogues captured · entrusted to the heavens for refinement`, "success");
|
|
408
401
|
} catch (err) {
|
|
409
|
-
// Do NOT mark as processed — messages will be retried on next idle event
|
|
410
402
|
showToast(tui, "🔴 Session Capture Failed", String(err).substring(0, 100), "error");
|
|
411
403
|
}
|
|
412
404
|
} catch (err) {
|
|
@@ -419,40 +411,3 @@ export function sessionIdleHook(
|
|
|
419
411
|
}, 10000);
|
|
420
412
|
};
|
|
421
413
|
}
|
|
422
|
-
|
|
423
|
-
export function compactingHook(client: OmemClient, containerTags: string[], tui: any, ingestMode: "smart" | "raw" = "smart") {
|
|
424
|
-
return async (
|
|
425
|
-
input: { sessionID?: string },
|
|
426
|
-
output: { context: string[]; prompt?: string },
|
|
427
|
-
) => {
|
|
428
|
-
if (input.sessionID && sessionMessages.has(input.sessionID)) {
|
|
429
|
-
const messages = sessionMessages.get(input.sessionID)!;
|
|
430
|
-
if (messages.length > 0) {
|
|
431
|
-
try {
|
|
432
|
-
const result = await client.ingestMessages(messages, {
|
|
433
|
-
mode: ingestMode,
|
|
434
|
-
tags: [...containerTags, "auto-capture"],
|
|
435
|
-
sessionId: input.sessionID,
|
|
436
|
-
});
|
|
437
|
-
if (result === null) {
|
|
438
|
-
showToast(tui, "🔴 Archive Failed", "Session archive blocked · check spiritual realm status", "error");
|
|
439
|
-
} else {
|
|
440
|
-
showToast(tui, "📦 Session Archived", `${messages.length} residual dialogues archived · merged into the realm`, "success");
|
|
441
|
-
}
|
|
442
|
-
} catch {
|
|
443
|
-
showToast(tui, "🔴 Archive Failed", "Session archive blocked · spiritual pulse anomaly", "error");
|
|
444
|
-
}
|
|
445
|
-
sessionMessages.delete(input.sessionID);
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
try {
|
|
450
|
-
const results = await client.searchMemories("*", 20, undefined, containerTags);
|
|
451
|
-
const block = buildContextBlock(results);
|
|
452
|
-
if (block) {
|
|
453
|
-
output.context.push(block);
|
|
454
|
-
}
|
|
455
|
-
} catch {
|
|
456
|
-
}
|
|
457
|
-
};
|
|
458
|
-
}
|
package/src/index.ts
CHANGED
|
@@ -96,7 +96,7 @@ const OmemPlugin: Plugin = async (input) => {
|
|
|
96
96
|
"chat.message": keywordDetectionHook(omemClient, containerTags, config.autoCaptureThreshold, tui, config.ingestMode),
|
|
97
97
|
"experimental.session.compacting": compactingHook(omemClient, containerTags, tui, config.ingestMode),
|
|
98
98
|
tool: buildTools(omemClient, containerTags, { agentId, getSessionId: () => currentSessionId }),
|
|
99
|
-
event: sessionIdleHook(omemClient, containerTags, tui, client, config.ingestMode),
|
|
99
|
+
event: sessionIdleHook(omemClient, containerTags, tui, client, config.ingestMode, () => currentSessionId),
|
|
100
100
|
"shell.env": async (_input: any, output: any) => {
|
|
101
101
|
if (directory) {
|
|
102
102
|
output.env.OMEM_PROJECT_DIR = directory;
|