cogmem 3.6.5 → 3.7.1

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.
Files changed (46) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/MEMORY_ATLAS.md +59 -12
  3. package/README.md +21 -13
  4. package/RELEASE_CHECKLIST.md +5 -4
  5. package/dist/agent/AgentMemoryBackend.d.ts +24 -3
  6. package/dist/agent/AgentMemoryBackend.js +285 -55
  7. package/dist/agent/AgentRecallQueryCompiler.d.ts +1 -1
  8. package/dist/agent/AgentRecallQueryCompiler.js +13 -1
  9. package/dist/agent/MemoryUsageReceipt.js +8 -10
  10. package/dist/agent/SessionWorkingState.js +3 -2
  11. package/dist/agent/UntrustedMemorySerializer.d.ts +2 -0
  12. package/dist/agent/UntrustedMemorySerializer.js +14 -0
  13. package/dist/atlas/ActionFrameExtractor.js +2 -0
  14. package/dist/atlas/EpisodeTitleGenerator.d.ts +31 -0
  15. package/dist/atlas/EpisodeTitleGenerator.js +191 -0
  16. package/dist/atlas/FacetQueryPlanner.d.ts +33 -0
  17. package/dist/atlas/FacetQueryPlanner.js +229 -0
  18. package/dist/atlas/GraphCurator.d.ts +30 -0
  19. package/dist/atlas/GraphCurator.js +501 -0
  20. package/dist/atlas/MemoryAtlasIndexer.d.ts +15 -0
  21. package/dist/atlas/MemoryAtlasIndexer.js +55 -6
  22. package/dist/atlas/MemoryAtlasQueryCompiler.js +10 -7
  23. package/dist/atlas/MemoryAtlasService.d.ts +4 -1
  24. package/dist/atlas/MemoryAtlasService.js +174 -11
  25. package/dist/atlas/MemoryAtlasTypes.d.ts +75 -11
  26. package/dist/bin/memory.js +18 -5
  27. package/dist/factory.d.ts +18 -0
  28. package/dist/factory.js +41 -2
  29. package/dist/host/openclaw/AutoMemoryPluginInstaller.js +135 -39
  30. package/dist/mcp/server.js +1 -1
  31. package/dist/recall/RecallExplanation.d.ts +1 -1
  32. package/dist/store/MemoryAtlasStore.d.ts +12 -1
  33. package/dist/store/MemoryAtlasStore.js +353 -15
  34. package/dist/utils/ActionKindRegistry.d.ts +10 -0
  35. package/dist/utils/ActionKindRegistry.js +18 -0
  36. package/dist/utils/EntityCueExtractor.d.ts +13 -0
  37. package/dist/utils/EntityCueExtractor.js +81 -0
  38. package/examples/hermes-backend/AGENTS.md +3 -3
  39. package/examples/hermes-backend/README.md +1 -1
  40. package/examples/hermes-backend/SKILL.md +14 -5
  41. package/examples/hermes-backend/references/operations.md +12 -9
  42. package/examples/openclaw-backend/AGENTS.md +3 -2
  43. package/examples/openclaw-backend/README.md +3 -2
  44. package/examples/openclaw-backend/SKILL.md +31 -9
  45. package/examples/openclaw-backend/references/operations.md +29 -11
  46. package/package.json +1 -1
@@ -5,7 +5,7 @@ import { basename, dirname, join, resolve } from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import { resolveCogmemConfigPath } from '../../config/CogmemConfig.js';
7
7
  const PLUGIN_ID = 'cogmem-auto-memory';
8
- const PLUGIN_VERSION = '0.6.3';
8
+ const PLUGIN_VERSION = '0.7.1';
9
9
  function defaultPublicEntrypoint() {
10
10
  return join(resolve(dirname(fileURLToPath(import.meta.url)), '../..'), 'public.js');
11
11
  }
@@ -516,15 +516,29 @@ function createMemoryUsageReceipt(input) {
516
516
 
517
517
  function formatSourceAnchor(anchor) {
518
518
  return [
519
- anchor.memoryId ? 'memory:' + anchor.memoryId : '',
520
- anchor.eventId ? 'event:' + anchor.eventId : '',
521
- anchor.sessionId ? 'session:' + anchor.sessionId : '',
522
- anchor.role ? 'role:' + anchor.role : '',
519
+ anchor.memoryId ? 'memory:' + serializeUntrustedMemory(anchor.memoryId, 160) : '',
520
+ anchor.eventId ? 'event:' + serializeUntrustedMemory(anchor.eventId, 160) : '',
521
+ anchor.sessionId ? 'session:' + serializeUntrustedMemory(anchor.sessionId, 160) : '',
522
+ anchor.role ? 'role:' + serializeUntrustedMemory(anchor.role, 80) : '',
523
523
  ].filter(Boolean).join('; ');
524
524
  }
525
525
 
526
526
  function listLines(values) {
527
- return values.length ? values.map((value) => '- ' + value) : ['- none'];
527
+ return values.length ? values.map((value) => '- ' + serializeUntrustedMemory(value, 260)) : ['- none'];
528
+ }
529
+
530
+ function serializeUntrustedMemory(input, limit) {
531
+ const clean = stripCogmemRecallBlocks(String(input || '')).text
532
+ .replace(new RegExp('</?COGMEM_[A-Z0-9_:-]+[^>]*>', 'gi'), '')
533
+ .replace(new RegExp('[\\\\u0000-\\\\u0008\\\\u000b\\\\u000c\\\\u000e-\\\\u001f\\\\u007f]', 'g'), ' ')
534
+ .replace(new RegExp('\\\\s+', 'g'), ' ')
535
+ .trim()
536
+ .replace(/&/g, '&amp;')
537
+ .replace(/</g, '&lt;')
538
+ .replace(/>/g, '&gt;')
539
+ .replace(/"/g, '&quot;')
540
+ .replace(/'/g, '&#39;');
541
+ return clean.slice(0, Math.max(0, Number(limit || 500)));
528
542
  }
529
543
 
530
544
  function clampBlock(text, closingTag, maxChars) {
@@ -535,14 +549,14 @@ function clampBlock(text, closingTag, maxChars) {
535
549
 
536
550
  function formatMemoryUsageBridge(receipt, maxChars) {
537
551
  const lines = [
538
- '<COGMEM_TURN_BRIDGE turn_id="' + String(receipt.turnId).replace(/"/g, '&quot;') + '" source="cogmem" compact="true" ttl_turns="' + receipt.ttlTurns + '" compile_allowed="false">',
552
+ '<COGMEM_TURN_BRIDGE turn_id="' + serializeUntrustedMemory(receipt.turnId, 180) + '" source="cogmem" compact="true" ttl_turns="' + receipt.ttlTurns + '" compile_allowed="false">',
539
553
  'Previous assistant answer used Cogmem memory.',
540
554
  '',
541
555
  'Used memory themes:',
542
556
  ...listLines(receipt.usedThemes || []),
543
557
  '',
544
558
  'Working conclusion produced in that turn:',
545
- '- ' + (receipt.workingConclusion || 'No compact conclusion recorded.'),
559
+ '- ' + serializeUntrustedMemory(receipt.workingConclusion || 'No compact conclusion recorded.', 260),
546
560
  '',
547
561
  'Source anchors:',
548
562
  ...listLines((receipt.sourceAnchors || []).map(formatSourceAnchor)),
@@ -608,7 +622,7 @@ function formatSessionWorkingState(state, maxChars) {
608
622
  const lines = [
609
623
  '<COGMEM_SESSION_STATE scope="current_session" compact="true" persistence="session_only" compile_allowed="false">',
610
624
  'Current working topic:',
611
- '- ' + (state.currentTopic || 'unspecified'),
625
+ '- ' + serializeUntrustedMemory(state.currentTopic || 'unspecified', 180),
612
626
  '',
613
627
  'Current design direction:',
614
628
  ...listLines(state.designDirection || []),
@@ -663,6 +677,9 @@ function classifyRecallIntent(query) {
663
677
  if (/原话|怎么说的|完整对话|上一句|下一句|exact quote|verbatim/.test(text)) {
664
678
  return 'forensic_quote';
665
679
  }
680
+ if (/(让你.{0,20}(对|给|把)?.{0,20}(做过|做了|启动|安装|配置|修改|重启|停止|操作|处理|执行)|对.{0,30}(做过什么|做了什么|哪些操作)|what did (i ask you to do|you do) to|operations? on)/i.test(text)) {
681
+ return 'action_history';
682
+ }
666
683
  if (/记得.{0,12}(聊过|讨论过|说过)|还记得|之前.{0,12}(聊过|讨论过|说过)|以前.{0,12}(聊过|讨论过|说过)|(过去|几个月前|半年前|上个月|前几天|昨天|上次|上个).{0,20}(聊过|讨论过|说过)|当时.{0,12}(聊|说|问)|那次.{0,12}(聊|说|问)|有没有.{0,12}(记录|聊过|讨论过)|have we discussed|did we talk about|previously discussed/.test(text)) {
667
684
  return 'historical_discussion';
668
685
  }
@@ -992,7 +1009,7 @@ function audit(config, record) {
992
1009
  const plugin = {
993
1010
  id: PLUGIN_ID,
994
1011
  name: 'CogMem Auto Memory',
995
- version: '0.6.3',
1012
+ version: '${PLUGIN_VERSION}',
996
1013
  register(api) {
997
1014
  if (!api || typeof api.on !== 'function') {
998
1015
  throw new Error('OpenClaw plugin API missing api.on');
@@ -1314,7 +1331,7 @@ async function recallPayload(input, config, kernel, memory, formatStrategyContex
1314
1331
  let result = await memory.recall({
1315
1332
  agentId: config.agentId || 'openclaw', projectId: config.projectId || 'openclaw',
1316
1333
  query: input.query || '', sessionId: input.sessionId, threadId: input.threadId,
1317
- excludeSessionId: input.excludeSessionId, intent: input.intent || 'memory_recall',
1334
+ excludeSessionId: input.excludeSessionId, intent: input.intent || undefined,
1318
1335
  anchorEventId: input.anchorEventId, anchorText: input.anchorText,
1319
1336
  limit: Number(config.limit || 3), retrievalPolicy: strategyCapsule && strategyCapsule.retrievalPolicy,
1320
1337
  });
@@ -1327,7 +1344,7 @@ async function recallPayload(input, config, kernel, memory, formatStrategyContex
1327
1344
  result = await memory.recall({
1328
1345
  agentId: config.agentId || 'openclaw', projectId: config.projectId || 'openclaw',
1329
1346
  query: input.query || '', sessionId: input.sessionId, threadId: input.threadId,
1330
- excludeSessionId: input.excludeSessionId, intent: input.intent || 'memory_recall',
1347
+ excludeSessionId: input.excludeSessionId, intent: input.intent || undefined,
1331
1348
  anchorEventId: input.anchorEventId, anchorText: input.anchorText,
1332
1349
  limit: Number(config.limit || 3), retrievalPolicy: strategyCapsule && strategyCapsule.retrievalPolicy,
1333
1350
  });
@@ -1338,18 +1355,42 @@ async function recallPayload(input, config, kernel, memory, formatStrategyContex
1338
1355
  maxMemoryRatio: Number(config.contextMemoryMaxRatio || 0.25), strategy: strategyCapsule,
1339
1356
  candidates: result.items.map(contextCandidateFromRecallItem),
1340
1357
  });
1341
- const plannedResult = activationPlan
1358
+ let plannedResult = activationPlan
1342
1359
  ? { ...result, items: activationPlan.selected.map((candidate) => candidate.recallItem) }
1343
1360
  : result;
1361
+ let activationReceipt = activationPlan && activationPlan.receipt;
1362
+ if (activationPlan && Array.isArray(result.items)) {
1363
+ const selectedIds = new Set(plannedResult.items.map((item) => item && item.id).filter(Boolean));
1364
+ const strictFacetItem = (result.decisionTrace && result.decisionTrace.selectedLane === 'facet_graph_raw_ledger')
1365
+ ? result.items.find((item) => item && item.canonicalId && Array.isArray(item.matchedFacets) && item.matchedFacets.length > 0)
1366
+ : undefined;
1367
+ if (strictFacetItem && !selectedIds.has(strictFacetItem.id)) {
1368
+ plannedResult = { ...plannedResult, items: [strictFacetItem, ...plannedResult.items].slice(0, Number(config.limit || 3)) };
1369
+ activationReceipt = {
1370
+ ...(activationReceipt || {}),
1371
+ facetGraphRetained: true,
1372
+ retainedCanonicalId: strictFacetItem.canonicalId,
1373
+ matchedFacets: strictFacetItem.matchedFacets,
1374
+ reason: 'strict_facet_match_must_not_be_silently_dropped',
1375
+ };
1376
+ }
1377
+ }
1344
1378
  const anchorItem = plannedResult.items.find((item) => item && item.sourceAnchor && item.sourceAnchor.eventId);
1379
+ const selectedCanonicalIds = new Set(plannedResult.items.map((item) => item && item.canonicalId).filter(Boolean));
1380
+ const selectedEpisodeCards = Array.isArray(result.atlasCards)
1381
+ ? result.atlasCards.filter((card) => card && selectedCanonicalIds.has(card.canonicalId))
1382
+ : [];
1383
+ plannedResult = { ...plannedResult, atlasCards: selectedEpisodeCards };
1345
1384
  const recallContext = formatRecallContext(plannedResult, config);
1346
1385
  return {
1347
1386
  context: recallContext ? (strategyCapsule ? formatStrategyContext(strategyCapsule) + '\n\n' : '') + recallContext : '',
1348
1387
  items: compactRecallItems(plannedResult.items, config), itemCount: plannedResult.items.length,
1349
- recallMode: result.recallMode, fallbackUsed: result.fallbackUsed, intent: input.intent || 'memory_recall',
1388
+ recallMode: result.recallMode, fallbackUsed: result.fallbackUsed, intent: (result.queryPlan && result.queryPlan.intent) || input.intent || 'memory_recall',
1350
1389
  anchorEventId: anchorItem && anchorItem.sourceAnchor && anchorItem.sourceAnchor.eventId,
1351
1390
  anchorText: anchorItem && anchorItem.text, queryPlan: result.queryPlan, decisionTrace: result.decisionTrace,
1352
- activationReceipt: activationPlan && activationPlan.receipt, strategyCapsule,
1391
+ atlasCards: result.atlasCards, selectedEpisodeCards,
1392
+ relatedButNotSelected: result.relatedButNotSelected, relaxationTrace: result.relaxationTrace,
1393
+ activationReceipt, strategyCapsule,
1353
1394
  strategyReplanned: replan.replanned, strategyReplanReason: replan.reason,
1354
1395
  recallLatencyMs: Date.now() - recallStartedAt,
1355
1396
  };
@@ -1394,16 +1435,27 @@ function stripCogmemRecallBlocks(text) {
1394
1435
 
1395
1436
  function formatAtlasContext(result, maxChars) {
1396
1437
  const nodes = Array.isArray(result && result.nodes) ? result.nodes : [];
1397
- if (!nodes.length) return '';
1438
+ const cards = Array.isArray(result && result.cards) ? result.cards : [];
1439
+ if (!nodes.length && !cards.length) return '';
1398
1440
  const edges = Array.isArray(result && result.edges) ? result.edges : [];
1399
1441
  const actions = Array.isArray(result && result.nextActions) ? result.nextActions : [];
1400
1442
  const nodeDetails = Array.isArray(result && result.nodeDetails) ? result.nodeDetails : [];
1401
1443
  const lines = [
1402
- '<COGMEM_MEMORY_ATLAS version="memory_atlas.v1" volatile="true" persistence="forbidden" evidence_authority="raw_event_ids_only">',
1444
+ '<COGMEM_MEMORY_ATLAS version="memory_atlas.v2" volatile="true" persistence="forbidden" evidence_authority="raw_event_ids_only">',
1403
1445
  'Bounded navigation map; use it to choose nodes and paths, not as durable evidence.',
1404
- 'Matched facets: ' + safeAtlasText(JSON.stringify(result.facets || {}), 1000),
1446
+ 'Matched facets: ' + safeAtlasText(JSON.stringify(result.facets && result.facets.planner || result.facets || {}), 1400),
1405
1447
  'Cold memory resurrected: ' + String(result.coldMemoryResurrected === true),
1406
1448
  '',
1449
+ 'Selected memory cards:',
1450
+ ...cards.slice(0, 8).map((card) => '- ' + formatAtlasCard(card)),
1451
+ ...(cards.length ? [
1452
+ '',
1453
+ 'Related but not selected:',
1454
+ ...cards.flatMap((card) => Array.isArray(card.relatedButNotSelected) ? card.relatedButNotSelected : [])
1455
+ .slice(0, 8)
1456
+ .map((card) => '- ' + safeAtlasText(card.displayTitle, 160) + ' [' + safeAtlasText(card.reason, 160) + ']'),
1457
+ '',
1458
+ ] : []),
1407
1459
  'Nodes:',
1408
1460
  ...nodes.slice(0, 30).map((node) => '- ' + JSON.stringify({
1409
1461
  id: safeAtlasText(node.id, 500), type: safeAtlasText(node.nodeType, 80),
@@ -1435,10 +1487,20 @@ function formatAtlasContext(result, maxChars) {
1435
1487
  }
1436
1488
 
1437
1489
  function safeAtlasText(input, limit) {
1490
+ return serializeUntrustedMemory(input, limit);
1491
+ }
1492
+
1493
+ function serializeUntrustedMemory(input, limit) {
1438
1494
  const clean = stripCogmemRecallBlocks(String(input || '')).text
1439
- .replace(/[<>]/g, (value) => value === '<' ? '\\u003c' : '\\u003e')
1495
+ .replace(/<\/?COGMEM_[A-Z0-9_:-]+[^>]*>/gi, '')
1496
+ .replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, ' ')
1440
1497
  .replace(/\s+/g, ' ')
1441
- .trim();
1498
+ .trim()
1499
+ .replace(/&/g, '&amp;')
1500
+ .replace(/</g, '&lt;')
1501
+ .replace(/>/g, '&gt;')
1502
+ .replace(/"/g, '&quot;')
1503
+ .replace(/'/g, '&#39;');
1442
1504
  return clean.slice(0, Math.max(0, Number(limit || 500)));
1443
1505
  }
1444
1506
 
@@ -1695,6 +1757,9 @@ function compactRecallItems(items, config) {
1695
1757
  projectId: item.projectId,
1696
1758
  sourceAnchor: item.sourceAnchor,
1697
1759
  whyMatched: item.whyMatched,
1760
+ canonicalId: item.canonicalId,
1761
+ displayTitle: item.displayTitle,
1762
+ matchedFacets: item.matchedFacets,
1698
1763
  }));
1699
1764
  }
1700
1765
 
@@ -1731,34 +1796,47 @@ function formatRecallContext(result, config) {
1731
1796
  if (result.decisionTrace) {
1732
1797
  lines.push('recallDecision=' + formatRecallDecision(result.decisionTrace));
1733
1798
  }
1799
+ if (Array.isArray(result.atlasCards) && result.atlasCards.length) {
1800
+ lines.push('selectedMemoryCards=' + result.atlasCards.slice(0, Number(config.memoryContextMaxItems || config.limit || 3)).map(formatAtlasCard).join(' | '));
1801
+ }
1802
+ if (Array.isArray(result.relaxationTrace) && result.relaxationTrace.length) {
1803
+ lines.push('relaxationTrace=' + result.relaxationTrace.map((step) => serializeUntrustedMemory(step.from, 80) + ' -> ' + serializeUntrustedMemory(step.to, 80) + ' (' + serializeUntrustedMemory(step.reason, 160) + ')').join(' | '));
1804
+ }
1805
+ if (Array.isArray(result.relatedButNotSelected) && result.relatedButNotSelected.length) {
1806
+ lines.push('relatedButNotSelected=' + result.relatedButNotSelected.slice(0, 4).map((item) => serializeUntrustedMemory(item.displayTitle, 160) + ' [' + serializeUntrustedMemory(item.reason, 160) + ']').join(' | '));
1807
+ }
1734
1808
  lines.push('');
1735
1809
  if (result.narrative && result.narrative.summary) {
1736
- lines.push(result.narrative.summary);
1810
+ lines.push(serializeUntrustedMemory(result.narrative.summary, 800));
1737
1811
  }
1738
1812
  const maxItems = Number(config.memoryContextMaxItems || config.limit || 3);
1739
1813
  const sourceWindowMaxChars = Number(config.sourceWindowMaxChars || 1200);
1740
1814
  const includeSourceWindow = config.includeSourceWindowByDefault === true;
1741
1815
  for (const item of result.items.slice(0, maxItems)) {
1742
- const source = item.source ? ' [' + item.source + ']' : '';
1743
- lines.push('- ' + item.text + source);
1816
+ const source = item.source ? ' [' + serializeUntrustedMemory(item.source, 220) + ']' : '';
1817
+ lines.push('- ' + serializeUntrustedMemory(item.text, 500) + source);
1744
1818
  const sourceType = item.sourceType || 'compiled_memory';
1745
1819
  const quote = item.canAnswerExactQuote === true ? 'true' : 'false';
1746
1820
  const confidence = Number.isFinite(item.confidence) ? String(item.confidence) : 'unknown';
1747
- const anchor = item.sourceAnchor ? '; anchorEvent=' + (item.sourceAnchor.eventId || 'unknown')
1748
- + (item.sourceAnchor.sessionId ? '; session=' + item.sourceAnchor.sessionId : '')
1749
- + (item.sourceAnchor.role ? '; role=' + item.sourceAnchor.role : '') : '';
1750
- const why = item.whyMatched ? '; whyMatched=' + item.whyMatched : '';
1751
- lines.push(' sourceType=' + sourceType + '; confidence=' + confidence + '; canAnswerExactQuote=' + quote + anchor + why);
1821
+ const anchor = item.sourceAnchor ? '; anchorEvent=' + serializeUntrustedMemory(item.sourceAnchor.eventId || 'unknown', 160)
1822
+ + (item.sourceAnchor.sessionId ? '; session=' + serializeUntrustedMemory(item.sourceAnchor.sessionId, 160) : '')
1823
+ + (item.sourceAnchor.role ? '; role=' + serializeUntrustedMemory(item.sourceAnchor.role, 80) : '') : '';
1824
+ const why = item.whyMatched ? '; whyMatched=' + serializeUntrustedMemory(item.whyMatched, 180) : '';
1825
+ const canonical = item.canonicalId ? '; canonicalId=' + serializeUntrustedMemory(item.canonicalId, 180) : '';
1826
+ const matchedFacets = Array.isArray(item.matchedFacets) && item.matchedFacets.length
1827
+ ? '; matchedFacets=' + item.matchedFacets.map((facet) => serializeUntrustedMemory(facet.type, 80) + ':' + serializeUntrustedMemory(facet.value, 160)).join(',')
1828
+ : '';
1829
+ lines.push(' sourceType=' + serializeUntrustedMemory(sourceType, 120) + '; confidence=' + confidence + '; canAnswerExactQuote=' + quote + canonical + matchedFacets + anchor + why);
1752
1830
  if (item.sourceContext && item.sourceContext.event) {
1753
1831
  const anchorEvent = item.sourceContext.event;
1754
1832
  const anchorFormatted = formatContextEvent(anchorEvent, Math.min(220, sourceWindowMaxChars));
1755
1833
  lines.push(' sourceContext=' + anchorFormatted.line);
1756
1834
  lines.push(' sourceWindow=' + formatSourceWindow(item.sourceContext.window, item.sourceContext));
1757
1835
  if (item.sourceContext.locator && item.sourceContext.locator.command) {
1758
- lines.push(' sourceLocator=' + item.sourceContext.locator.command);
1836
+ lines.push(' sourceLocator=' + serializeUntrustedMemory(item.sourceContext.locator.command, 500));
1759
1837
  } else if (item.sourceContext.event.eventId) {
1760
1838
  const project = item.sourceContext.event.projectId ? ' --project ' + item.sourceContext.event.projectId : '';
1761
- lines.push(' sourceLocator=cogmem memory show --event ' + item.sourceContext.event.eventId + project + ' --before 2 --after 2 --json');
1839
+ lines.push(' sourceLocator=' + serializeUntrustedMemory('cogmem memory show --event ' + item.sourceContext.event.eventId + project + ' --before 2 --after 2 --json', 500));
1762
1840
  }
1763
1841
  const seenEventIds = new Set([anchorEvent.eventId].filter(Boolean));
1764
1842
  const before = uniqueWindowEvents(Array.isArray(item.sourceContext.before) ? item.sourceContext.before : [], seenEventIds).slice(-2);
@@ -1801,6 +1879,24 @@ function formatRecallDecision(trace) {
1801
1879
  + ',raw:' + Number(counts.rawLedger || 0);
1802
1880
  }
1803
1881
 
1882
+ function formatAtlasCard(card) {
1883
+ const facets = Array.isArray(card && card.matchedFacets)
1884
+ ? card.matchedFacets.map((facet) => serializeUntrustedMemory(facet.type, 80) + ':' + serializeUntrustedMemory(facet.value, 160)).join(',')
1885
+ : '';
1886
+ const paths = Array.isArray(card && card.matchedPaths)
1887
+ ? card.matchedPaths.map((path) => serializeUntrustedMemory(path.relation, 80) + '@' + serializeUntrustedMemory(path.facet && path.facet.nodeId || 'facet', 180)).slice(0, 6).join(',')
1888
+ : '';
1889
+ const locator = card && card.sourceLocator && card.sourceLocator.command ? '; sourceLocator=' + serializeUntrustedMemory(card.sourceLocator.command, 500) : '';
1890
+ const why = card && card.whyMatched ? '; whyMatched=' + serializeUntrustedMemory(card.whyMatched, 180) : '';
1891
+ return serializeUntrustedMemory(card && card.canonicalId || 'episode:unknown', 180)
1892
+ + '; title=' + serializeUntrustedMemory(card && card.displayTitle, 120)
1893
+ + '; summary=' + serializeUntrustedMemory(card && card.oneLineSummary, 180)
1894
+ + (facets ? '; matchedFacets=' + facets : '')
1895
+ + (paths ? '; matchedPaths=' + paths : '')
1896
+ + why
1897
+ + locator;
1898
+ }
1899
+
1804
1900
  function clampRecallContext(text, maxChars) {
1805
1901
  const closingTag = '</COGMEM_RECALL_CONTEXT>';
1806
1902
  if (text.length <= maxChars) return text;
@@ -1809,10 +1905,10 @@ function clampRecallContext(text, maxChars) {
1809
1905
  }
1810
1906
 
1811
1907
  function formatContextEvent(event, limit) {
1812
- const truncation = truncateLineWithMeta(event && event.text, limit);
1813
- const label = contextEventLabel(event);
1814
- const role = event && event.role ? event.role : 'unknown';
1815
- const eventId = event && event.eventId ? event.eventId : 'unknown';
1908
+ const truncation = truncateLineWithMeta(serializeUntrustedMemory(event && event.text, limit), limit);
1909
+ const label = serializeUntrustedMemory(contextEventLabel(event), 120);
1910
+ const role = serializeUntrustedMemory(event && event.role ? event.role : 'unknown', 80);
1911
+ const eventId = serializeUntrustedMemory(event && event.eventId ? event.eventId : 'unknown', 160);
1816
1912
  const charRange = event && event.charRange ? '; charRange=' + event.charRange.start + '-' + event.charRange.end : '';
1817
1913
  const sourceRange = formatSourceRange(event && event.sourceRange);
1818
1914
  const textLength = Number.isFinite(event && event.textLength) ? event.textLength : truncation.originalChars;
@@ -1834,17 +1930,17 @@ function formatSourceWindow(window, context) {
1834
1930
  const dropped = Array.isArray(window && window.droppedOverlapEventIds) ? window.droppedOverlapEventIds : [];
1835
1931
  return 'before=' + formatWindowSide(before)
1836
1932
  + '; after=' + formatWindowSide(after)
1837
- + '; overlap=' + (overlapEventIds.length ? overlapEventIds.join(',') : 'none')
1838
- + '; droppedOverlap=' + (dropped.length ? dropped.join(',') : 'none')
1839
- + '; overlapHandling=' + ((window && window.overlapHandling) || 'drop_from_after');
1933
+ + '; overlap=' + (overlapEventIds.length ? overlapEventIds.map((id) => serializeUntrustedMemory(id, 120)).join(',') : 'none')
1934
+ + '; droppedOverlap=' + (dropped.length ? dropped.map((id) => serializeUntrustedMemory(id, 120)).join(',') : 'none')
1935
+ + '; overlapHandling=' + serializeUntrustedMemory((window && window.overlapHandling) || 'drop_from_after', 120);
1840
1936
  }
1841
1937
 
1842
1938
  function formatWindowSide(side) {
1843
1939
  return 'requestedCount=' + Number(side.requestedCount || 0)
1844
1940
  + ', count=' + Number(side.count || 0)
1845
1941
  + ', excludesAnchor=' + (side.excludesAnchor !== false)
1846
- + ', ordering=' + (side.ordering || 'chronological')
1847
- + ', roleFilter=' + (side.roleFilter || 'all');
1942
+ + ', ordering=' + serializeUntrustedMemory(side.ordering || 'chronological', 80)
1943
+ + ', roleFilter=' + serializeUntrustedMemory(side.roleFilter || 'all', 80);
1848
1944
  }
1849
1945
 
1850
1946
  function uniqueWindowEvents(events, seenEventIds) {
@@ -5,7 +5,7 @@ import { callCogmemMcpTool, listCogmemMcpTools, } from './CoreMcpTools.js';
5
5
  export function createCogmemMcpServer(runtime = {}) {
6
6
  const server = new Server({
7
7
  name: 'cogmem-core',
8
- version: '3.6.5',
8
+ version: '3.7.1',
9
9
  }, {
10
10
  capabilities: {
11
11
  tools: {},
@@ -44,7 +44,7 @@ export interface RecallExplanation {
44
44
  projectId?: string;
45
45
  agentId?: string;
46
46
  collection?: string;
47
- recallMode: MemoryKernelNavigationResult['recallMode'] | 'raw_ledger_fallback';
47
+ recallMode: MemoryKernelNavigationResult['recallMode'] | 'raw_ledger_fallback' | 'atlas_facet_recall' | 'atlas_raw_grounded_recall';
48
48
  fallbackUsed: boolean;
49
49
  narrative?: NonNullable<MemoryKernelNavigationResult['navigation']>['narrative'];
50
50
  pulseTrace?: NonNullable<MemoryKernelNavigationResult['navigation']>['pulse']['trace'];
@@ -1,5 +1,8 @@
1
1
  import type Database from 'bun:sqlite';
2
- import type { MemoryAtlasAction, MemoryAtlasEdge, MemoryAtlasNode } from '../atlas/MemoryAtlasTypes.js';
2
+ import type { FacetQueryPlan } from '../atlas/FacetQueryPlanner.js';
3
+ import type { MemoryAtlasAction, MemoryAtlasCard, MemoryAtlasEdge, MemoryAtlasNode, MemoryAtlasRelatedCard } from '../atlas/MemoryAtlasTypes.js';
4
+ export declare const MEMORY_ATLAS_PROJECTION_NAME = "memory_atlas.v2";
5
+ export declare const MEMORY_ATLAS_PROJECTION_SCHEMA_VERSION = "3.7.2";
3
6
  export declare class MemoryAtlasStore {
4
7
  readonly db: Database;
5
8
  constructor(db: Database);
@@ -18,6 +21,9 @@ export declare class MemoryAtlasStore {
18
21
  keywords?: string[];
19
22
  targetNodeIds?: string[];
20
23
  }): MemoryAtlasNode[];
24
+ searchCanonicalEpisodeCards(projectId: string, plan: FacetQueryPlan, limit: number): MemoryAtlasCard[];
25
+ relatedEpisodeCards(projectId: string, canonicalId: string, selectedIds: Set<string>, limit: number): MemoryAtlasRelatedCard[];
26
+ private topicRelatedCardsForPlan;
21
27
  resolveTargetNodeIds(projectId: string, query: string): {
22
28
  nodeIds: string[];
23
29
  entitySourceIds: string[];
@@ -46,6 +52,8 @@ export declare class MemoryAtlasStore {
46
52
  decay(projectId?: string, factor?: number, now?: number): number;
47
53
  projectionNeedsRefresh(projectId: string): boolean;
48
54
  markProjectionClean(projectId: string, metadata?: Record<string, unknown>, now?: number): void;
55
+ markProjectionDirty(projectId: string, metadata?: Record<string, unknown>, now?: number): void;
56
+ aggregateFacetNodeSupport(projectId: string, now?: number): void;
49
57
  markProjectionFailed(projectId: string, error: string, now?: number): void;
50
58
  getProjectionState(projectId: string): {
51
59
  status: string;
@@ -56,5 +64,8 @@ export declare class MemoryAtlasStore {
56
64
  countDocuments(projectId?: string): number;
57
65
  private refreshFtsNode;
58
66
  private topicRelationEdges;
67
+ private episodeEdgesForFacet;
68
+ private episodeRows;
69
+ private cardFromEpisodeRow;
59
70
  }
60
71
  //# sourceMappingURL=MemoryAtlasStore.d.ts.map