cogmem 3.6.5 → 3.7.0

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.
@@ -1,8 +1,11 @@
1
+ import { MEMORY_ATLAS_PROJECTION_NAME } from '../store/MemoryAtlasStore.js';
1
2
  import { eventTextForMemory } from '../episode/CogmemBlockStripper.js';
3
+ import { FacetQueryPlanner } from './FacetQueryPlanner.js';
2
4
  import { compileAtlasQuery } from './MemoryAtlasQueryCompiler.js';
3
5
  export class MemoryAtlasService {
4
6
  store;
5
7
  eventStore;
8
+ facetPlanner = new FacetQueryPlanner();
6
9
  constructor(store, eventStore) {
7
10
  this.store = store;
8
11
  this.eventStore = eventStore;
@@ -14,13 +17,29 @@ export class MemoryAtlasService {
14
17
  }
15
18
  search(query, options) {
16
19
  const projectId = requiredProject(options.projectId);
17
- const nodes = this.store.search(boundedQuery(query), projectId, boundedLimit(options.limit));
20
+ const limit = boundedLimit(options.limit);
21
+ const facetResult = this.searchFacetCardsWithRelaxation(query, projectId, limit, options);
22
+ const cards = facetResult.cards;
23
+ let nodes = this.store.search(boundedQuery(query), projectId, limit);
24
+ if (cards.length) {
25
+ const cardNodes = cards.map((card) => this.store.getNode(card.canonicalId, projectId)).filter((node) => Boolean(node));
26
+ nodes = uniqueNodes([...cardNodes, ...nodes]).slice(0, limit);
27
+ }
18
28
  const withEvidence = this.attachEvidence(nodes, projectId, options);
19
- return slice(projectId, withEvidence, this.edgesFor(withEvidence, projectId), query);
29
+ const result = slice(projectId, withEvidence, this.edgesFor(withEvidence, projectId), query);
30
+ result.facets = facetsForPlan(facetResult.plan);
31
+ result.matchedFacets = cards.flatMap((card) => card.matchedFacets);
32
+ if (cards.length)
33
+ result.cards = this.attachCardEvidence(cards, projectId, options);
34
+ if (facetResult.relaxationTrace.length)
35
+ result.relaxationTrace = facetResult.relaxationTrace;
36
+ return result;
20
37
  }
21
38
  explore(query, options) {
22
39
  const projectId = requiredProject(options.projectId);
23
40
  const limit = boundedLimit(options.limit);
41
+ const facetResult = this.searchFacetCardsWithRelaxation(query, projectId, limit, options);
42
+ const cards = facetResult.cards;
24
43
  const compiled = compileAtlasQuery(boundedQuery(query), options.now);
25
44
  const target = this.store.resolveTargetNodeIds(projectId, compiled.text);
26
45
  let nodes = this.store.searchFaceted(query, projectId, limit, {
@@ -28,6 +47,10 @@ export class MemoryAtlasService {
28
47
  keywords: target.nodeIds.length ? compiled.keywords : compiled.tokens,
29
48
  targetNodeIds: target.nodeIds.length ? target.nodeIds : undefined,
30
49
  });
50
+ if (cards.length) {
51
+ const cardNodes = cards.map((card) => this.store.getNode(card.canonicalId, projectId)).filter((node) => Boolean(node));
52
+ nodes = uniqueNodes([...cardNodes, ...nodes]).slice(0, limit);
53
+ }
31
54
  if (compiled.actionIntent) {
32
55
  const actions = this.store.listActions(projectId, { target: compiled.target, targetEntityIds: target.entitySourceIds,
33
56
  from: compiled.range?.from, to: compiled.range?.to, limit });
@@ -36,7 +59,15 @@ export class MemoryAtlasService {
36
59
  const nodesWithEvidence = this.attachEvidence(nodes, projectId, options);
37
60
  const edges = this.edgesFor(nodesWithEvidence, projectId);
38
61
  const result = slice(projectId, nodesWithEvidence, edges, query);
39
- result.facets = { time: compiled.range, target: target.labels.join(', ') || compiled.target, memoryKinds: compiled.memoryKinds, keywords: compiled.keywords };
62
+ result.facets = {
63
+ ...facetsForPlan(facetResult.plan),
64
+ legacy: { time: compiled.range, target: target.labels.join(', ') || compiled.target, memoryKinds: compiled.memoryKinds, keywords: compiled.keywords },
65
+ };
66
+ result.matchedFacets = cards.flatMap((card) => card.matchedFacets);
67
+ if (cards.length)
68
+ result.cards = this.attachCardEvidence(cards, projectId, options);
69
+ if (facetResult.relaxationTrace.length)
70
+ result.relaxationTrace = facetResult.relaxationTrace;
40
71
  const hasFacet = Boolean(compiled.range || compiled.target || compiled.memoryKinds.length || compiled.tokens.length);
41
72
  result.coldMemoryResurrected = hasFacet && nodes.some((node) => node.activation <= 0.1);
42
73
  return result;
@@ -134,19 +165,22 @@ export class MemoryAtlasService {
134
165
  pathEdges.reverse();
135
166
  }
136
167
  const path = found ? pathIds.map((id) => this.store.getNode(id, projectId)).filter((node) => Boolean(node)) : [];
137
- return { version: 'memory_atlas.v1', projectId, from: start, to: target, path,
168
+ return { version: MEMORY_ATLAS_PROJECTION_NAME, projectId, from: start, to: target, path,
138
169
  edges: found ? this.safeEdges(pathEdges, projectId) : [], truncated: expanded.size >= 2000 };
139
170
  }
140
171
  timeline(query, options) {
141
172
  const projectId = requiredProject(options.projectId);
142
173
  const compiled = compileAtlasQuery(boundedQuery(query), options.now);
143
174
  const limit = boundedLimit(options.limit);
175
+ const facetResult = this.searchFacetCardsWithRelaxation(query, projectId, limit, options);
176
+ const cards = this.attachCardEvidence(facetResult.cards, projectId, options)
177
+ .sort((left, right) => dateKey(left) - dateKey(right) || left.displayTitle.localeCompare(right.displayTitle));
144
178
  const target = this.store.resolveTargetNodeIds(projectId, compiled.text);
145
179
  const nodes = this.store.searchFaceted(query, projectId, limit, {
146
180
  from: compiled.range?.from, to: compiled.range?.to, memoryKinds: compiled.memoryKinds,
147
181
  keywords: target.nodeIds.length ? compiled.keywords : compiled.tokens,
148
182
  targetNodeIds: target.nodeIds.length ? target.nodeIds : undefined,
149
- }).sort((left, right) => Number(right.occurredAt || 0) - Number(left.occurredAt || 0)).map((node) => {
183
+ }).sort((left, right) => Number(left.occurredAt || 0) - Number(right.occurredAt || 0)).map((node) => {
150
184
  const evidence = this.evidence(node.id, projectId, options.evidenceLimit, options.includeEvidence);
151
185
  const evidenceTotal = this.store.evidenceTotal(node.id, projectId);
152
186
  return { ...node, evidenceCount: evidenceTotal, evidenceTotal, evidenceReturned: evidence.length, evidence, neighbors: [] };
@@ -154,8 +188,28 @@ export class MemoryAtlasService {
154
188
  const actions = this.store.listActions(projectId, { target: compiled.target, targetEntityIds: target.entitySourceIds,
155
189
  from: compiled.range?.from, to: compiled.range?.to, limit: boundedLimit(options.limit) })
156
190
  .map((action) => ({ ...action, evidence: this.evidence(action.id, projectId, options.evidenceLimit, options.includeEvidence) }));
157
- return { version: 'memory_atlas.v1', projectId, query, range: compiled.range,
158
- temporalResurrection: Boolean(compiled.range && [...nodes, ...actions].length), nodes, actions, warnings: [] };
191
+ return { version: MEMORY_ATLAS_PROJECTION_NAME, projectId, query, range: compiled.range,
192
+ temporalResurrection: Boolean((compiled.range || facetResult.plan.temporalIntent) && [...nodes, ...actions, ...cards].length),
193
+ nodes, actions, cards,
194
+ groupedByIssue: groupCardsByIssue(cards),
195
+ relaxationTrace: facetResult.relaxationTrace.length ? facetResult.relaxationTrace : undefined,
196
+ facets: facetsForPlan(facetResult.plan),
197
+ matchedFacets: cards.flatMap((card) => card.matchedFacets),
198
+ warnings: [] };
199
+ }
200
+ searchFacetCardsWithRelaxation(query, projectId, limit, options) {
201
+ let plan = this.facetPlanner.plan(boundedQuery(query), { projectId, now: options.now });
202
+ const relaxationTrace = [];
203
+ let cards = plan.facets.length ? this.store.searchCanonicalEpisodeCards(projectId, plan, limit) : [];
204
+ if (!cards.length && plan.facets.length) {
205
+ const relaxed = relaxFacetPlan(plan);
206
+ if (relaxed) {
207
+ plan = relaxed.plan;
208
+ cards = this.store.searchCanonicalEpisodeCards(projectId, relaxed.plan, limit);
209
+ relaxationTrace.push(...relaxed.trace);
210
+ }
211
+ }
212
+ return { plan, cards, relaxationTrace };
159
213
  }
160
214
  attachEvidence(nodes, projectId, options) {
161
215
  return nodes.map((node) => {
@@ -164,6 +218,17 @@ export class MemoryAtlasService {
164
218
  return { ...node, evidenceCount: evidenceTotal, evidenceTotal, evidenceReturned: evidence.length, evidence };
165
219
  });
166
220
  }
221
+ attachCardEvidence(cards, projectId, options) {
222
+ return cards.map((card) => {
223
+ const evidence = this.evidence(card.canonicalId, projectId, options.evidenceLimit, options.includeEvidence);
224
+ return {
225
+ ...card,
226
+ sourceLocator: evidence[0]?.sourceLocator ?? card.sourceLocator,
227
+ evidenceTotal: Math.max(card.evidenceTotal, this.store.evidenceTotal(card.canonicalId, projectId)),
228
+ evidenceReturned: evidence.length,
229
+ };
230
+ });
231
+ }
167
232
  evidence(nodeId, projectId, requested, includeExcerpt) {
168
233
  const limit = Math.max(1, Math.min(requested ?? 2, 10));
169
234
  return this.store.evidenceIds(nodeId, projectId, limit).flatMap((eventId) => {
@@ -229,7 +294,61 @@ function chunked(values, size) {
229
294
  chunks.push(values.slice(index, index + size));
230
295
  return chunks;
231
296
  }
232
- function slice(projectId, nodes, edges, query) { return { version: 'memory_atlas.v1', projectId, query, nodes, edges, nextActions: nodes.slice(0, 5).map((node) => ({ label: `Inspect ${node.label}`, tool: 'cogmem_graph_node', args: { id: node.id, projectId } })), warnings: [] }; }
297
+ function slice(projectId, nodes, edges, query) { return { version: MEMORY_ATLAS_PROJECTION_NAME, projectId, query, nodes, edges, nextActions: nodes.slice(0, 5).map((node) => ({ label: `Inspect ${node.label}`, tool: 'cogmem_graph_node', args: { id: node.id, projectId } })), warnings: [] }; }
298
+ function facetsForPlan(plan) {
299
+ return {
300
+ planner: {
301
+ intent: plan.intent,
302
+ operator: plan.operator,
303
+ temporalIntent: plan.temporalIntent,
304
+ groupBy: plan.groupBy,
305
+ exactness: plan.exactness,
306
+ keywords: plan.keywords,
307
+ facets: plan.facets.map((facet) => ({
308
+ type: facet.type,
309
+ value: facet.value,
310
+ label: facet.label,
311
+ nodeId: facet.nodeId || `${facet.type}:${facet.value}`,
312
+ relation: facet.relation || 'MATCHES',
313
+ })),
314
+ },
315
+ };
316
+ }
317
+ function dateKey(card) {
318
+ if (card.localDate && /^\d{4}-\d{2}-\d{2}$/u.test(card.localDate)) {
319
+ return Date.parse(`${card.localDate}T00:00:00.000Z`);
320
+ }
321
+ return Number.POSITIVE_INFINITY;
322
+ }
323
+ function groupCardsByIssue(cards) {
324
+ const groups = new Map();
325
+ for (const card of cards) {
326
+ const key = card.issueType || 'unclassified';
327
+ groups.set(key, [...(groups.get(key) || []), card]);
328
+ }
329
+ return Array.from(groups.entries()).map(([issueType, group]) => ({ issueType, cards: group }));
330
+ }
331
+ function relaxFacetPlan(plan) {
332
+ const dayFacet = plan.facets.find((facet) => facet.type === 'time' && facet.granularity === 'day');
333
+ if (!dayFacet)
334
+ return null;
335
+ const monthValue = dayFacet.value.slice(0, 7);
336
+ return {
337
+ plan: {
338
+ ...plan,
339
+ exactness: 'relaxed',
340
+ facets: plan.facets.map((facet) => facet === dayFacet ? {
341
+ ...facet,
342
+ value: monthValue,
343
+ label: monthValue,
344
+ nodeId: facet.nodeId?.replace(dayFacet.value, monthValue),
345
+ relation: 'OCCURRED_IN',
346
+ granularity: 'month',
347
+ } : facet),
348
+ },
349
+ trace: [{ from: dayFacet.value, to: monthValue, reason: 'exact day facet had no canonical episode match; relaxed to parent month' }],
350
+ };
351
+ }
233
352
  function atlasSourceLocator(event, projectId) {
234
353
  const project = projectId || event.projectId;
235
354
  const projectArg = project ? ` --project ${cliArg(project)}` : '';
@@ -1,4 +1,4 @@
1
- export type MemoryAtlasNodeType = 'project' | 'topic' | 'entity' | 'cluster' | 'episode' | 'belief' | 'action' | 'time' | 'event';
1
+ export type MemoryAtlasNodeType = 'project' | 'topic' | 'issue' | 'entity' | 'session' | 'thread' | 'memoryKind' | 'actionKind' | 'cluster' | 'episode' | 'raw_event' | 'belief' | 'action' | 'time' | 'event' | 'decision' | 'correction';
2
2
  export interface MemoryAtlasEvidence {
3
3
  eventId: string;
4
4
  globalSeq?: number;
@@ -39,6 +39,48 @@ export interface MemoryAtlasNode {
39
39
  /** Bounded first-hop raw evidence locators for agent-facing graph search/explore responses. */
40
40
  evidence?: MemoryAtlasEvidence[];
41
41
  }
42
+ export interface MemoryAtlasMatchedFacet {
43
+ type: 'time' | 'topic' | 'issue' | 'entity' | 'session' | 'thread' | 'memoryKind' | 'actionKind';
44
+ value: string;
45
+ label: string;
46
+ nodeId: string;
47
+ relation: string;
48
+ }
49
+ export interface MemoryAtlasMatchedPath {
50
+ facet: MemoryAtlasMatchedFacet;
51
+ via: string[];
52
+ relation: string;
53
+ confidence: number;
54
+ }
55
+ export interface MemoryAtlasRelatedCard {
56
+ canonicalId: string;
57
+ displayTitle: string;
58
+ reason: string;
59
+ matchedFacets?: MemoryAtlasMatchedFacet[];
60
+ }
61
+ export interface MemoryAtlasCard {
62
+ canonicalId: string;
63
+ nodeType: MemoryAtlasNodeType;
64
+ displayTitle: string;
65
+ oneLineSummary?: string;
66
+ matchedFacets: MemoryAtlasMatchedFacet[];
67
+ matchedPaths: MemoryAtlasMatchedPath[];
68
+ parentTopics: string[];
69
+ issueType?: string;
70
+ eventKind?: string;
71
+ localDate?: string;
72
+ whyMatched: string;
73
+ relatedButNotSelected: MemoryAtlasRelatedCard[];
74
+ sourceLocator?: MemoryAtlasEvidence['sourceLocator'];
75
+ evidenceEventIds: string[];
76
+ evidenceTotal: number;
77
+ evidenceReturned: number;
78
+ }
79
+ export interface MemoryAtlasRelaxationStep {
80
+ from: string;
81
+ to: string;
82
+ reason: string;
83
+ }
42
84
  export interface MemoryAtlasEdge {
43
85
  source: string;
44
86
  relation: string;
@@ -52,7 +94,7 @@ export interface MemoryAtlasNextAction {
52
94
  args: Record<string, unknown>;
53
95
  }
54
96
  export interface MemoryAtlasSlice {
55
- version: 'memory_atlas.v1';
97
+ version: 'memory_atlas.v2';
56
98
  projectId: string;
57
99
  query?: string;
58
100
  nodes: MemoryAtlasNode[];
@@ -60,15 +102,29 @@ export interface MemoryAtlasSlice {
60
102
  nextActions: MemoryAtlasNextAction[];
61
103
  warnings: string[];
62
104
  facets?: {
63
- time?: {
64
- from: number;
65
- to: number;
66
- label: string;
105
+ planner?: {
106
+ intent: string;
107
+ operator: string;
108
+ temporalIntent?: string;
109
+ groupBy?: string;
110
+ exactness: string;
111
+ facets: MemoryAtlasMatchedFacet[];
112
+ keywords: string[];
113
+ };
114
+ legacy?: {
115
+ time?: {
116
+ from: number;
117
+ to: number;
118
+ label: string;
119
+ };
120
+ target?: string;
121
+ memoryKinds: string[];
122
+ keywords: string[];
67
123
  };
68
- target?: string;
69
- memoryKinds: string[];
70
- keywords: string[];
71
124
  };
125
+ matchedFacets?: MemoryAtlasMatchedFacet[];
126
+ cards?: MemoryAtlasCard[];
127
+ relaxationTrace?: MemoryAtlasRelaxationStep[];
72
128
  coldMemoryResurrected?: boolean;
73
129
  }
74
130
  export interface MemoryAtlasNodeDetail extends MemoryAtlasNode {
@@ -87,7 +143,7 @@ export interface MemoryAtlasAction {
87
143
  evidence: MemoryAtlasEvidence[];
88
144
  }
89
145
  export interface MemoryAtlasTimelineResult {
90
- version: 'memory_atlas.v1';
146
+ version: 'memory_atlas.v2';
91
147
  projectId: string;
92
148
  query: string;
93
149
  range?: {
@@ -98,10 +154,18 @@ export interface MemoryAtlasTimelineResult {
98
154
  temporalResurrection: boolean;
99
155
  nodes: MemoryAtlasNodeDetail[];
100
156
  actions: MemoryAtlasAction[];
157
+ cards?: MemoryAtlasCard[];
158
+ groupedByIssue?: Array<{
159
+ issueType: string;
160
+ cards: MemoryAtlasCard[];
161
+ }>;
162
+ relaxationTrace?: MemoryAtlasRelaxationStep[];
163
+ facets?: MemoryAtlasSlice['facets'];
164
+ matchedFacets?: MemoryAtlasMatchedFacet[];
101
165
  warnings: string[];
102
166
  }
103
167
  export interface MemoryAtlasPathResult {
104
- version: 'memory_atlas.v1';
168
+ version: 'memory_atlas.v2';
105
169
  projectId: string;
106
170
  from: string;
107
171
  to: string;
@@ -573,6 +573,9 @@ function runRecall(kernel, args) {
573
573
  decisionTrace: result.decisionTrace,
574
574
  strategyCapsule,
575
575
  narrative: result.narrative,
576
+ atlasCards: result.atlasCards,
577
+ relatedButNotSelected: result.relatedButNotSelected,
578
+ relaxationTrace: result.relaxationTrace,
576
579
  items: result.items,
577
580
  };
578
581
  }
@@ -881,7 +884,7 @@ function printHuman(command, payload) {
881
884
  return;
882
885
  }
883
886
  if (command === 'graph' || command.startsWith('graph-')) {
884
- console.log(`memoryAtlas: ${payload.version || 'memory_atlas.v1'} project=${payload.projectId || 'unknown'}`);
887
+ console.log(`memoryAtlas: ${payload.version || 'memory_atlas.v2'} project=${payload.projectId || 'unknown'}`);
885
888
  const rows = Array.isArray(payload.nodes) ? payload.nodes
886
889
  : Array.isArray(payload.path) ? payload.path
887
890
  : Array.isArray(payload.actions) ? payload.actions
package/dist/factory.js CHANGED
@@ -83,7 +83,7 @@ import { SqliteVecStore } from './store/SqliteVecStore.js';
83
83
  import { VectorStore } from './store/VectorStore.js';
84
84
  import { config } from './utils/Config.js';
85
85
  import { KernelRunningError, SnapshotExporter, SnapshotImporter, } from './snapshot/index.js';
86
- const CORE_VERSION = '3.6.5';
86
+ const CORE_VERSION = '3.7.0';
87
87
  const LATEST_SCHEMA_VERSION = 27;
88
88
  export class MemoryKernel {
89
89
  options;
@@ -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.0';
9
9
  function defaultPublicEntrypoint() {
10
10
  return join(resolve(dirname(fileURLToPath(import.meta.url)), '../..'), 'public.js');
11
11
  }
@@ -992,7 +992,7 @@ function audit(config, record) {
992
992
  const plugin = {
993
993
  id: PLUGIN_ID,
994
994
  name: 'CogMem Auto Memory',
995
- version: '0.6.3',
995
+ version: '${PLUGIN_VERSION}',
996
996
  register(api) {
997
997
  if (!api || typeof api.on !== 'function') {
998
998
  throw new Error('OpenClaw plugin API missing api.on');
@@ -1338,10 +1338,32 @@ async function recallPayload(input, config, kernel, memory, formatStrategyContex
1338
1338
  maxMemoryRatio: Number(config.contextMemoryMaxRatio || 0.25), strategy: strategyCapsule,
1339
1339
  candidates: result.items.map(contextCandidateFromRecallItem),
1340
1340
  });
1341
- const plannedResult = activationPlan
1341
+ let plannedResult = activationPlan
1342
1342
  ? { ...result, items: activationPlan.selected.map((candidate) => candidate.recallItem) }
1343
1343
  : result;
1344
+ let activationReceipt = activationPlan && activationPlan.receipt;
1345
+ if (activationPlan && Array.isArray(result.items)) {
1346
+ const selectedIds = new Set(plannedResult.items.map((item) => item && item.id).filter(Boolean));
1347
+ const strictFacetItem = (result.decisionTrace && result.decisionTrace.selectedLane === 'facet_graph_raw_ledger')
1348
+ ? result.items.find((item) => item && item.canonicalId && Array.isArray(item.matchedFacets) && item.matchedFacets.length > 0)
1349
+ : undefined;
1350
+ if (strictFacetItem && !selectedIds.has(strictFacetItem.id)) {
1351
+ plannedResult = { ...plannedResult, items: [strictFacetItem, ...plannedResult.items].slice(0, Number(config.limit || 3)) };
1352
+ activationReceipt = {
1353
+ ...(activationReceipt || {}),
1354
+ facetGraphRetained: true,
1355
+ retainedCanonicalId: strictFacetItem.canonicalId,
1356
+ matchedFacets: strictFacetItem.matchedFacets,
1357
+ reason: 'strict_facet_match_must_not_be_silently_dropped',
1358
+ };
1359
+ }
1360
+ }
1344
1361
  const anchorItem = plannedResult.items.find((item) => item && item.sourceAnchor && item.sourceAnchor.eventId);
1362
+ const selectedCanonicalIds = new Set(plannedResult.items.map((item) => item && item.canonicalId).filter(Boolean));
1363
+ const selectedEpisodeCards = Array.isArray(result.atlasCards)
1364
+ ? result.atlasCards.filter((card) => card && selectedCanonicalIds.has(card.canonicalId))
1365
+ : [];
1366
+ plannedResult = { ...plannedResult, atlasCards: selectedEpisodeCards };
1345
1367
  const recallContext = formatRecallContext(plannedResult, config);
1346
1368
  return {
1347
1369
  context: recallContext ? (strategyCapsule ? formatStrategyContext(strategyCapsule) + '\n\n' : '') + recallContext : '',
@@ -1349,7 +1371,9 @@ async function recallPayload(input, config, kernel, memory, formatStrategyContex
1349
1371
  recallMode: result.recallMode, fallbackUsed: result.fallbackUsed, intent: input.intent || 'memory_recall',
1350
1372
  anchorEventId: anchorItem && anchorItem.sourceAnchor && anchorItem.sourceAnchor.eventId,
1351
1373
  anchorText: anchorItem && anchorItem.text, queryPlan: result.queryPlan, decisionTrace: result.decisionTrace,
1352
- activationReceipt: activationPlan && activationPlan.receipt, strategyCapsule,
1374
+ atlasCards: result.atlasCards, selectedEpisodeCards,
1375
+ relatedButNotSelected: result.relatedButNotSelected, relaxationTrace: result.relaxationTrace,
1376
+ activationReceipt, strategyCapsule,
1353
1377
  strategyReplanned: replan.replanned, strategyReplanReason: replan.reason,
1354
1378
  recallLatencyMs: Date.now() - recallStartedAt,
1355
1379
  };
@@ -1394,16 +1418,27 @@ function stripCogmemRecallBlocks(text) {
1394
1418
 
1395
1419
  function formatAtlasContext(result, maxChars) {
1396
1420
  const nodes = Array.isArray(result && result.nodes) ? result.nodes : [];
1397
- if (!nodes.length) return '';
1421
+ const cards = Array.isArray(result && result.cards) ? result.cards : [];
1422
+ if (!nodes.length && !cards.length) return '';
1398
1423
  const edges = Array.isArray(result && result.edges) ? result.edges : [];
1399
1424
  const actions = Array.isArray(result && result.nextActions) ? result.nextActions : [];
1400
1425
  const nodeDetails = Array.isArray(result && result.nodeDetails) ? result.nodeDetails : [];
1401
1426
  const lines = [
1402
- '<COGMEM_MEMORY_ATLAS version="memory_atlas.v1" volatile="true" persistence="forbidden" evidence_authority="raw_event_ids_only">',
1427
+ '<COGMEM_MEMORY_ATLAS version="memory_atlas.v2" volatile="true" persistence="forbidden" evidence_authority="raw_event_ids_only">',
1403
1428
  'Bounded navigation map; use it to choose nodes and paths, not as durable evidence.',
1404
- 'Matched facets: ' + safeAtlasText(JSON.stringify(result.facets || {}), 1000),
1429
+ 'Matched facets: ' + safeAtlasText(JSON.stringify(result.facets && result.facets.planner || result.facets || {}), 1400),
1405
1430
  'Cold memory resurrected: ' + String(result.coldMemoryResurrected === true),
1406
1431
  '',
1432
+ 'Selected memory cards:',
1433
+ ...cards.slice(0, 8).map((card) => '- ' + formatAtlasCard(card)),
1434
+ ...(cards.length ? [
1435
+ '',
1436
+ 'Related but not selected:',
1437
+ ...cards.flatMap((card) => Array.isArray(card.relatedButNotSelected) ? card.relatedButNotSelected : [])
1438
+ .slice(0, 8)
1439
+ .map((card) => '- ' + safeAtlasText(card.displayTitle, 160) + ' [' + safeAtlasText(card.reason, 160) + ']'),
1440
+ '',
1441
+ ] : []),
1407
1442
  'Nodes:',
1408
1443
  ...nodes.slice(0, 30).map((node) => '- ' + JSON.stringify({
1409
1444
  id: safeAtlasText(node.id, 500), type: safeAtlasText(node.nodeType, 80),
@@ -1695,6 +1730,9 @@ function compactRecallItems(items, config) {
1695
1730
  projectId: item.projectId,
1696
1731
  sourceAnchor: item.sourceAnchor,
1697
1732
  whyMatched: item.whyMatched,
1733
+ canonicalId: item.canonicalId,
1734
+ displayTitle: item.displayTitle,
1735
+ matchedFacets: item.matchedFacets,
1698
1736
  }));
1699
1737
  }
1700
1738
 
@@ -1731,6 +1769,15 @@ function formatRecallContext(result, config) {
1731
1769
  if (result.decisionTrace) {
1732
1770
  lines.push('recallDecision=' + formatRecallDecision(result.decisionTrace));
1733
1771
  }
1772
+ if (Array.isArray(result.atlasCards) && result.atlasCards.length) {
1773
+ lines.push('selectedMemoryCards=' + result.atlasCards.slice(0, Number(config.memoryContextMaxItems || config.limit || 3)).map(formatAtlasCard).join(' | '));
1774
+ }
1775
+ if (Array.isArray(result.relaxationTrace) && result.relaxationTrace.length) {
1776
+ lines.push('relaxationTrace=' + result.relaxationTrace.map((step) => step.from + ' -> ' + step.to + ' (' + step.reason + ')').join(' | '));
1777
+ }
1778
+ if (Array.isArray(result.relatedButNotSelected) && result.relatedButNotSelected.length) {
1779
+ lines.push('relatedButNotSelected=' + result.relatedButNotSelected.slice(0, 4).map((item) => item.displayTitle + ' [' + item.reason + ']').join(' | '));
1780
+ }
1734
1781
  lines.push('');
1735
1782
  if (result.narrative && result.narrative.summary) {
1736
1783
  lines.push(result.narrative.summary);
@@ -1748,7 +1795,11 @@ function formatRecallContext(result, config) {
1748
1795
  + (item.sourceAnchor.sessionId ? '; session=' + item.sourceAnchor.sessionId : '')
1749
1796
  + (item.sourceAnchor.role ? '; role=' + item.sourceAnchor.role : '') : '';
1750
1797
  const why = item.whyMatched ? '; whyMatched=' + item.whyMatched : '';
1751
- lines.push(' sourceType=' + sourceType + '; confidence=' + confidence + '; canAnswerExactQuote=' + quote + anchor + why);
1798
+ const canonical = item.canonicalId ? '; canonicalId=' + item.canonicalId : '';
1799
+ const matchedFacets = Array.isArray(item.matchedFacets) && item.matchedFacets.length
1800
+ ? '; matchedFacets=' + item.matchedFacets.map((facet) => facet.type + ':' + facet.value).join(',')
1801
+ : '';
1802
+ lines.push(' sourceType=' + sourceType + '; confidence=' + confidence + '; canAnswerExactQuote=' + quote + canonical + matchedFacets + anchor + why);
1752
1803
  if (item.sourceContext && item.sourceContext.event) {
1753
1804
  const anchorEvent = item.sourceContext.event;
1754
1805
  const anchorFormatted = formatContextEvent(anchorEvent, Math.min(220, sourceWindowMaxChars));
@@ -1801,6 +1852,24 @@ function formatRecallDecision(trace) {
1801
1852
  + ',raw:' + Number(counts.rawLedger || 0);
1802
1853
  }
1803
1854
 
1855
+ function formatAtlasCard(card) {
1856
+ const facets = Array.isArray(card && card.matchedFacets)
1857
+ ? card.matchedFacets.map((facet) => facet.type + ':' + facet.value).join(',')
1858
+ : '';
1859
+ const paths = Array.isArray(card && card.matchedPaths)
1860
+ ? card.matchedPaths.map((path) => path.relation + '@' + (path.facet && path.facet.nodeId || 'facet')).slice(0, 6).join(',')
1861
+ : '';
1862
+ const locator = card && card.sourceLocator && card.sourceLocator.command ? '; sourceLocator=' + card.sourceLocator.command : '';
1863
+ const why = card && card.whyMatched ? '; whyMatched=' + truncateLineWithMeta(card.whyMatched, 180).text : '';
1864
+ return (card && card.canonicalId || 'episode:unknown')
1865
+ + '; title=' + truncateLineWithMeta(card && card.displayTitle, 120).text
1866
+ + '; summary=' + truncateLineWithMeta(card && card.oneLineSummary, 180).text
1867
+ + (facets ? '; matchedFacets=' + facets : '')
1868
+ + (paths ? '; matchedPaths=' + paths : '')
1869
+ + why
1870
+ + locator;
1871
+ }
1872
+
1804
1873
  function clampRecallContext(text, maxChars) {
1805
1874
  const closingTag = '</COGMEM_RECALL_CONTEXT>';
1806
1875
  if (text.length <= maxChars) return text;
@@ -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.0',
9
9
  }, {
10
10
  capabilities: {
11
11
  tools: {},
@@ -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.0";
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[];
@@ -56,5 +62,8 @@ export declare class MemoryAtlasStore {
56
62
  countDocuments(projectId?: string): number;
57
63
  private refreshFtsNode;
58
64
  private topicRelationEdges;
65
+ private episodeEdgesForFacet;
66
+ private episodeRows;
67
+ private cardFromEpisodeRow;
59
68
  }
60
69
  //# sourceMappingURL=MemoryAtlasStore.d.ts.map