cogmem 3.6.4 → 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.
- package/CHANGELOG.md +24 -0
- package/MEMORY_ATLAS.md +47 -10
- package/README.md +36 -13
- package/RELEASE_CHECKLIST.md +7 -4
- package/dist/agent/AgentMemoryBackend.d.ts +19 -2
- package/dist/agent/AgentMemoryBackend.js +134 -3
- package/dist/agent/AgentRecallQueryCompiler.d.ts +1 -1
- package/dist/agent/AgentRecallQueryCompiler.js +12 -0
- package/dist/atlas/EpisodeTitleGenerator.d.ts +31 -0
- package/dist/atlas/EpisodeTitleGenerator.js +152 -0
- package/dist/atlas/FacetQueryPlanner.d.ts +31 -0
- package/dist/atlas/FacetQueryPlanner.js +188 -0
- package/dist/atlas/GraphCurator.d.ts +27 -0
- package/dist/atlas/GraphCurator.js +397 -0
- package/dist/atlas/MemoryAtlasIndexer.d.ts +3 -0
- package/dist/atlas/MemoryAtlasIndexer.js +19 -6
- package/dist/atlas/MemoryAtlasService.d.ts +5 -1
- package/dist/atlas/MemoryAtlasService.js +165 -12
- package/dist/atlas/MemoryAtlasTypes.d.ts +89 -11
- package/dist/bin/connect.js +85 -13
- package/dist/bin/memory.js +221 -18
- package/dist/factory.d.ts +2 -0
- package/dist/factory.js +36 -18
- package/dist/host/openclaw/AutoMemoryPluginInstaller.js +83 -10
- package/dist/mcp/server.js +1 -1
- package/dist/store/EventStore.d.ts +6 -0
- package/dist/store/EventStore.js +28 -2
- package/dist/store/MemoryAtlasStore.d.ts +10 -1
- package/dist/store/MemoryAtlasStore.js +228 -14
- package/dist/types/index.d.ts +3 -0
- package/examples/hermes-backend/AGENTS.md +11 -5
- package/examples/hermes-backend/README.md +5 -3
- package/examples/hermes-backend/SKILL.md +28 -5
- package/examples/hermes-backend/references/operations.md +16 -6
- package/examples/openclaw-backend/AGENTS.md +9 -3
- package/examples/openclaw-backend/README.md +9 -3
- package/examples/openclaw-backend/SKILL.md +23 -10
- package/examples/openclaw-backend/references/operations.md +21 -8
- package/package.json +1 -1
package/dist/bin/memory.js
CHANGED
|
@@ -55,6 +55,8 @@ function readArgs(argv) {
|
|
|
55
55
|
before: numberArg(values, 'before'),
|
|
56
56
|
after: numberArg(values, 'after'),
|
|
57
57
|
sinceGlobalSeq: numberArg(values, 'since') ?? numberArg(values, 'since-global-seq'),
|
|
58
|
+
untilGlobalSeq: numberArg(values, 'until') ?? numberArg(values, 'until-global-seq'),
|
|
59
|
+
order: orderArg(values, 'order'),
|
|
58
60
|
intervalMs: numberArg(values, 'interval-ms'),
|
|
59
61
|
maxRuns: numberArg(values, 'max-runs'),
|
|
60
62
|
promoteLimit: numberArg(values, 'promote-limit'),
|
|
@@ -75,6 +77,7 @@ function usage() {
|
|
|
75
77
|
'',
|
|
76
78
|
'Commands:',
|
|
77
79
|
' status summarize raw ledger, vector, and dream backlog state',
|
|
80
|
+
' plan summarize status as agent-safe next actions',
|
|
78
81
|
' list list raw ledger events with source anchors',
|
|
79
82
|
' search --query <q> search raw ledger text without requiring hot vectors',
|
|
80
83
|
' recall --query <q> run agent-facing governed recall with source context',
|
|
@@ -101,7 +104,9 @@ function usage() {
|
|
|
101
104
|
' --thread <id> scope to one thread',
|
|
102
105
|
' --session <id> scope to one session',
|
|
103
106
|
' --limit <n> result limit, default 20',
|
|
104
|
-
' --since <globalSeq>
|
|
107
|
+
' --since <globalSeq> list/bind events at or after a global sequence',
|
|
108
|
+
' --until <globalSeq> list events at or before a global sequence',
|
|
109
|
+
' --order <asc|desc> list raw ledger order, default desc',
|
|
105
110
|
' --status <status> candidate queue status, default candidate',
|
|
106
111
|
' --id <candidate> candidate id for review',
|
|
107
112
|
' --action <action> review action: approve, reject, defer, supersede, or relink',
|
|
@@ -117,7 +122,7 @@ function usage() {
|
|
|
117
122
|
' --interval-ms <n> watch sleep interval, default 300000',
|
|
118
123
|
' --max-runs <n> stop watch after n iterations; omit for long-running worker',
|
|
119
124
|
' --agent <id> agent id for governed recall, default openclaw',
|
|
120
|
-
' --intent <intent> memory_recall, previous_session_summary, or
|
|
125
|
+
' --intent <intent> memory_recall, previous_session_summary, forensic_quote, or historical_discussion',
|
|
121
126
|
' --db <memory.db> open an explicit database path',
|
|
122
127
|
' --config <toml> open a cogmem TOML config',
|
|
123
128
|
' --include-evidence include bounded raw excerpts; event ids are always returned',
|
|
@@ -135,6 +140,7 @@ function usage() {
|
|
|
135
140
|
}
|
|
136
141
|
function isMemoryCommand(value) {
|
|
137
142
|
return value === 'status'
|
|
143
|
+
|| value === 'plan'
|
|
138
144
|
|| value === 'list'
|
|
139
145
|
|| value === 'search'
|
|
140
146
|
|| value === 'recall'
|
|
@@ -166,9 +172,17 @@ function recallIntentArg(values, key) {
|
|
|
166
172
|
const raw = stringArg(values, key);
|
|
167
173
|
if (!raw)
|
|
168
174
|
return undefined;
|
|
169
|
-
if (raw === 'memory_recall' || raw === 'previous_session_summary' || raw === 'forensic_quote')
|
|
175
|
+
if (raw === 'memory_recall' || raw === 'previous_session_summary' || raw === 'forensic_quote' || raw === 'historical_discussion')
|
|
170
176
|
return raw;
|
|
171
|
-
throw new Error(`--${key} must be one of memory_recall, previous_session_summary, forensic_quote`);
|
|
177
|
+
throw new Error(`--${key} must be one of memory_recall, previous_session_summary, forensic_quote, historical_discussion`);
|
|
178
|
+
}
|
|
179
|
+
function orderArg(values, key) {
|
|
180
|
+
const raw = stringArg(values, key);
|
|
181
|
+
if (!raw)
|
|
182
|
+
return undefined;
|
|
183
|
+
if (raw === 'asc' || raw === 'desc')
|
|
184
|
+
return raw;
|
|
185
|
+
throw new Error(`--${key} must be one of asc, desc`);
|
|
172
186
|
}
|
|
173
187
|
function stringArg(values, key) {
|
|
174
188
|
const value = values[key];
|
|
@@ -219,30 +233,32 @@ function inspectionDbPath(args) {
|
|
|
219
233
|
function runReadOnlyInspection(args) {
|
|
220
234
|
const inspection = new MemoryInspectionStore(inspectionDbPath(args));
|
|
221
235
|
try {
|
|
222
|
-
if (args.command === 'status') {
|
|
236
|
+
if (args.command === 'status' || args.command === 'plan') {
|
|
223
237
|
const payload = inspection.status({
|
|
224
238
|
projectId: args.projectId, workspaceId: args.workspaceId,
|
|
225
239
|
threadId: args.threadId, sessionId: args.sessionId,
|
|
226
240
|
});
|
|
241
|
+
const queueSummary = buildCandidateQueueSummary(inspection, args.projectId, args.limit || 5000);
|
|
242
|
+
const plan = buildMemoryPlan(payload, queueSummary, args.projectId);
|
|
243
|
+
const enrichedPayload = args.command === 'plan'
|
|
244
|
+
? plan
|
|
245
|
+
: { ...payload, queueSummary, nextActions: plan.nextActions, blocking: plan.blocking, nonBlocking: plan.nonBlocking };
|
|
227
246
|
if (args.json) {
|
|
228
|
-
printCliJson(
|
|
247
|
+
printCliJson(`memory.${args.command}`, enrichedPayload, {
|
|
229
248
|
queue: payload.dreamCandidateQueue,
|
|
230
249
|
beliefs: payload.activeBeliefs,
|
|
231
250
|
});
|
|
232
251
|
}
|
|
233
252
|
else
|
|
234
|
-
printHuman(
|
|
253
|
+
printHuman(args.command, enrichedPayload);
|
|
235
254
|
return;
|
|
236
255
|
}
|
|
237
|
-
const
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
limit: args.limit || 50,
|
|
242
|
-
});
|
|
243
|
-
const payload = { total: candidates.length, status, candidates: candidates.map(candidateToJson) };
|
|
256
|
+
const queueSummary = buildCandidateQueueSummary(inspection, args.projectId, args.limit || 50);
|
|
257
|
+
const payload = args.status
|
|
258
|
+
? buildSpecificCandidatePayload(inspection, args.projectId, args.status, args.limit || 50, queueSummary)
|
|
259
|
+
: buildGroupedCandidatePayload(queueSummary);
|
|
244
260
|
if (args.json)
|
|
245
|
-
printCliJson('memory.candidates', payload);
|
|
261
|
+
printCliJson('memory.candidates', payload, { queue: queueSummary });
|
|
246
262
|
else
|
|
247
263
|
printHuman('candidates', payload);
|
|
248
264
|
}
|
|
@@ -262,6 +278,7 @@ function eventText(event) {
|
|
|
262
278
|
}
|
|
263
279
|
function eventToJson(event) {
|
|
264
280
|
const text = eventText(event);
|
|
281
|
+
const locator = eventSourceLocator(event, 2, 2);
|
|
265
282
|
return {
|
|
266
283
|
eventId: event.eventId,
|
|
267
284
|
label: memoryEventLabel(event),
|
|
@@ -294,6 +311,21 @@ function eventToJson(event) {
|
|
|
294
311
|
causalityType: event.causalityType,
|
|
295
312
|
orderingConfidence: event.orderingConfidence,
|
|
296
313
|
},
|
|
314
|
+
sourceLocator: locator,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
function eventSourceLocator(event, before, after) {
|
|
318
|
+
const projectArg = event.projectId ? ` --project ${cliArg(event.projectId)}` : '';
|
|
319
|
+
const base = `cogmem memory show --event ${cliArg(event.eventId)}${projectArg}`;
|
|
320
|
+
return {
|
|
321
|
+
eventId: event.eventId,
|
|
322
|
+
globalSeq: event.globalSeq,
|
|
323
|
+
projectId: event.projectId,
|
|
324
|
+
threadId: event.threadId,
|
|
325
|
+
sessionId: event.sessionId,
|
|
326
|
+
localDate: event.localDate,
|
|
327
|
+
command: `${base} --before ${before} --after ${after} --json`,
|
|
328
|
+
contextCommand: `${base} --before 3 --after 3 --json`,
|
|
297
329
|
};
|
|
298
330
|
}
|
|
299
331
|
function candidateToJson(candidate) {
|
|
@@ -313,6 +345,141 @@ function candidateToJson(candidate) {
|
|
|
313
345
|
updatedAt: candidate.updatedAt,
|
|
314
346
|
};
|
|
315
347
|
}
|
|
348
|
+
function buildCandidateQueueSummary(inspection, projectId, limit) {
|
|
349
|
+
const status = inspection.status({ projectId });
|
|
350
|
+
const queue = status.dreamCandidateQueue;
|
|
351
|
+
const candidateRows = inspection.listCandidates({ projectId, status: 'candidate', limit });
|
|
352
|
+
const needsRows = inspection.listCandidates({ projectId, status: 'needs_confirmation', limit: Math.max(limit, 5000) });
|
|
353
|
+
const now = Date.now();
|
|
354
|
+
const deferredRows = needsRows.filter((candidate) => candidate.reviewAfter !== undefined && candidate.reviewAfter > now);
|
|
355
|
+
const activeNeedsRows = needsRows.filter((candidate) => !(candidate.reviewAfter !== undefined && candidate.reviewAfter > now));
|
|
356
|
+
return {
|
|
357
|
+
candidate: queue.candidate,
|
|
358
|
+
needs_confirmation: queue.needsConfirmation,
|
|
359
|
+
activeNeedsConfirmation: Math.max(0, queue.needsConfirmation - deferredRows.length),
|
|
360
|
+
deferredNeedsConfirmation: deferredRows.length,
|
|
361
|
+
promoted: queue.promoted,
|
|
362
|
+
rejected: queue.rejected,
|
|
363
|
+
superseded: queue.superseded,
|
|
364
|
+
shadow: queue.shadow,
|
|
365
|
+
groups: {
|
|
366
|
+
candidate: candidateRows.slice(0, limit).map(candidateToJson),
|
|
367
|
+
needs_confirmation: activeNeedsRows.slice(0, limit).map(candidateToJson),
|
|
368
|
+
deferred: deferredRows.slice(0, limit).map(candidateToJson),
|
|
369
|
+
},
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
function buildSpecificCandidatePayload(inspection, projectId, status, limit, queueSummary) {
|
|
373
|
+
const candidates = inspection.listCandidates({ projectId, status, limit });
|
|
374
|
+
const payload = {
|
|
375
|
+
total: candidates.length,
|
|
376
|
+
status,
|
|
377
|
+
candidates: candidates.map(candidateToJson),
|
|
378
|
+
queueSummary,
|
|
379
|
+
};
|
|
380
|
+
if (status === 'candidate') {
|
|
381
|
+
payload.warning = 'This lists only status=candidate. Use memory candidates without --status for grouped candidate and needs_confirmation queues.';
|
|
382
|
+
}
|
|
383
|
+
else if (status === 'needs_confirmation') {
|
|
384
|
+
payload.warning = 'memory govern does not process needs_confirmation. Use memory review with explicit user evidence, or defer with review_after.';
|
|
385
|
+
}
|
|
386
|
+
return payload;
|
|
387
|
+
}
|
|
388
|
+
function buildGroupedCandidatePayload(queueSummary) {
|
|
389
|
+
return {
|
|
390
|
+
total: queueSummary.groups.candidate.length + queueSummary.groups.needs_confirmation.length + queueSummary.groups.deferred.length,
|
|
391
|
+
status: 'grouped',
|
|
392
|
+
queueSummary,
|
|
393
|
+
groups: queueSummary.groups,
|
|
394
|
+
nextActions: candidateNextActions(queueSummary, undefined),
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
function buildMemoryPlan(status, queueSummary, projectId) {
|
|
398
|
+
const nextActions = [];
|
|
399
|
+
const blocking = [];
|
|
400
|
+
const nonBlocking = [];
|
|
401
|
+
const projectArg = projectId ? ` --project ${cliArg(projectId)}` : '';
|
|
402
|
+
const episodeDream = status.episodeDream;
|
|
403
|
+
const pendingEpisodes = asCount(episodeDream.pending) + asCount(episodeDream.retryScheduled);
|
|
404
|
+
const undreamedRawCount = asCount(status.undreamedRawCount);
|
|
405
|
+
if (pendingEpisodes > 0) {
|
|
406
|
+
nextActions.push({
|
|
407
|
+
priority: 'high',
|
|
408
|
+
type: 'dream_tick',
|
|
409
|
+
safeForAutomation: true,
|
|
410
|
+
reason: `${pendingEpisodes} sealed episode Dream jobs can be processed`,
|
|
411
|
+
command: `cogmem dream tick${projectArg} --mode auto --max-episodes 20 --json`,
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
if (undreamedRawCount > 0) {
|
|
415
|
+
nonBlocking.push({
|
|
416
|
+
type: 'raw_dream_ledger_lag',
|
|
417
|
+
count: undreamedRawCount,
|
|
418
|
+
episodeDreamPending: pendingEpisodes,
|
|
419
|
+
resolvableByDreamTick: false,
|
|
420
|
+
safeForAutomation: false,
|
|
421
|
+
reason: pendingEpisodes > 0
|
|
422
|
+
? 'Raw ledger dream coverage is tracked separately from sealed episode Dream jobs; dream tick may process episodes but does not guarantee this counter will clear.'
|
|
423
|
+
: 'Raw ledger dream coverage is behind, but there are no sealed episode Dream jobs. Do not run dream tick for this signal alone.',
|
|
424
|
+
command: `cogmem memory list${projectArg} --order asc --limit 20 --json`,
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
nextActions.push(...candidateNextActions(queueSummary, projectId));
|
|
428
|
+
if (queueSummary.activeNeedsConfirmation > 0) {
|
|
429
|
+
blocking.push({
|
|
430
|
+
type: 'needs_confirmation',
|
|
431
|
+
count: queueSummary.activeNeedsConfirmation,
|
|
432
|
+
reason: 'Manual review requires explicit user evidence; memory govern will not process these candidates.',
|
|
433
|
+
command: `cogmem memory candidates${projectArg} --status needs_confirmation --json`,
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
if (queueSummary.deferredNeedsConfirmation > 0) {
|
|
437
|
+
nonBlocking.push({
|
|
438
|
+
type: 'deferred_confirmation',
|
|
439
|
+
count: queueSummary.deferredNeedsConfirmation,
|
|
440
|
+
reason: 'Deferred needs_confirmation remains in the review queue until review_after.',
|
|
441
|
+
command: `cogmem memory candidates${projectArg} --json`,
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
return {
|
|
445
|
+
healthy: nextActions.length === 0 && blocking.length === 0,
|
|
446
|
+
projectId,
|
|
447
|
+
queueSummary,
|
|
448
|
+
dreamBacklog: status.dreamBacklog,
|
|
449
|
+
episodeDream: status.episodeDream,
|
|
450
|
+
vectorState: status.vectorState,
|
|
451
|
+
nextActions,
|
|
452
|
+
blocking,
|
|
453
|
+
nonBlocking,
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
function candidateNextActions(queueSummary, projectId) {
|
|
457
|
+
const projectArg = projectId ? ` --project ${cliArg(projectId)}` : '';
|
|
458
|
+
const actions = [];
|
|
459
|
+
if (queueSummary.candidate > 0) {
|
|
460
|
+
actions.push({
|
|
461
|
+
priority: 'high',
|
|
462
|
+
type: 'govern',
|
|
463
|
+
safeForAutomation: true,
|
|
464
|
+
reason: `${queueSummary.candidate} ordinary candidates are ready for deterministic governance`,
|
|
465
|
+
command: `cogmem memory govern${projectArg} --limit 100 --json`,
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
if (queueSummary.activeNeedsConfirmation > 0) {
|
|
469
|
+
actions.push({
|
|
470
|
+
priority: 'medium',
|
|
471
|
+
type: 'review_needs_confirmation',
|
|
472
|
+
safeForAutomation: false,
|
|
473
|
+
reason: `${queueSummary.activeNeedsConfirmation} candidates need explicit operator/user confirmation`,
|
|
474
|
+
command: `cogmem memory candidates${projectArg} --status needs_confirmation --json`,
|
|
475
|
+
reviewCommand: `cogmem memory review${projectArg} --id <candidate-id> --action <approve|reject|defer|supersede|relink> --actor <operator> --reason <reason> --json`,
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
return actions;
|
|
479
|
+
}
|
|
480
|
+
function asCount(value) {
|
|
481
|
+
return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : 0;
|
|
482
|
+
}
|
|
316
483
|
function runStatus(kernel, args) {
|
|
317
484
|
const page = kernel.eventStore.queryEvents(1, 1, {
|
|
318
485
|
projectId: args.projectId ? [args.projectId] : undefined,
|
|
@@ -344,9 +511,15 @@ function runList(kernel, args) {
|
|
|
344
511
|
workspaceId: args.workspaceId ? [args.workspaceId] : undefined,
|
|
345
512
|
threadId: args.threadId ? [args.threadId] : undefined,
|
|
346
513
|
sessionId: args.sessionId ? [args.sessionId] : undefined,
|
|
514
|
+
sinceGlobalSeq: args.sinceGlobalSeq,
|
|
515
|
+
untilGlobalSeq: args.untilGlobalSeq,
|
|
516
|
+
order: args.order,
|
|
347
517
|
});
|
|
348
518
|
return {
|
|
349
519
|
total: page.total,
|
|
520
|
+
sinceGlobalSeq: args.sinceGlobalSeq,
|
|
521
|
+
untilGlobalSeq: args.untilGlobalSeq,
|
|
522
|
+
order: args.order || 'desc',
|
|
350
523
|
events: page.records.map(eventToJson),
|
|
351
524
|
};
|
|
352
525
|
}
|
|
@@ -400,6 +573,9 @@ function runRecall(kernel, args) {
|
|
|
400
573
|
decisionTrace: result.decisionTrace,
|
|
401
574
|
strategyCapsule,
|
|
402
575
|
narrative: result.narrative,
|
|
576
|
+
atlasCards: result.atlasCards,
|
|
577
|
+
relatedButNotSelected: result.relatedButNotSelected,
|
|
578
|
+
relaxationTrace: result.relaxationTrace,
|
|
403
579
|
items: result.items,
|
|
404
580
|
};
|
|
405
581
|
}
|
|
@@ -607,12 +783,24 @@ function runCandidates(kernel, args) {
|
|
|
607
783
|
};
|
|
608
784
|
}
|
|
609
785
|
function printHuman(command, payload) {
|
|
610
|
-
if (command === 'status') {
|
|
786
|
+
if (command === 'status' || command === 'plan') {
|
|
787
|
+
if (command === 'plan') {
|
|
788
|
+
console.log(`healthy: ${payload.healthy}`);
|
|
789
|
+
console.log(`queueSummary: ${JSON.stringify(payload.queueSummary)}`);
|
|
790
|
+
console.log(`nextActions: ${JSON.stringify(payload.nextActions)}`);
|
|
791
|
+
console.log(`blocking: ${JSON.stringify(payload.blocking)}`);
|
|
792
|
+
console.log(`nonBlocking: ${JSON.stringify(payload.nonBlocking)}`);
|
|
793
|
+
return;
|
|
794
|
+
}
|
|
611
795
|
console.log(`rawEvents: ${payload.rawEventCount}`);
|
|
612
796
|
console.log(`vectors: ${payload.vectorCount}`);
|
|
613
797
|
console.log(`dreamBacklog: ${JSON.stringify(payload.dreamBacklog)}`);
|
|
614
798
|
console.log(`episodeDream: ${JSON.stringify(payload.episodeDream)}`);
|
|
615
799
|
console.log(`dreamCandidateQueue: ${JSON.stringify(payload.dreamCandidateQueue)}`);
|
|
800
|
+
if (payload.queueSummary)
|
|
801
|
+
console.log(`queueSummary: ${JSON.stringify(payload.queueSummary)}`);
|
|
802
|
+
if (payload.nextActions)
|
|
803
|
+
console.log(`nextActions: ${JSON.stringify(payload.nextActions)}`);
|
|
616
804
|
return;
|
|
617
805
|
}
|
|
618
806
|
if (command === 'dream') {
|
|
@@ -646,6 +834,18 @@ function printHuman(command, payload) {
|
|
|
646
834
|
return;
|
|
647
835
|
}
|
|
648
836
|
if (command === 'candidates') {
|
|
837
|
+
if (payload.queueSummary)
|
|
838
|
+
console.log(`queueSummary: ${JSON.stringify(payload.queueSummary)}`);
|
|
839
|
+
const groups = payload.groups;
|
|
840
|
+
if (groups) {
|
|
841
|
+
for (const [group, values] of Object.entries(groups)) {
|
|
842
|
+
console.log(`${group}: ${values.length}`);
|
|
843
|
+
for (const candidate of values) {
|
|
844
|
+
console.log(`- ${candidate.candidateId} ${candidate.candidateType} ${candidate.status} confidence=${candidate.confidence}`);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
649
849
|
const candidates = Array.isArray(payload.candidates) ? payload.candidates : [];
|
|
650
850
|
for (const candidate of candidates) {
|
|
651
851
|
console.log(`- ${candidate.candidateId} ${candidate.candidateType} ${candidate.status} confidence=${candidate.confidence}`);
|
|
@@ -684,7 +884,7 @@ function printHuman(command, payload) {
|
|
|
684
884
|
return;
|
|
685
885
|
}
|
|
686
886
|
if (command === 'graph' || command.startsWith('graph-')) {
|
|
687
|
-
console.log(`memoryAtlas: ${payload.version || 'memory_atlas.
|
|
887
|
+
console.log(`memoryAtlas: ${payload.version || 'memory_atlas.v2'} project=${payload.projectId || 'unknown'}`);
|
|
688
888
|
const rows = Array.isArray(payload.nodes) ? payload.nodes
|
|
689
889
|
: Array.isArray(payload.path) ? payload.path
|
|
690
890
|
: Array.isArray(payload.actions) ? payload.actions
|
|
@@ -741,7 +941,7 @@ async function main() {
|
|
|
741
941
|
console.log(usage());
|
|
742
942
|
return;
|
|
743
943
|
}
|
|
744
|
-
if (args.command === 'status' || args.command === 'candidates') {
|
|
944
|
+
if (args.command === 'status' || args.command === 'plan' || args.command === 'candidates') {
|
|
745
945
|
runReadOnlyInspection(args);
|
|
746
946
|
return;
|
|
747
947
|
}
|
|
@@ -801,3 +1001,6 @@ main().catch((error) => {
|
|
|
801
1001
|
console.error(error instanceof Error ? error.message : String(error));
|
|
802
1002
|
process.exit(1);
|
|
803
1003
|
});
|
|
1004
|
+
function cliArg(value) {
|
|
1005
|
+
return /^[A-Za-z0-9._:/=@+-]+$/u.test(value) ? value : `'${value.replace(/'/g, `'\\''`)}'`;
|
|
1006
|
+
}
|
package/dist/factory.d.ts
CHANGED
|
@@ -138,6 +138,8 @@ export interface MemoryBindingBackfillOptions {
|
|
|
138
138
|
export interface MemoryBindingBackfillResult {
|
|
139
139
|
projectId?: string;
|
|
140
140
|
sinceGlobalSeq?: number;
|
|
141
|
+
nextGlobalSeq?: number;
|
|
142
|
+
hasMore: boolean;
|
|
141
143
|
scannedEvents: number;
|
|
142
144
|
bindableEvents: number;
|
|
143
145
|
boundEvents: number;
|
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.0';
|
|
87
87
|
const LATEST_SCHEMA_VERSION = 27;
|
|
88
88
|
export class MemoryKernel {
|
|
89
89
|
options;
|
|
@@ -1195,18 +1195,22 @@ export class MemoryKernel {
|
|
|
1195
1195
|
}
|
|
1196
1196
|
bindRawEvents(options = {}) {
|
|
1197
1197
|
const limit = Math.max(1, Math.min(options.limit ?? 500, 5000));
|
|
1198
|
-
const
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1198
|
+
const afterGlobalSeq = options.sinceGlobalSeq === undefined
|
|
1199
|
+
? undefined
|
|
1200
|
+
: Math.max(-1, options.sinceGlobalSeq - 1);
|
|
1201
|
+
const records = this.eventStore.listRawEventsAfterGlobalSeq({
|
|
1202
|
+
projectId: options.projectId,
|
|
1203
|
+
workspaceId: options.workspaceId,
|
|
1204
|
+
threadId: options.threadId,
|
|
1205
|
+
sessionId: options.sessionId,
|
|
1206
|
+
afterGlobalSeq,
|
|
1207
|
+
limit,
|
|
1203
1208
|
});
|
|
1204
|
-
const records = page.records
|
|
1205
|
-
.filter((event) => options.sinceGlobalSeq === undefined || (event.globalSeq || 0) >= options.sinceGlobalSeq)
|
|
1206
|
-
.sort((a, b) => (a.globalSeq || 0) - (b.globalSeq || 0));
|
|
1207
1209
|
const result = {
|
|
1208
1210
|
projectId: options.projectId,
|
|
1209
1211
|
sinceGlobalSeq: options.sinceGlobalSeq,
|
|
1212
|
+
nextGlobalSeq: records.at(-1)?.globalSeq,
|
|
1213
|
+
hasMore: records.length >= limit,
|
|
1210
1214
|
scannedEvents: records.length,
|
|
1211
1215
|
bindableEvents: 0,
|
|
1212
1216
|
boundEvents: 0,
|
|
@@ -1325,16 +1329,30 @@ export class MemoryKernel {
|
|
|
1325
1329
|
return { touched: this.memoryAtlasStore.recordAccess(input.projectId, valid, input.reason, input.query, input.now) };
|
|
1326
1330
|
}
|
|
1327
1331
|
countUnboundBindableRawEvents(projectId, limit = 1000) {
|
|
1328
|
-
const
|
|
1329
|
-
|
|
1330
|
-
});
|
|
1332
|
+
const max = Math.max(1, limit);
|
|
1333
|
+
let afterGlobalSeq;
|
|
1331
1334
|
let count = 0;
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1335
|
+
while (count < max) {
|
|
1336
|
+
const batchLimit = Math.min(500, max);
|
|
1337
|
+
const events = this.eventStore.listRawEventsAfterGlobalSeq({
|
|
1338
|
+
projectId,
|
|
1339
|
+
afterGlobalSeq,
|
|
1340
|
+
limit: batchLimit,
|
|
1341
|
+
});
|
|
1342
|
+
if (events.length === 0)
|
|
1343
|
+
break;
|
|
1344
|
+
afterGlobalSeq = events.at(-1)?.globalSeq ?? afterGlobalSeq;
|
|
1345
|
+
for (const event of events) {
|
|
1346
|
+
if (!this.memoryBindingService.isBindableRawEvent(event))
|
|
1347
|
+
continue;
|
|
1348
|
+
if (this.memoryBindingStore.listBindings({ eventId: event.eventId, limit: 1 }).length > 0)
|
|
1349
|
+
continue;
|
|
1350
|
+
count += 1;
|
|
1351
|
+
if (count >= max)
|
|
1352
|
+
break;
|
|
1353
|
+
}
|
|
1354
|
+
if (events.length < batchLimit)
|
|
1355
|
+
break;
|
|
1338
1356
|
}
|
|
1339
1357
|
return count;
|
|
1340
1358
|
}
|
|
@@ -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.
|
|
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
|
}
|
|
@@ -26,7 +26,7 @@ export function installOpenClawAutoMemoryPlugin(options) {
|
|
|
26
26
|
? { kind: 'toml', path: resolve(options.configPath) }
|
|
27
27
|
: resolveCogmemConfigPath({ cwd: workspaceRoot });
|
|
28
28
|
if (configResolution.kind !== 'toml') {
|
|
29
|
-
throw new Error(`Missing cogmem config at ${configResolution.path}.
|
|
29
|
+
throw new Error(`Missing cogmem config at ${configResolution.path}. Ask an operator to run the interactive setup: cogmem init --agent openclaw --scope project.`);
|
|
30
30
|
}
|
|
31
31
|
const configPath = configResolution.path;
|
|
32
32
|
const pluginDir = resolve(options.pluginDir || defaultOpenClawAutoMemoryPluginDir(workspaceRoot));
|
|
@@ -663,6 +663,9 @@ function classifyRecallIntent(query) {
|
|
|
663
663
|
if (/原话|怎么说的|完整对话|上一句|下一句|exact quote|verbatim/.test(text)) {
|
|
664
664
|
return 'forensic_quote';
|
|
665
665
|
}
|
|
666
|
+
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
|
+
return 'historical_discussion';
|
|
668
|
+
}
|
|
666
669
|
return 'memory_recall';
|
|
667
670
|
}
|
|
668
671
|
|
|
@@ -989,7 +992,7 @@ function audit(config, record) {
|
|
|
989
992
|
const plugin = {
|
|
990
993
|
id: PLUGIN_ID,
|
|
991
994
|
name: 'CogMem Auto Memory',
|
|
992
|
-
version: '
|
|
995
|
+
version: '${PLUGIN_VERSION}',
|
|
993
996
|
register(api) {
|
|
994
997
|
if (!api || typeof api.on !== 'function') {
|
|
995
998
|
throw new Error('OpenClaw plugin API missing api.on');
|
|
@@ -1335,10 +1338,32 @@ async function recallPayload(input, config, kernel, memory, formatStrategyContex
|
|
|
1335
1338
|
maxMemoryRatio: Number(config.contextMemoryMaxRatio || 0.25), strategy: strategyCapsule,
|
|
1336
1339
|
candidates: result.items.map(contextCandidateFromRecallItem),
|
|
1337
1340
|
});
|
|
1338
|
-
|
|
1341
|
+
let plannedResult = activationPlan
|
|
1339
1342
|
? { ...result, items: activationPlan.selected.map((candidate) => candidate.recallItem) }
|
|
1340
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
|
+
}
|
|
1341
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 };
|
|
1342
1367
|
const recallContext = formatRecallContext(plannedResult, config);
|
|
1343
1368
|
return {
|
|
1344
1369
|
context: recallContext ? (strategyCapsule ? formatStrategyContext(strategyCapsule) + '\n\n' : '') + recallContext : '',
|
|
@@ -1346,7 +1371,9 @@ async function recallPayload(input, config, kernel, memory, formatStrategyContex
|
|
|
1346
1371
|
recallMode: result.recallMode, fallbackUsed: result.fallbackUsed, intent: input.intent || 'memory_recall',
|
|
1347
1372
|
anchorEventId: anchorItem && anchorItem.sourceAnchor && anchorItem.sourceAnchor.eventId,
|
|
1348
1373
|
anchorText: anchorItem && anchorItem.text, queryPlan: result.queryPlan, decisionTrace: result.decisionTrace,
|
|
1349
|
-
|
|
1374
|
+
atlasCards: result.atlasCards, selectedEpisodeCards,
|
|
1375
|
+
relatedButNotSelected: result.relatedButNotSelected, relaxationTrace: result.relaxationTrace,
|
|
1376
|
+
activationReceipt, strategyCapsule,
|
|
1350
1377
|
strategyReplanned: replan.replanned, strategyReplanReason: replan.reason,
|
|
1351
1378
|
recallLatencyMs: Date.now() - recallStartedAt,
|
|
1352
1379
|
};
|
|
@@ -1391,16 +1418,27 @@ function stripCogmemRecallBlocks(text) {
|
|
|
1391
1418
|
|
|
1392
1419
|
function formatAtlasContext(result, maxChars) {
|
|
1393
1420
|
const nodes = Array.isArray(result && result.nodes) ? result.nodes : [];
|
|
1394
|
-
|
|
1421
|
+
const cards = Array.isArray(result && result.cards) ? result.cards : [];
|
|
1422
|
+
if (!nodes.length && !cards.length) return '';
|
|
1395
1423
|
const edges = Array.isArray(result && result.edges) ? result.edges : [];
|
|
1396
1424
|
const actions = Array.isArray(result && result.nextActions) ? result.nextActions : [];
|
|
1397
1425
|
const nodeDetails = Array.isArray(result && result.nodeDetails) ? result.nodeDetails : [];
|
|
1398
1426
|
const lines = [
|
|
1399
|
-
'<COGMEM_MEMORY_ATLAS version="memory_atlas.
|
|
1427
|
+
'<COGMEM_MEMORY_ATLAS version="memory_atlas.v2" volatile="true" persistence="forbidden" evidence_authority="raw_event_ids_only">',
|
|
1400
1428
|
'Bounded navigation map; use it to choose nodes and paths, not as durable evidence.',
|
|
1401
|
-
'Matched facets: ' + safeAtlasText(JSON.stringify(result.facets || {}),
|
|
1429
|
+
'Matched facets: ' + safeAtlasText(JSON.stringify(result.facets && result.facets.planner || result.facets || {}), 1400),
|
|
1402
1430
|
'Cold memory resurrected: ' + String(result.coldMemoryResurrected === true),
|
|
1403
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
|
+
] : []),
|
|
1404
1442
|
'Nodes:',
|
|
1405
1443
|
...nodes.slice(0, 30).map((node) => '- ' + JSON.stringify({
|
|
1406
1444
|
id: safeAtlasText(node.id, 500), type: safeAtlasText(node.nodeType, 80),
|
|
@@ -1692,6 +1730,9 @@ function compactRecallItems(items, config) {
|
|
|
1692
1730
|
projectId: item.projectId,
|
|
1693
1731
|
sourceAnchor: item.sourceAnchor,
|
|
1694
1732
|
whyMatched: item.whyMatched,
|
|
1733
|
+
canonicalId: item.canonicalId,
|
|
1734
|
+
displayTitle: item.displayTitle,
|
|
1735
|
+
matchedFacets: item.matchedFacets,
|
|
1695
1736
|
}));
|
|
1696
1737
|
}
|
|
1697
1738
|
|
|
@@ -1728,6 +1769,15 @@ function formatRecallContext(result, config) {
|
|
|
1728
1769
|
if (result.decisionTrace) {
|
|
1729
1770
|
lines.push('recallDecision=' + formatRecallDecision(result.decisionTrace));
|
|
1730
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
|
+
}
|
|
1731
1781
|
lines.push('');
|
|
1732
1782
|
if (result.narrative && result.narrative.summary) {
|
|
1733
1783
|
lines.push(result.narrative.summary);
|
|
@@ -1745,7 +1795,11 @@ function formatRecallContext(result, config) {
|
|
|
1745
1795
|
+ (item.sourceAnchor.sessionId ? '; session=' + item.sourceAnchor.sessionId : '')
|
|
1746
1796
|
+ (item.sourceAnchor.role ? '; role=' + item.sourceAnchor.role : '') : '';
|
|
1747
1797
|
const why = item.whyMatched ? '; whyMatched=' + item.whyMatched : '';
|
|
1748
|
-
|
|
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);
|
|
1749
1803
|
if (item.sourceContext && item.sourceContext.event) {
|
|
1750
1804
|
const anchorEvent = item.sourceContext.event;
|
|
1751
1805
|
const anchorFormatted = formatContextEvent(anchorEvent, Math.min(220, sourceWindowMaxChars));
|
|
@@ -1754,7 +1808,8 @@ function formatRecallContext(result, config) {
|
|
|
1754
1808
|
if (item.sourceContext.locator && item.sourceContext.locator.command) {
|
|
1755
1809
|
lines.push(' sourceLocator=' + item.sourceContext.locator.command);
|
|
1756
1810
|
} else if (item.sourceContext.event.eventId) {
|
|
1757
|
-
|
|
1811
|
+
const project = item.sourceContext.event.projectId ? ' --project ' + item.sourceContext.event.projectId : '';
|
|
1812
|
+
lines.push(' sourceLocator=cogmem memory show --event ' + item.sourceContext.event.eventId + project + ' --before 2 --after 2 --json');
|
|
1758
1813
|
}
|
|
1759
1814
|
const seenEventIds = new Set([anchorEvent.eventId].filter(Boolean));
|
|
1760
1815
|
const before = uniqueWindowEvents(Array.isArray(item.sourceContext.before) ? item.sourceContext.before : [], seenEventIds).slice(-2);
|
|
@@ -1797,6 +1852,24 @@ function formatRecallDecision(trace) {
|
|
|
1797
1852
|
+ ',raw:' + Number(counts.rawLedger || 0);
|
|
1798
1853
|
}
|
|
1799
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
|
+
|
|
1800
1873
|
function clampRecallContext(text, maxChars) {
|
|
1801
1874
|
const closingTag = '</COGMEM_RECALL_CONTEXT>';
|
|
1802
1875
|
if (text.length <= maxChars) return text;
|
package/dist/mcp/server.js
CHANGED