@mingxy/cerebro 1.12.0 → 1.12.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mingxy/cerebro",
3
- "version": "1.12.0",
3
+ "version": "1.12.2",
4
4
  "description": "Cerebro persistent memory plugin for OpenCode — auto-recall, auto-capture, 9 memory tools with clustering",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
package/src/hooks.ts CHANGED
@@ -323,7 +323,7 @@ export function autoRecallHook(client: CerebroClient, containerTags: string[], t
323
323
  if (policy === "none") return;
324
324
 
325
325
  try {
326
- logDebug("autoRecallHook start", { sessionId: input.sessionID, agentId, policy });
326
+ logDebug("autoRecallHook start", { sessionId: input.sessionID, agentId, policy, similarityThreshold, maxRecallResults, fetchMultiplier, topkCapMultiplier, mmrJaccardThreshold, mmrPenaltyFactor, phase2Multiplier, llmMaxEval, refineStrategy, refineMediumChars });
327
327
  const messages = sessionMessages.get(input.sessionID) ?? [];
328
328
  const userMessages = messages.filter((m) => m.role === "user");
329
329
 
@@ -379,13 +379,13 @@ export function autoRecallHook(client: CerebroClient, containerTags: string[], t
379
379
  let profileCountText = "";
380
380
  let profileBlock = "";
381
381
  const lastInjected = profileInjectedSessions.get(input.sessionID);
382
- const ttlExpired = !lastInjected || (Date.now() - lastInjected > 5 * 60 * 1000);
382
+ const ttlExpired = !lastInjected || (Date.now() - lastInjected > 30 * 60 * 1000);
383
383
  const isFirstInjection = !lastInjected;
384
384
  if (profile && ttlExpired) {
385
385
  const prefs = ((profile as any)?.static_facts ?? [])
386
386
  .filter((sf: any) => {
387
387
  const t: string[] = sf.tags ?? [];
388
- return t.includes("preferences") || t.includes("preference_extract") || t.some((tag: string) => tag.includes("偏好"));
388
+ return t.includes("preferences");
389
389
  })
390
390
  .map((sf: any) => sf.l2_content ?? sf.content ?? "")
391
391
  .filter(Boolean);
@@ -633,6 +633,13 @@ export function compactingHook(client: CerebroClient, containerTags: string[], t
633
633
  output.context.push(block);
634
634
  output.context.push(FETCH_POLICY);
635
635
  }
636
+ // 将compacting搜索结果的ID写入injectedMemoryIds,避免后续autoRecall重复注入
637
+ if (input.sessionID && results.length > 0) {
638
+ const compactingIds = results.map((r) => r.memory.id);
639
+ const existing = injectedMemoryIds.get(input.sessionID) ?? new Set<string>();
640
+ injectedMemoryIds.set(input.sessionID, new Set([...existing, ...compactingIds]));
641
+ logDebug("compactingHook updated injectedMemoryIds", { sessionId: input.sessionID, addedCount: compactingIds.length, totalExisting: existing.size });
642
+ }
636
643
  } catch {
637
644
  }
638
645
 
@@ -652,7 +659,6 @@ export function compactingHook(client: CerebroClient, containerTags: string[], t
652
659
  if (input.sessionID) {
653
660
  sessionMessages.delete(input.sessionID);
654
661
  profileInjectedSessions.delete(input.sessionID);
655
- injectedMemoryIds.delete(input.sessionID);
656
662
  firstMessages.delete(input.sessionID);
657
663
  }
658
664
  return;
@@ -679,7 +685,6 @@ export function compactingHook(client: CerebroClient, containerTags: string[], t
679
685
  if (isAutoStoreEnabled && !isAutoStoreEnabled(input.sessionID)) {
680
686
  sessionMessages.delete(input.sessionID);
681
687
  profileInjectedSessions.delete(input.sessionID);
682
- injectedMemoryIds.delete(input.sessionID);
683
688
  firstMessages.delete(input.sessionID);
684
689
  } else {
685
690
  const messages = sessionMessages.get(input.sessionID)!;
@@ -708,8 +713,11 @@ export function compactingHook(client: CerebroClient, containerTags: string[], t
708
713
  // Cleanup tracked messages regardless of ingest result
709
714
  sessionMessages.delete(input.sessionID);
710
715
  profileInjectedSessions.delete(input.sessionID);
711
- injectedMemoryIds.delete(input.sessionID);
712
716
  firstMessages.delete(input.sessionID);
717
+ // Evict stale injectedMemoryIds if over size cap (200 sessions)
718
+ if (injectedMemoryIds.size > 200) {
719
+ injectedMemoryIds.clear();
720
+ }
713
721
  }
714
722
 
715
723
  // Phase 2: compact inserts "[restore checkpointed" user message — poll for that marker
package/src/tools.ts CHANGED
@@ -52,17 +52,17 @@ export function buildTools(client: CerebroClient, containerTags: string[], conte
52
52
  "Do NOT overuse 'private' for normal work notes — default 'global' is correct for most cases."
53
53
  ),
54
54
  category: tool.schema
55
- .string()
55
+ .enum(["cases", "preferences", "entities", "events", "profile", "patterns"])
56
56
  .optional()
57
57
  .describe(
58
- "MUST be one of (choose the BEST fit): " +
59
- "'cases' (default) = work records, bug fixes, architecture decisions, implementation notes, meeting conclusions; " +
60
- "'preferences' = user likes/dislikes, coding style preferences, tool choices (e.g. 'prefers Vim over VSCode'); " +
61
- "'entities' = projects, tools, people, concepts — defining what something IS (e.g. 'omem-server: Rust memory backend using LanceDB'); " +
58
+ "Memory category. MUST be one of these exact values (lowercase): " +
59
+ "'cases' (default) = work records, bug fixes, architecture decisions; " +
60
+ "'preferences' = user likes/dislikes, coding style, tool choices; " +
61
+ "'entities' = projects, tools, people, concepts; " +
62
62
  "'events' = time-bound milestones (deployments, releases, incidents); " +
63
63
  "'profile' = user identity traits (role, skills, team membership); " +
64
- "'patterns' = workflows, methodologies, best practices, recurring solutions. " +
65
- "When in doubt, use 'cases'."
64
+ "'patterns' = workflows, methodologies, best practices. " +
65
+ "When in doubt, omit this field (defaults to 'cases')."
66
66
  ),
67
67
  },
68
68
  async execute(args) {