@mingxy/cerebro 1.18.10 → 1.18.12
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 +56 -56
- package/src/client.ts +0 -14
- package/src/config.ts +1 -1
- package/src/hooks.ts +4 -108
- package/web/assets/index-BGZ625Cl.js +164 -0
- package/web/assets/index-CmkWtIf5.css +1 -0
- package/web/index.html +2 -2
- package/web/assets/index-BASbdDDa.css +0 -1
- package/web/assets/index-DmWQKvrX.js +0 -164
package/package.json
CHANGED
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@mingxy/cerebro",
|
|
3
|
-
"version": "1.18.
|
|
4
|
-
"description": "Cerebro persistent memory plugin for OpenCode
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "src/index.ts",
|
|
7
|
-
"exports": {
|
|
8
|
-
".": {
|
|
9
|
-
"types": "./src/index.ts",
|
|
10
|
-
"default": "./src/index.ts"
|
|
11
|
-
},
|
|
12
|
-
"./tui": {
|
|
13
|
-
"types": "./src/tui.tsx",
|
|
14
|
-
"default": "./src/tui.tsx"
|
|
15
|
-
}
|
|
16
|
-
},
|
|
17
|
-
"oc-plugin": [
|
|
18
|
-
"server",
|
|
19
|
-
"tui"
|
|
20
|
-
],
|
|
21
|
-
"scripts": {
|
|
22
|
-
"build:web": "bash ../../scripts/build-plugin-web.sh",
|
|
23
|
-
"prepublishOnly": "npm run build:web && npx tsc --noEmit"
|
|
24
|
-
},
|
|
25
|
-
"keywords": [
|
|
26
|
-
"opencode",
|
|
27
|
-
"memory",
|
|
28
|
-
"ai-agent",
|
|
29
|
-
"persistent-memory",
|
|
30
|
-
"cerebro",
|
|
31
|
-
"clustering",
|
|
32
|
-
"project-isolation"
|
|
33
|
-
],
|
|
34
|
-
"author": "mingxy-cerebro",
|
|
35
|
-
"files": [
|
|
36
|
-
"src/",
|
|
37
|
-
"web/"
|
|
38
|
-
],
|
|
39
|
-
"license": "Apache-2.0",
|
|
40
|
-
"homepage": "https://github.com/mingxy-cerebro/cerebro-server",
|
|
41
|
-
"repository": {
|
|
42
|
-
"type": "git",
|
|
43
|
-
"url": "https://github.com/mingxy-cerebro/cerebro-server.git",
|
|
44
|
-
"directory": "plugins/opencode"
|
|
45
|
-
},
|
|
46
|
-
"dependencies": {
|
|
47
|
-
"@opencode-ai/plugin": "^1.0.162",
|
|
48
|
-
"@opentui/core": "^0.1.92",
|
|
49
|
-
"@opentui/solid": "^0.1.92",
|
|
50
|
-
"solid-js": "^1.9.10"
|
|
51
|
-
},
|
|
52
|
-
"devDependencies": {
|
|
53
|
-
"@types/node": "^25.5.0",
|
|
54
|
-
"typescript": "^5.7.0"
|
|
55
|
-
}
|
|
56
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@mingxy/cerebro",
|
|
3
|
+
"version": "1.18.12",
|
|
4
|
+
"description": "Cerebro persistent memory plugin for OpenCode \u2014 auto-recall, auto-capture, 9 memory tools with clustering, project-scoped memory isolation",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./src/index.ts",
|
|
10
|
+
"default": "./src/index.ts"
|
|
11
|
+
},
|
|
12
|
+
"./tui": {
|
|
13
|
+
"types": "./src/tui.tsx",
|
|
14
|
+
"default": "./src/tui.tsx"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"oc-plugin": [
|
|
18
|
+
"server",
|
|
19
|
+
"tui"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build:web": "bash ../../scripts/build-plugin-web.sh",
|
|
23
|
+
"prepublishOnly": "npm run build:web && npx tsc --noEmit"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"opencode",
|
|
27
|
+
"memory",
|
|
28
|
+
"ai-agent",
|
|
29
|
+
"persistent-memory",
|
|
30
|
+
"cerebro",
|
|
31
|
+
"clustering",
|
|
32
|
+
"project-isolation"
|
|
33
|
+
],
|
|
34
|
+
"author": "mingxy-cerebro",
|
|
35
|
+
"files": [
|
|
36
|
+
"src/",
|
|
37
|
+
"web/"
|
|
38
|
+
],
|
|
39
|
+
"license": "Apache-2.0",
|
|
40
|
+
"homepage": "https://github.com/mingxy-cerebro/cerebro-server",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "https://github.com/mingxy-cerebro/cerebro-server.git",
|
|
44
|
+
"directory": "plugins/opencode"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@opencode-ai/plugin": "^1.0.162",
|
|
48
|
+
"@opentui/core": "^0.1.92",
|
|
49
|
+
"@opentui/solid": "^0.1.92",
|
|
50
|
+
"solid-js": "^1.9.10"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/node": "^25.5.0",
|
|
54
|
+
"typescript": "^5.7.0"
|
|
55
|
+
}
|
|
56
|
+
}
|
package/src/client.ts
CHANGED
|
@@ -42,19 +42,6 @@ export interface ListResponse {
|
|
|
42
42
|
offset: number;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
export interface ClusterSummary {
|
|
46
|
-
cluster_id: string;
|
|
47
|
-
title: string;
|
|
48
|
-
summary: string;
|
|
49
|
-
member_count: number;
|
|
50
|
-
relevance_score: number;
|
|
51
|
-
key_memories: MemoryDto[];
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export interface ClusteredRecallResult {
|
|
55
|
-
cluster_summaries: ClusterSummary[];
|
|
56
|
-
standalone_memories: MemoryDto[];
|
|
57
|
-
}
|
|
58
45
|
|
|
59
46
|
export interface DiscardedItem {
|
|
60
47
|
memory_id: string;
|
|
@@ -72,7 +59,6 @@ export interface ShouldRecallResponse {
|
|
|
72
59
|
confidence?: number;
|
|
73
60
|
memories?: SearchResult[];
|
|
74
61
|
discarded?: DiscardedItem[];
|
|
75
|
-
clustered?: ClusteredRecallResult;
|
|
76
62
|
}
|
|
77
63
|
|
|
78
64
|
export interface PreferenceDto {
|
package/src/config.ts
CHANGED
package/src/hooks.ts
CHANGED
|
@@ -290,76 +290,6 @@ function buildContextBlock(
|
|
|
290
290
|
};
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
-
function buildClusteredContextBlock(
|
|
294
|
-
clustered: import("./client.js").ClusteredRecallResult,
|
|
295
|
-
budget: number,
|
|
296
|
-
maxContentLength: number = 500,
|
|
297
|
-
minItemChars: number = MIN_ITEM_CONTENT_CHARS,
|
|
298
|
-
): ContextBlockResult {
|
|
299
|
-
const sections: string[] = [];
|
|
300
|
-
const injectedIds: string[] = [];
|
|
301
|
-
|
|
302
|
-
if (clustered.cluster_summaries.length > 0) {
|
|
303
|
-
const totalClusterScore = clustered.cluster_summaries.reduce((sum, cs) => sum + cs.relevance_score, 0);
|
|
304
|
-
|
|
305
|
-
sections.push("## 📋 主题簇(聚合记忆)");
|
|
306
|
-
for (const cs of clustered.cluster_summaries) {
|
|
307
|
-
const scoreIndicator = cs.relevance_score >= 0.8 ? "★★★" : cs.relevance_score >= 0.6 ? "★★" : "★";
|
|
308
|
-
const clusterMaxLen = totalClusterScore > 0
|
|
309
|
-
? Math.min(maxContentLength, Math.max(minItemChars, Math.floor((cs.relevance_score / totalClusterScore) * budget)))
|
|
310
|
-
: Math.min(maxContentLength, Math.max(minItemChars, Math.floor(budget / clustered.cluster_summaries.length)));
|
|
311
|
-
sections.push(`\n### ${cs.title} (整合自${cs.member_count}条记忆) ${scoreIndicator}`);
|
|
312
|
-
sections.push(`> ${cs.summary}`);
|
|
313
|
-
if (cs.key_memories.length > 0) {
|
|
314
|
-
sections.push("**核心要点:**");
|
|
315
|
-
for (const mem of cs.key_memories) {
|
|
316
|
-
const idTag = mem.id ? ` [id:${mem.id}]` : "";
|
|
317
|
-
const relTag = mem.relations && mem.relations.length > 0
|
|
318
|
-
? ` [rel:${mem.relations.map((rel) => rel.target_id).join(",")}]`
|
|
319
|
-
: "";
|
|
320
|
-
const importanceBar = mem.importance >= 0.7 ? "●" : mem.importance >= 0.4 ? "◐" : "○";
|
|
321
|
-
const content = truncate(mem.content, clusterMaxLen);
|
|
322
|
-
sections.push(`- ${importanceBar}${idTag}${relTag} ${content}`);
|
|
323
|
-
if (mem.id) injectedIds.push(mem.id);
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
if (clustered.standalone_memories.length > 0) {
|
|
330
|
-
const standaloneBudget = clustered.cluster_summaries.length === 0
|
|
331
|
-
? budget
|
|
332
|
-
: Math.floor(budget * 0.3);
|
|
333
|
-
const standaloneMaxLen = Math.min(maxContentLength, Math.max(minItemChars, Math.floor(standaloneBudget / clustered.standalone_memories.length)));
|
|
334
|
-
|
|
335
|
-
sections.push("\n## 📌 补充信息");
|
|
336
|
-
for (const mem of clustered.standalone_memories) {
|
|
337
|
-
const idTag = mem.id ? ` [id:${mem.id}]` : "";
|
|
338
|
-
const relTag = mem.relations && mem.relations.length > 0
|
|
339
|
-
? ` [rel:${mem.relations.map((rel) => rel.target_id).join(",")}]`
|
|
340
|
-
: "";
|
|
341
|
-
const content = truncate(mem.content, standaloneMaxLen);
|
|
342
|
-
sections.push(`-${idTag}${relTag} ${content}`);
|
|
343
|
-
if (mem.id) injectedIds.push(mem.id);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
const totalInjected = clustered.cluster_summaries.reduce((s, cs) => s + cs.member_count, 0) + clustered.standalone_memories.length;
|
|
348
|
-
|
|
349
|
-
return {
|
|
350
|
-
text: sections.length > 0
|
|
351
|
-
? [
|
|
352
|
-
"<cerebro-context>",
|
|
353
|
-
"",
|
|
354
|
-
...sections,
|
|
355
|
-
"</cerebro-context>",
|
|
356
|
-
].join("\n")
|
|
357
|
-
: "",
|
|
358
|
-
injectedMemoryIds: injectedIds,
|
|
359
|
-
injectedCount: totalInjected,
|
|
360
|
-
};
|
|
361
|
-
}
|
|
362
|
-
|
|
363
293
|
export function autoRecallHook(client: CerebroClient, containerTags: string[], tui: any, config: Partial<OmemPluginConfig> = {}, getAgentName?: () => string, directory?: string) {
|
|
364
294
|
const similarityThreshold = config.recall?.similarityThreshold ?? 0.4;
|
|
365
295
|
const maxRecallResults = config.recall?.maxRecallResults ?? 10;
|
|
@@ -474,7 +404,7 @@ export function autoRecallHook(client: CerebroClient, containerTags: string[], t
|
|
|
474
404
|
showToast(tui, "🧠 Cerebro Service Unavailable", "Unable to reach memory API · check connection", "error", toastDelayMs);
|
|
475
405
|
return;
|
|
476
406
|
}
|
|
477
|
-
logDebug("autoRecallHook shouldRecall result", { shouldRecall: shouldRecallRes.should_recall, confidence: shouldRecallRes.confidence, memCount: shouldRecallRes.memories?.length ?? 0, discardedCount: shouldRecallRes.discarded?.length ?? 0
|
|
407
|
+
logDebug("autoRecallHook shouldRecall result", { shouldRecall: shouldRecallRes.should_recall, confidence: shouldRecallRes.confidence, memCount: shouldRecallRes.memories?.length ?? 0, discardedCount: shouldRecallRes.discarded?.length ?? 0 });
|
|
478
408
|
|
|
479
409
|
const storedMemoryIds = shouldRecallRes.memories?.map((r) => r.memory.id) ?? [];
|
|
480
410
|
const storedDiscardedIds = shouldRecallRes.discarded?.map((d) => d.memory_id) ?? [];
|
|
@@ -494,38 +424,7 @@ export function autoRecallHook(client: CerebroClient, containerTags: string[], t
|
|
|
494
424
|
},
|
|
495
425
|
): Promise<string | undefined> => {
|
|
496
426
|
try {
|
|
497
|
-
const
|
|
498
|
-
if (shouldRecallRes.memories) {
|
|
499
|
-
for (const r of shouldRecallRes.memories) {
|
|
500
|
-
memoryLookup.set(r.memory.id, r);
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
const items = clustered
|
|
504
|
-
? [
|
|
505
|
-
...clustered.cluster_summaries.flatMap((cs) =>
|
|
506
|
-
cs.key_memories.map((mem) => {
|
|
507
|
-
const sr = memoryLookup.get(mem.id ?? "");
|
|
508
|
-
return {
|
|
509
|
-
memory_id: mem.id ?? "",
|
|
510
|
-
score: cs.relevance_score,
|
|
511
|
-
refine_relevance: sr?.refine_relevance,
|
|
512
|
-
refine_reasoning: sr?.refine_reasoning,
|
|
513
|
-
is_kept: opts.injectedMemoryIds.includes(mem.id ?? ""),
|
|
514
|
-
};
|
|
515
|
-
})
|
|
516
|
-
),
|
|
517
|
-
...clustered.standalone_memories.map((mem) => {
|
|
518
|
-
const sr = memoryLookup.get(mem.id ?? "");
|
|
519
|
-
return {
|
|
520
|
-
memory_id: mem.id ?? "",
|
|
521
|
-
score: sr?.score ?? 0,
|
|
522
|
-
refine_relevance: sr?.refine_relevance,
|
|
523
|
-
refine_reasoning: sr?.refine_reasoning,
|
|
524
|
-
is_kept: opts.injectedMemoryIds.includes(mem.id ?? ""),
|
|
525
|
-
};
|
|
526
|
-
}),
|
|
527
|
-
]
|
|
528
|
-
: [
|
|
427
|
+
const items = [
|
|
529
428
|
...(shouldRecallRes.memories?.map((r) => ({
|
|
530
429
|
memory_id: r.memory.id,
|
|
531
430
|
score: r.score,
|
|
@@ -583,7 +482,6 @@ export function autoRecallHook(client: CerebroClient, containerTags: string[], t
|
|
|
583
482
|
}
|
|
584
483
|
|
|
585
484
|
const results = shouldRecallRes.memories ?? [];
|
|
586
|
-
const clustered = shouldRecallRes.clustered;
|
|
587
485
|
|
|
588
486
|
// --- Token Budget Calculation ---
|
|
589
487
|
const profileChars = profileBlock ? profileBlock.length : 0;
|
|
@@ -596,9 +494,7 @@ export function autoRecallHook(client: CerebroClient, containerTags: string[], t
|
|
|
596
494
|
configuredMax: maxContentLength,
|
|
597
495
|
});
|
|
598
496
|
|
|
599
|
-
const ctxResult =
|
|
600
|
-
? buildClusteredContextBlock(clustered, budgetRemaining, maxContentLength, MIN_ITEM_CONTENT_CHARS)
|
|
601
|
-
: buildContextBlock(results, budgetRemaining, maxContentLength, MIN_ITEM_CONTENT_CHARS);
|
|
497
|
+
const ctxResult = buildContextBlock(results, budgetRemaining, maxContentLength, MIN_ITEM_CONTENT_CHARS);
|
|
602
498
|
if (ctxResult.text) {
|
|
603
499
|
appendToSystem(output.system, ctxResult.text);
|
|
604
500
|
appendToSystem(output.system, FETCH_POLICY);
|
|
@@ -616,7 +512,7 @@ export function autoRecallHook(client: CerebroClient, containerTags: string[], t
|
|
|
616
512
|
logDebug("autoRecallHook profile injected after context", { sessionId: input.sessionID, outputSystemLength: output.system.length });
|
|
617
513
|
}
|
|
618
514
|
|
|
619
|
-
logDebug("autoRecallHook injection complete", {
|
|
515
|
+
logDebug("autoRecallHook injection complete", { sessionId: input.sessionID });
|
|
620
516
|
|
|
621
517
|
const didInjectProfile = !!profileBlock;
|
|
622
518
|
const didInjectContext = !!ctxResult.text;
|