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.
- package/CHANGELOG.md +24 -0
- package/MEMORY_ATLAS.md +59 -12
- package/README.md +21 -13
- package/RELEASE_CHECKLIST.md +5 -4
- package/dist/agent/AgentMemoryBackend.d.ts +24 -3
- package/dist/agent/AgentMemoryBackend.js +285 -55
- package/dist/agent/AgentRecallQueryCompiler.d.ts +1 -1
- package/dist/agent/AgentRecallQueryCompiler.js +13 -1
- package/dist/agent/MemoryUsageReceipt.js +8 -10
- package/dist/agent/SessionWorkingState.js +3 -2
- package/dist/agent/UntrustedMemorySerializer.d.ts +2 -0
- package/dist/agent/UntrustedMemorySerializer.js +14 -0
- package/dist/atlas/ActionFrameExtractor.js +2 -0
- package/dist/atlas/EpisodeTitleGenerator.d.ts +31 -0
- package/dist/atlas/EpisodeTitleGenerator.js +191 -0
- package/dist/atlas/FacetQueryPlanner.d.ts +33 -0
- package/dist/atlas/FacetQueryPlanner.js +229 -0
- package/dist/atlas/GraphCurator.d.ts +30 -0
- package/dist/atlas/GraphCurator.js +501 -0
- package/dist/atlas/MemoryAtlasIndexer.d.ts +15 -0
- package/dist/atlas/MemoryAtlasIndexer.js +55 -6
- package/dist/atlas/MemoryAtlasQueryCompiler.js +10 -7
- package/dist/atlas/MemoryAtlasService.d.ts +4 -1
- package/dist/atlas/MemoryAtlasService.js +174 -11
- package/dist/atlas/MemoryAtlasTypes.d.ts +75 -11
- package/dist/bin/memory.js +18 -5
- package/dist/factory.d.ts +18 -0
- package/dist/factory.js +41 -2
- package/dist/host/openclaw/AutoMemoryPluginInstaller.js +135 -39
- package/dist/mcp/server.js +1 -1
- package/dist/recall/RecallExplanation.d.ts +1 -1
- package/dist/store/MemoryAtlasStore.d.ts +12 -1
- package/dist/store/MemoryAtlasStore.js +353 -15
- package/dist/utils/ActionKindRegistry.d.ts +10 -0
- package/dist/utils/ActionKindRegistry.js +18 -0
- package/dist/utils/EntityCueExtractor.d.ts +13 -0
- package/dist/utils/EntityCueExtractor.js +81 -0
- package/examples/hermes-backend/AGENTS.md +3 -3
- package/examples/hermes-backend/README.md +1 -1
- package/examples/hermes-backend/SKILL.md +14 -5
- package/examples/hermes-backend/references/operations.md +12 -9
- package/examples/openclaw-backend/AGENTS.md +3 -2
- package/examples/openclaw-backend/README.md +3 -2
- package/examples/openclaw-backend/SKILL.md +31 -9
- package/examples/openclaw-backend/references/operations.md +29 -11
- package/package.json +1 -1
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { EventStore } from '../store/EventStore.js';
|
|
2
|
-
import type
|
|
2
|
+
import { type MemoryAtlasStore } from '../store/MemoryAtlasStore.js';
|
|
3
3
|
import type { MemoryAtlasNodeDetail, MemoryAtlasPathResult, MemoryAtlasQueryOptions, MemoryAtlasSlice, MemoryAtlasTimelineResult } from './MemoryAtlasTypes.js';
|
|
4
4
|
export declare class MemoryAtlasService {
|
|
5
5
|
private store;
|
|
6
6
|
private eventStore;
|
|
7
|
+
private readonly facetPlanner;
|
|
7
8
|
constructor(store: MemoryAtlasStore, eventStore: EventStore);
|
|
8
9
|
overview(options: MemoryAtlasQueryOptions): MemoryAtlasSlice;
|
|
9
10
|
search(query: string, options: MemoryAtlasQueryOptions): MemoryAtlasSlice;
|
|
@@ -16,7 +17,9 @@ export declare class MemoryAtlasService {
|
|
|
16
17
|
maxHops?: number;
|
|
17
18
|
}): MemoryAtlasPathResult;
|
|
18
19
|
timeline(query: string, options: MemoryAtlasQueryOptions): MemoryAtlasTimelineResult;
|
|
20
|
+
private searchFacetCardsWithRelaxation;
|
|
19
21
|
private attachEvidence;
|
|
22
|
+
private attachCardEvidence;
|
|
20
23
|
private evidence;
|
|
21
24
|
private edgesFor;
|
|
22
25
|
private safeEdges;
|
|
@@ -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
|
|
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
|
-
|
|
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 = {
|
|
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;
|
|
@@ -57,7 +88,8 @@ export class MemoryAtlasService {
|
|
|
57
88
|
if (!Number.isInteger(hops) || hops < 1 || hops > 2)
|
|
58
89
|
throw new Error('hops must be between 1 and 2');
|
|
59
90
|
const limit = boundedLimit(options.limit);
|
|
60
|
-
const
|
|
91
|
+
const start = canonicalInputNodeId(this.store, boundedId(nodeId), projectId);
|
|
92
|
+
const seen = new Set([start]);
|
|
61
93
|
let frontier = [...seen];
|
|
62
94
|
const selectedEdges = [];
|
|
63
95
|
for (let depth = 0; depth < hops; depth += 1) {
|
|
@@ -80,8 +112,8 @@ export class MemoryAtlasService {
|
|
|
80
112
|
path(from, to, options) {
|
|
81
113
|
const projectId = requiredProject(options.projectId);
|
|
82
114
|
const maxHops = Math.max(1, Math.min(options.maxHops ?? 6, 6));
|
|
83
|
-
const start = boundedId(from);
|
|
84
|
-
const target = boundedId(to);
|
|
115
|
+
const start = canonicalInputNodeId(this.store, boundedId(from), projectId);
|
|
116
|
+
const target = canonicalInputNodeId(this.store, boundedId(to), projectId);
|
|
85
117
|
const parents = new Map();
|
|
86
118
|
const best = new Map([[start, 0]]);
|
|
87
119
|
const queue = [{ id: start, cost: 0, hops: 0 }];
|
|
@@ -134,19 +166,22 @@ export class MemoryAtlasService {
|
|
|
134
166
|
pathEdges.reverse();
|
|
135
167
|
}
|
|
136
168
|
const path = found ? pathIds.map((id) => this.store.getNode(id, projectId)).filter((node) => Boolean(node)) : [];
|
|
137
|
-
return { version:
|
|
169
|
+
return { version: MEMORY_ATLAS_PROJECTION_NAME, projectId, from: start, to: target, path,
|
|
138
170
|
edges: found ? this.safeEdges(pathEdges, projectId) : [], truncated: expanded.size >= 2000 };
|
|
139
171
|
}
|
|
140
172
|
timeline(query, options) {
|
|
141
173
|
const projectId = requiredProject(options.projectId);
|
|
142
174
|
const compiled = compileAtlasQuery(boundedQuery(query), options.now);
|
|
143
175
|
const limit = boundedLimit(options.limit);
|
|
176
|
+
const facetResult = this.searchFacetCardsWithRelaxation(query, projectId, limit, options);
|
|
177
|
+
const cards = this.attachCardEvidence(facetResult.cards, projectId, options)
|
|
178
|
+
.sort((left, right) => dateKey(left) - dateKey(right) || left.displayTitle.localeCompare(right.displayTitle));
|
|
144
179
|
const target = this.store.resolveTargetNodeIds(projectId, compiled.text);
|
|
145
180
|
const nodes = this.store.searchFaceted(query, projectId, limit, {
|
|
146
181
|
from: compiled.range?.from, to: compiled.range?.to, memoryKinds: compiled.memoryKinds,
|
|
147
182
|
keywords: target.nodeIds.length ? compiled.keywords : compiled.tokens,
|
|
148
183
|
targetNodeIds: target.nodeIds.length ? target.nodeIds : undefined,
|
|
149
|
-
}).sort((left, right) => Number(
|
|
184
|
+
}).sort((left, right) => Number(left.occurredAt || 0) - Number(right.occurredAt || 0)).map((node) => {
|
|
150
185
|
const evidence = this.evidence(node.id, projectId, options.evidenceLimit, options.includeEvidence);
|
|
151
186
|
const evidenceTotal = this.store.evidenceTotal(node.id, projectId);
|
|
152
187
|
return { ...node, evidenceCount: evidenceTotal, evidenceTotal, evidenceReturned: evidence.length, evidence, neighbors: [] };
|
|
@@ -154,8 +189,28 @@ export class MemoryAtlasService {
|
|
|
154
189
|
const actions = this.store.listActions(projectId, { target: compiled.target, targetEntityIds: target.entitySourceIds,
|
|
155
190
|
from: compiled.range?.from, to: compiled.range?.to, limit: boundedLimit(options.limit) })
|
|
156
191
|
.map((action) => ({ ...action, evidence: this.evidence(action.id, projectId, options.evidenceLimit, options.includeEvidence) }));
|
|
157
|
-
return { version:
|
|
158
|
-
temporalResurrection: Boolean(compiled.range && [...nodes, ...actions].length),
|
|
192
|
+
return { version: MEMORY_ATLAS_PROJECTION_NAME, projectId, query, range: compiled.range,
|
|
193
|
+
temporalResurrection: Boolean((compiled.range || facetResult.plan.temporalIntent) && [...nodes, ...actions, ...cards].length),
|
|
194
|
+
nodes, actions, cards,
|
|
195
|
+
groupedByIssue: groupCardsByIssue(cards),
|
|
196
|
+
relaxationTrace: facetResult.relaxationTrace.length ? facetResult.relaxationTrace : undefined,
|
|
197
|
+
facets: facetsForPlan(facetResult.plan),
|
|
198
|
+
matchedFacets: cards.flatMap((card) => card.matchedFacets),
|
|
199
|
+
warnings: [] };
|
|
200
|
+
}
|
|
201
|
+
searchFacetCardsWithRelaxation(query, projectId, limit, options) {
|
|
202
|
+
let plan = this.facetPlanner.plan(boundedQuery(query), { projectId, now: options.now });
|
|
203
|
+
const relaxationTrace = [];
|
|
204
|
+
let cards = plan.facets.length ? this.store.searchCanonicalEpisodeCards(projectId, plan, limit) : [];
|
|
205
|
+
for (let attempt = 0; !cards.length && plan.facets.length && attempt < 3; attempt += 1) {
|
|
206
|
+
const relaxed = relaxFacetPlan(plan);
|
|
207
|
+
if (!relaxed)
|
|
208
|
+
break;
|
|
209
|
+
plan = relaxed.plan;
|
|
210
|
+
cards = this.store.searchCanonicalEpisodeCards(projectId, plan, limit);
|
|
211
|
+
relaxationTrace.push(...relaxed.trace);
|
|
212
|
+
}
|
|
213
|
+
return { plan, cards, relaxationTrace };
|
|
159
214
|
}
|
|
160
215
|
attachEvidence(nodes, projectId, options) {
|
|
161
216
|
return nodes.map((node) => {
|
|
@@ -164,6 +219,17 @@ export class MemoryAtlasService {
|
|
|
164
219
|
return { ...node, evidenceCount: evidenceTotal, evidenceTotal, evidenceReturned: evidence.length, evidence };
|
|
165
220
|
});
|
|
166
221
|
}
|
|
222
|
+
attachCardEvidence(cards, projectId, options) {
|
|
223
|
+
return cards.map((card) => {
|
|
224
|
+
const evidence = this.evidence(card.canonicalId, projectId, options.evidenceLimit, options.includeEvidence);
|
|
225
|
+
return {
|
|
226
|
+
...card,
|
|
227
|
+
sourceLocator: evidence[0]?.sourceLocator ?? card.sourceLocator,
|
|
228
|
+
evidenceTotal: Math.max(card.evidenceTotal, this.store.evidenceTotal(card.canonicalId, projectId)),
|
|
229
|
+
evidenceReturned: evidence.length,
|
|
230
|
+
};
|
|
231
|
+
});
|
|
232
|
+
}
|
|
167
233
|
evidence(nodeId, projectId, requested, includeExcerpt) {
|
|
168
234
|
const limit = Math.max(1, Math.min(requested ?? 2, 10));
|
|
169
235
|
return this.store.evidenceIds(nodeId, projectId, limit).flatMap((eventId) => {
|
|
@@ -203,6 +269,17 @@ export class MemoryAtlasService {
|
|
|
203
269
|
.flatMap((chunk) => this.store.findEdgesFromNodesToTarget(projectId, chunk, target)));
|
|
204
270
|
}
|
|
205
271
|
}
|
|
272
|
+
function canonicalInputNodeId(store, id, projectId) {
|
|
273
|
+
if (id.startsWith(`entity:${projectId}:`))
|
|
274
|
+
return id;
|
|
275
|
+
if (!id.startsWith('entity:'))
|
|
276
|
+
return id;
|
|
277
|
+
const entityId = id.slice('entity:'.length);
|
|
278
|
+
if (!entityId.startsWith('facet:'))
|
|
279
|
+
return id;
|
|
280
|
+
const scoped = `entity:${projectId}:${entityId}`;
|
|
281
|
+
return store.getNode(scoped, projectId) ? scoped : id;
|
|
282
|
+
}
|
|
206
283
|
function requiredProject(value) { if (!value?.trim())
|
|
207
284
|
throw new Error('projectId is required for Memory Atlas queries'); return value.trim(); }
|
|
208
285
|
function boundedLimit(value) { if (value !== undefined && (!Number.isFinite(value) || value < 1))
|
|
@@ -229,7 +306,93 @@ function chunked(values, size) {
|
|
|
229
306
|
chunks.push(values.slice(index, index + size));
|
|
230
307
|
return chunks;
|
|
231
308
|
}
|
|
232
|
-
function slice(projectId, nodes, edges, query) { return { version:
|
|
309
|
+
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: [] }; }
|
|
310
|
+
function facetsForPlan(plan) {
|
|
311
|
+
return {
|
|
312
|
+
planner: {
|
|
313
|
+
intent: plan.intent,
|
|
314
|
+
operator: plan.operator,
|
|
315
|
+
temporalIntent: plan.temporalIntent,
|
|
316
|
+
groupBy: plan.groupBy,
|
|
317
|
+
exactness: plan.exactness,
|
|
318
|
+
keywords: plan.keywords,
|
|
319
|
+
facets: plan.facets.map((facet) => ({
|
|
320
|
+
type: facet.type,
|
|
321
|
+
value: facet.value,
|
|
322
|
+
label: facet.label,
|
|
323
|
+
nodeId: facet.nodeId || `${facet.type}:${facet.value}`,
|
|
324
|
+
relation: facet.relation || 'MATCHES',
|
|
325
|
+
})),
|
|
326
|
+
},
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
function dateKey(card) {
|
|
330
|
+
if (card.localDate && /^\d{4}-\d{2}-\d{2}$/u.test(card.localDate)) {
|
|
331
|
+
return Date.parse(`${card.localDate}T00:00:00.000Z`);
|
|
332
|
+
}
|
|
333
|
+
return Number.POSITIVE_INFINITY;
|
|
334
|
+
}
|
|
335
|
+
function groupCardsByIssue(cards) {
|
|
336
|
+
const groups = new Map();
|
|
337
|
+
for (const card of cards) {
|
|
338
|
+
const key = card.issueType || 'unclassified';
|
|
339
|
+
groups.set(key, [...(groups.get(key) || []), card]);
|
|
340
|
+
}
|
|
341
|
+
return Array.from(groups.entries()).map(([issueType, group]) => ({ issueType, cards: group }));
|
|
342
|
+
}
|
|
343
|
+
function relaxFacetPlan(plan) {
|
|
344
|
+
const dayFacet = plan.facets.find((facet) => facet.type === 'time' && facet.granularity === 'day');
|
|
345
|
+
if (dayFacet) {
|
|
346
|
+
const monthValue = dayFacet.value.slice(0, 7);
|
|
347
|
+
return {
|
|
348
|
+
plan: replaceFacet(plan, dayFacet, {
|
|
349
|
+
...dayFacet,
|
|
350
|
+
value: monthValue,
|
|
351
|
+
label: monthValue,
|
|
352
|
+
nodeId: dayFacet.nodeId?.replace(dayFacet.value, monthValue),
|
|
353
|
+
relation: 'OCCURRED_IN',
|
|
354
|
+
granularity: 'month',
|
|
355
|
+
}),
|
|
356
|
+
trace: [{ from: dayFacet.value, to: monthValue, reason: 'exact day facet had no canonical episode match; relaxed to parent month' }],
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
const monthFacet = plan.facets.find((facet) => facet.type === 'time' && facet.granularity === 'month');
|
|
360
|
+
if (monthFacet) {
|
|
361
|
+
const yearValue = monthFacet.value.slice(0, 4);
|
|
362
|
+
return {
|
|
363
|
+
plan: replaceFacet(plan, monthFacet, {
|
|
364
|
+
...monthFacet,
|
|
365
|
+
value: yearValue,
|
|
366
|
+
label: yearValue,
|
|
367
|
+
nodeId: monthFacet.nodeId?.replace(monthFacet.value, yearValue),
|
|
368
|
+
relation: 'OCCURRED_IN',
|
|
369
|
+
granularity: 'year',
|
|
370
|
+
}),
|
|
371
|
+
trace: [{ from: monthFacet.value, to: yearValue, reason: 'exact month facet had no canonical episode match; relaxed to parent year' }],
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
const issueFacet = plan.facets.find((facet) => facet.type === 'issue');
|
|
375
|
+
const topicFacet = plan.facets.find((facet) => facet.type === 'topic');
|
|
376
|
+
if (issueFacet && topicFacet) {
|
|
377
|
+
return {
|
|
378
|
+
plan: {
|
|
379
|
+
...plan,
|
|
380
|
+
exactness: 'relaxed',
|
|
381
|
+
requiresIntersection: plan.facets.length - 1 > 1,
|
|
382
|
+
facets: plan.facets.filter((facet) => facet !== issueFacet),
|
|
383
|
+
},
|
|
384
|
+
trace: [{ from: issueFacet.value, to: topicFacet.value, reason: 'issue facet had no canonical episode match; relaxed to parent topic' }],
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
function replaceFacet(plan, from, to) {
|
|
390
|
+
return {
|
|
391
|
+
...plan,
|
|
392
|
+
exactness: 'relaxed',
|
|
393
|
+
facets: plan.facets.map((facet) => facet === from ? to : facet),
|
|
394
|
+
};
|
|
395
|
+
}
|
|
233
396
|
function atlasSourceLocator(event, projectId) {
|
|
234
397
|
const project = projectId || event.projectId;
|
|
235
398
|
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.
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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.
|
|
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.
|
|
168
|
+
version: 'memory_atlas.v2';
|
|
105
169
|
projectId: string;
|
|
106
170
|
from: string;
|
|
107
171
|
to: string;
|
package/dist/bin/memory.js
CHANGED
|
@@ -28,6 +28,7 @@ function readArgs(argv) {
|
|
|
28
28
|
command,
|
|
29
29
|
query: stringArg(values, 'query') || stringArg(values, 'q'),
|
|
30
30
|
eventId: stringArg(values, 'event') || stringArg(values, 'event-id'),
|
|
31
|
+
episodeId: stringArg(values, 'episode') || stringArg(values, 'episode-id'),
|
|
31
32
|
nodeId: stringArg(values, 'id') || stringArg(values, 'node-id'),
|
|
32
33
|
fromId: stringArg(values, 'from'),
|
|
33
34
|
toId: stringArg(values, 'to'),
|
|
@@ -96,6 +97,7 @@ function usage() {
|
|
|
96
97
|
' graph-neighbors expand --id by --hops 1..2',
|
|
97
98
|
' graph-path find a bounded path from --from to --to',
|
|
98
99
|
' graph-timeline reconstruct entity/time/action history for --query',
|
|
100
|
+
' graph-reindex reproject one Atlas episode/card by --event or --episode without rebuilding the whole graph',
|
|
99
101
|
'',
|
|
100
102
|
'Common options:',
|
|
101
103
|
' --project <id> scope to one project',
|
|
@@ -122,7 +124,7 @@ function usage() {
|
|
|
122
124
|
' --interval-ms <n> watch sleep interval, default 300000',
|
|
123
125
|
' --max-runs <n> stop watch after n iterations; omit for long-running worker',
|
|
124
126
|
' --agent <id> agent id for governed recall, default openclaw',
|
|
125
|
-
' --intent <intent> memory_recall, previous_session_summary, forensic_quote, or
|
|
127
|
+
' --intent <intent> memory_recall, previous_session_summary, forensic_quote, historical_discussion, or action_history',
|
|
126
128
|
' --db <memory.db> open an explicit database path',
|
|
127
129
|
' --config <toml> open a cogmem TOML config',
|
|
128
130
|
' --include-evidence include bounded raw excerpts; event ids are always returned',
|
|
@@ -158,7 +160,8 @@ function isMemoryCommand(value) {
|
|
|
158
160
|
|| value === 'graph-node'
|
|
159
161
|
|| value === 'graph-neighbors'
|
|
160
162
|
|| value === 'graph-path'
|
|
161
|
-
|| value === 'graph-timeline'
|
|
163
|
+
|| value === 'graph-timeline'
|
|
164
|
+
|| value === 'graph-reindex';
|
|
162
165
|
}
|
|
163
166
|
function reviewActionArg(values, key) {
|
|
164
167
|
const raw = stringArg(values, key);
|
|
@@ -172,9 +175,9 @@ function recallIntentArg(values, key) {
|
|
|
172
175
|
const raw = stringArg(values, key);
|
|
173
176
|
if (!raw)
|
|
174
177
|
return undefined;
|
|
175
|
-
if (raw === 'memory_recall' || raw === 'previous_session_summary' || raw === 'forensic_quote' || raw === 'historical_discussion')
|
|
178
|
+
if (raw === 'memory_recall' || raw === 'previous_session_summary' || raw === 'forensic_quote' || raw === 'historical_discussion' || raw === 'action_history')
|
|
176
179
|
return raw;
|
|
177
|
-
throw new Error(`--${key} must be one of memory_recall, previous_session_summary, forensic_quote, historical_discussion`);
|
|
180
|
+
throw new Error(`--${key} must be one of memory_recall, previous_session_summary, forensic_quote, historical_discussion, action_history`);
|
|
178
181
|
}
|
|
179
182
|
function orderArg(values, key) {
|
|
180
183
|
const raw = stringArg(values, key);
|
|
@@ -573,6 +576,9 @@ function runRecall(kernel, args) {
|
|
|
573
576
|
decisionTrace: result.decisionTrace,
|
|
574
577
|
strategyCapsule,
|
|
575
578
|
narrative: result.narrative,
|
|
579
|
+
atlasCards: result.atlasCards,
|
|
580
|
+
relatedButNotSelected: result.relatedButNotSelected,
|
|
581
|
+
relaxationTrace: result.relaxationTrace,
|
|
576
582
|
items: result.items,
|
|
577
583
|
};
|
|
578
584
|
}
|
|
@@ -600,6 +606,13 @@ function runGraphCommand(kernel, args) {
|
|
|
600
606
|
evidenceLimit: args.evidenceLimit, now: args.now,
|
|
601
607
|
refresh: args.refresh ? true : (args.staleOk ? false : undefined),
|
|
602
608
|
staleOk: args.staleOk || !args.refresh };
|
|
609
|
+
if (args.command === 'graph-reindex') {
|
|
610
|
+
if (!args.eventId && !args.episodeId)
|
|
611
|
+
throw new Error(`graph-reindex requires --event or --episode.\n${usage()}`);
|
|
612
|
+
if (args.eventId && args.episodeId)
|
|
613
|
+
throw new Error(`graph-reindex accepts exactly one of --event or --episode.\n${usage()}`);
|
|
614
|
+
return kernel.reindexMemoryAtlas({ projectId, eventId: args.eventId, episodeId: args.episodeId });
|
|
615
|
+
}
|
|
603
616
|
if (args.command === 'graph')
|
|
604
617
|
return kernel.graphOverview(options);
|
|
605
618
|
if (args.command === 'graph-search') {
|
|
@@ -881,7 +894,7 @@ function printHuman(command, payload) {
|
|
|
881
894
|
return;
|
|
882
895
|
}
|
|
883
896
|
if (command === 'graph' || command.startsWith('graph-')) {
|
|
884
|
-
console.log(`memoryAtlas: ${payload.version || 'memory_atlas.
|
|
897
|
+
console.log(`memoryAtlas: ${payload.version || 'memory_atlas.v2'} project=${payload.projectId || 'unknown'}`);
|
|
885
898
|
const rows = Array.isArray(payload.nodes) ? payload.nodes
|
|
886
899
|
: Array.isArray(payload.path) ? payload.path
|
|
887
900
|
: Array.isArray(payload.actions) ? payload.actions
|
package/dist/factory.d.ts
CHANGED
|
@@ -326,9 +326,15 @@ export type EpisodeRepairInput = {
|
|
|
326
326
|
};
|
|
327
327
|
export interface EpisodeRepairResult {
|
|
328
328
|
repairId: string;
|
|
329
|
+
applied: boolean;
|
|
329
330
|
operation: EpisodeRepairInput['operation'];
|
|
330
331
|
affectedEpisodeIds: string[];
|
|
332
|
+
changedFields: string[];
|
|
331
333
|
staleCandidateIds: string[];
|
|
334
|
+
requeuedDream: boolean;
|
|
335
|
+
graphRefreshNeeded: boolean;
|
|
336
|
+
nextCommands: string[];
|
|
337
|
+
note: string;
|
|
332
338
|
}
|
|
333
339
|
export interface ToolCallMemoryEventInput {
|
|
334
340
|
projectId?: string;
|
|
@@ -635,6 +641,18 @@ export declare class MemoryKernel {
|
|
|
635
641
|
documents: number;
|
|
636
642
|
actions: number;
|
|
637
643
|
};
|
|
644
|
+
reindexMemoryAtlas(options: {
|
|
645
|
+
projectId: string;
|
|
646
|
+
eventId?: string;
|
|
647
|
+
episodeId?: string;
|
|
648
|
+
}): {
|
|
649
|
+
projectId: string;
|
|
650
|
+
episodeIds: string[];
|
|
651
|
+
refreshed: boolean;
|
|
652
|
+
curatedEpisodes: number;
|
|
653
|
+
facetEdges: number;
|
|
654
|
+
reviewNeeded: number;
|
|
655
|
+
};
|
|
638
656
|
ensureMemoryAtlas(options: {
|
|
639
657
|
projectId: string;
|
|
640
658
|
}): {
|
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.
|
|
86
|
+
const CORE_VERSION = '3.7.1';
|
|
87
87
|
const LATEST_SCHEMA_VERSION = 27;
|
|
88
88
|
export class MemoryKernel {
|
|
89
89
|
options;
|
|
@@ -1056,6 +1056,7 @@ export class MemoryKernel {
|
|
|
1056
1056
|
const affected = new Set();
|
|
1057
1057
|
const before = {};
|
|
1058
1058
|
const previousStatuses = new Map();
|
|
1059
|
+
let requeuedDream = input.operation === 'requeue-dream' || input.operation === 'invalidate-dream-run';
|
|
1059
1060
|
if (input.operation === 'move-event') {
|
|
1060
1061
|
const link = this.episodeStore.getEventLink(input.eventId);
|
|
1061
1062
|
const source = link ? this.episodeStore.getEpisode(link.episodeId) : undefined;
|
|
@@ -1169,11 +1170,30 @@ export class MemoryKernel {
|
|
|
1169
1170
|
const episode = this.episodeStore.getEpisode(episodeId);
|
|
1170
1171
|
if (episode?.eventCount && episode.status === 'sealed') {
|
|
1171
1172
|
this.episodeStore.requeueDreamForRepair(episodeId, input.operation === 'invalidate-dream-run' ? input.mode || 'normal' : 'normal', now);
|
|
1173
|
+
requeuedDream = true;
|
|
1172
1174
|
}
|
|
1173
1175
|
}
|
|
1174
1176
|
const after = Object.fromEntries([...affected].map((episodeId) => [episodeId, this.episodeStore.getEpisode(episodeId)]));
|
|
1175
1177
|
const repairId = this.episodeStore.recordRepairAudit({ projectId: input.projectId, operation: input.operation, payload: input, before, after, now });
|
|
1176
|
-
|
|
1178
|
+
const affectedEpisodeIds = [...affected];
|
|
1179
|
+
const nextCommands = affectedEpisodeIds.length
|
|
1180
|
+
? affectedEpisodeIds.map((episodeId) => `cogmem memory graph-reindex --project ${input.projectId} --episode ${episodeId} --json`)
|
|
1181
|
+
: [];
|
|
1182
|
+
return {
|
|
1183
|
+
repairId,
|
|
1184
|
+
applied: true,
|
|
1185
|
+
operation: input.operation,
|
|
1186
|
+
affectedEpisodeIds,
|
|
1187
|
+
changedFields: changedFieldsForRepair(input),
|
|
1188
|
+
staleCandidateIds,
|
|
1189
|
+
requeuedDream,
|
|
1190
|
+
graphRefreshNeeded: affectedEpisodeIds.length > 0,
|
|
1191
|
+
nextCommands: [
|
|
1192
|
+
...nextCommands,
|
|
1193
|
+
`cogmem memory graph-explore --project ${input.projectId} --query "<query>" --json`,
|
|
1194
|
+
],
|
|
1195
|
+
note: 'repairId is an audit id, not a dream candidate id; do not run memory dream --promote just because repairId exists.',
|
|
1196
|
+
};
|
|
1177
1197
|
}
|
|
1178
1198
|
listDreamCandidates(options = {}) {
|
|
1179
1199
|
return this.deepWriteCandidateStore.listCandidates(options);
|
|
@@ -1265,6 +1285,9 @@ export class MemoryKernel {
|
|
|
1265
1285
|
rebuildMemoryAtlas(options = {}) {
|
|
1266
1286
|
return this.memoryAtlasIndexer.rebuild(options);
|
|
1267
1287
|
}
|
|
1288
|
+
reindexMemoryAtlas(options) {
|
|
1289
|
+
return this.memoryAtlasIndexer.reindex(options);
|
|
1290
|
+
}
|
|
1268
1291
|
ensureMemoryAtlas(options) {
|
|
1269
1292
|
return this.memoryAtlasIndexer.ensureFresh(options);
|
|
1270
1293
|
}
|
|
@@ -2002,6 +2025,22 @@ export function createMemoryKernelFromConfig(input = {}) {
|
|
|
2002
2025
|
const { configPath: _configPath, cwd: _cwd, env: _env, ...explicitOptions } = options;
|
|
2003
2026
|
return createMemoryKernel({ ...loaded.options, ...explicitOptions });
|
|
2004
2027
|
}
|
|
2028
|
+
function changedFieldsForRepair(input) {
|
|
2029
|
+
if (input.operation === 'reclassify') {
|
|
2030
|
+
return [
|
|
2031
|
+
input.episodeType ? 'episodeType' : '',
|
|
2032
|
+
input.topicPath ? 'topicPath' : '',
|
|
2033
|
+
input.importance !== undefined ? 'importance' : '',
|
|
2034
|
+
].filter(Boolean);
|
|
2035
|
+
}
|
|
2036
|
+
if (input.operation === 'move-event')
|
|
2037
|
+
return ['episodeEvents'];
|
|
2038
|
+
if (input.operation === 'split' || input.operation === 'merge')
|
|
2039
|
+
return ['episodeEvents', 'crossRefs'];
|
|
2040
|
+
if (input.operation === 'requeue-dream' || input.operation === 'invalidate-dream-run')
|
|
2041
|
+
return ['dreamQueue'];
|
|
2042
|
+
return [];
|
|
2043
|
+
}
|
|
2005
2044
|
function requiredGovernancePayloadString(payload, field) {
|
|
2006
2045
|
const value = payload[field];
|
|
2007
2046
|
if (typeof value !== 'string' || value.trim() === '')
|