cogmem 3.6.4 → 3.6.5
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 +12 -0
- package/README.md +29 -9
- package/RELEASE_CHECKLIST.md +6 -4
- package/dist/agent/AgentMemoryBackend.d.ts +6 -1
- package/dist/agent/AgentMemoryBackend.js +51 -1
- package/dist/agent/AgentRecallQueryCompiler.d.ts +1 -1
- package/dist/agent/AgentRecallQueryCompiler.js +12 -0
- package/dist/atlas/MemoryAtlasService.d.ts +1 -0
- package/dist/atlas/MemoryAtlasService.js +39 -5
- package/dist/atlas/MemoryAtlasTypes.d.ts +14 -0
- package/dist/bin/connect.js +85 -13
- package/dist/bin/memory.js +217 -17
- package/dist/factory.d.ts +2 -0
- package/dist/factory.js +36 -18
- package/dist/host/openclaw/AutoMemoryPluginInstaller.js +6 -2
- package/dist/mcp/server.js +1 -1
- package/dist/store/EventStore.d.ts +6 -0
- package/dist/store/EventStore.js +28 -2
- 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 +24 -3
- package/examples/hermes-backend/references/operations.md +13 -5
- package/examples/openclaw-backend/AGENTS.md +8 -3
- package/examples/openclaw-backend/README.md +7 -2
- package/examples/openclaw-backend/SKILL.md +14 -6
- package/examples/openclaw-backend/references/operations.md +16 -5
- 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
|
}
|
|
@@ -607,12 +780,24 @@ function runCandidates(kernel, args) {
|
|
|
607
780
|
};
|
|
608
781
|
}
|
|
609
782
|
function printHuman(command, payload) {
|
|
610
|
-
if (command === 'status') {
|
|
783
|
+
if (command === 'status' || command === 'plan') {
|
|
784
|
+
if (command === 'plan') {
|
|
785
|
+
console.log(`healthy: ${payload.healthy}`);
|
|
786
|
+
console.log(`queueSummary: ${JSON.stringify(payload.queueSummary)}`);
|
|
787
|
+
console.log(`nextActions: ${JSON.stringify(payload.nextActions)}`);
|
|
788
|
+
console.log(`blocking: ${JSON.stringify(payload.blocking)}`);
|
|
789
|
+
console.log(`nonBlocking: ${JSON.stringify(payload.nonBlocking)}`);
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
611
792
|
console.log(`rawEvents: ${payload.rawEventCount}`);
|
|
612
793
|
console.log(`vectors: ${payload.vectorCount}`);
|
|
613
794
|
console.log(`dreamBacklog: ${JSON.stringify(payload.dreamBacklog)}`);
|
|
614
795
|
console.log(`episodeDream: ${JSON.stringify(payload.episodeDream)}`);
|
|
615
796
|
console.log(`dreamCandidateQueue: ${JSON.stringify(payload.dreamCandidateQueue)}`);
|
|
797
|
+
if (payload.queueSummary)
|
|
798
|
+
console.log(`queueSummary: ${JSON.stringify(payload.queueSummary)}`);
|
|
799
|
+
if (payload.nextActions)
|
|
800
|
+
console.log(`nextActions: ${JSON.stringify(payload.nextActions)}`);
|
|
616
801
|
return;
|
|
617
802
|
}
|
|
618
803
|
if (command === 'dream') {
|
|
@@ -646,6 +831,18 @@ function printHuman(command, payload) {
|
|
|
646
831
|
return;
|
|
647
832
|
}
|
|
648
833
|
if (command === 'candidates') {
|
|
834
|
+
if (payload.queueSummary)
|
|
835
|
+
console.log(`queueSummary: ${JSON.stringify(payload.queueSummary)}`);
|
|
836
|
+
const groups = payload.groups;
|
|
837
|
+
if (groups) {
|
|
838
|
+
for (const [group, values] of Object.entries(groups)) {
|
|
839
|
+
console.log(`${group}: ${values.length}`);
|
|
840
|
+
for (const candidate of values) {
|
|
841
|
+
console.log(`- ${candidate.candidateId} ${candidate.candidateType} ${candidate.status} confidence=${candidate.confidence}`);
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
649
846
|
const candidates = Array.isArray(payload.candidates) ? payload.candidates : [];
|
|
650
847
|
for (const candidate of candidates) {
|
|
651
848
|
console.log(`- ${candidate.candidateId} ${candidate.candidateType} ${candidate.status} confidence=${candidate.confidence}`);
|
|
@@ -741,7 +938,7 @@ async function main() {
|
|
|
741
938
|
console.log(usage());
|
|
742
939
|
return;
|
|
743
940
|
}
|
|
744
|
-
if (args.command === 'status' || args.command === 'candidates') {
|
|
941
|
+
if (args.command === 'status' || args.command === 'plan' || args.command === 'candidates') {
|
|
745
942
|
runReadOnlyInspection(args);
|
|
746
943
|
return;
|
|
747
944
|
}
|
|
@@ -801,3 +998,6 @@ main().catch((error) => {
|
|
|
801
998
|
console.error(error instanceof Error ? error.message : String(error));
|
|
802
999
|
process.exit(1);
|
|
803
1000
|
});
|
|
1001
|
+
function cliArg(value) {
|
|
1002
|
+
return /^[A-Za-z0-9._:/=@+-]+$/u.test(value) ? value : `'${value.replace(/'/g, `'\\''`)}'`;
|
|
1003
|
+
}
|
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.6.
|
|
86
|
+
const CORE_VERSION = '3.6.5';
|
|
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
|
}
|
|
@@ -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
|
|
|
@@ -1754,7 +1757,8 @@ function formatRecallContext(result, config) {
|
|
|
1754
1757
|
if (item.sourceContext.locator && item.sourceContext.locator.command) {
|
|
1755
1758
|
lines.push(' sourceLocator=' + item.sourceContext.locator.command);
|
|
1756
1759
|
} else if (item.sourceContext.event.eventId) {
|
|
1757
|
-
|
|
1760
|
+
const project = item.sourceContext.event.projectId ? ' --project ' + item.sourceContext.event.projectId : '';
|
|
1761
|
+
lines.push(' sourceLocator=cogmem memory show --event ' + item.sourceContext.event.eventId + project + ' --before 2 --after 2 --json');
|
|
1758
1762
|
}
|
|
1759
1763
|
const seenEventIds = new Set([anchorEvent.eventId].filter(Boolean));
|
|
1760
1764
|
const before = uniqueWindowEvents(Array.isArray(item.sourceContext.before) ? item.sourceContext.before : [], seenEventIds).slice(-2);
|
package/dist/mcp/server.js
CHANGED
|
@@ -61,6 +61,9 @@ export declare class EventStore {
|
|
|
61
61
|
getLatestEvent(): MemoryEvent | null;
|
|
62
62
|
listRawEventsAfterGlobalSeq(options?: {
|
|
63
63
|
projectId?: string;
|
|
64
|
+
workspaceId?: string;
|
|
65
|
+
threadId?: string;
|
|
66
|
+
sessionId?: string;
|
|
64
67
|
afterGlobalSeq?: number;
|
|
65
68
|
limit?: number;
|
|
66
69
|
}): MemoryEvent[];
|
|
@@ -78,6 +81,9 @@ export declare class EventStore {
|
|
|
78
81
|
sessionId?: string[];
|
|
79
82
|
startTime?: number;
|
|
80
83
|
endTime?: number;
|
|
84
|
+
sinceGlobalSeq?: number;
|
|
85
|
+
untilGlobalSeq?: number;
|
|
86
|
+
order?: 'asc' | 'desc';
|
|
81
87
|
}): EventAuditPage;
|
|
82
88
|
getEvent(eventId: string): MemoryEvent | null;
|
|
83
89
|
getThreadEvents(threadId: string, options?: {
|
package/dist/store/EventStore.js
CHANGED
|
@@ -270,6 +270,18 @@ export class EventStore {
|
|
|
270
270
|
conditions.push('project_id = ?');
|
|
271
271
|
params.push(options.projectId);
|
|
272
272
|
}
|
|
273
|
+
if (options.workspaceId) {
|
|
274
|
+
conditions.push('workspace_id = ?');
|
|
275
|
+
params.push(options.workspaceId);
|
|
276
|
+
}
|
|
277
|
+
if (options.threadId) {
|
|
278
|
+
conditions.push('thread_id = ?');
|
|
279
|
+
params.push(options.threadId);
|
|
280
|
+
}
|
|
281
|
+
if (options.sessionId) {
|
|
282
|
+
conditions.push('session_id = ?');
|
|
283
|
+
params.push(options.sessionId);
|
|
284
|
+
}
|
|
273
285
|
if (options.afterGlobalSeq !== undefined) {
|
|
274
286
|
conditions.push('COALESCE(global_seq, 0) > ?');
|
|
275
287
|
params.push(options.afterGlobalSeq);
|
|
@@ -347,7 +359,18 @@ export class EventStore {
|
|
|
347
359
|
conditions.push('occurred_at <= ?');
|
|
348
360
|
params.push(filters.endTime);
|
|
349
361
|
}
|
|
362
|
+
if (filters?.sinceGlobalSeq !== undefined) {
|
|
363
|
+
conditions.push('COALESCE(global_seq, 0) >= ?');
|
|
364
|
+
params.push(filters.sinceGlobalSeq);
|
|
365
|
+
}
|
|
366
|
+
if (filters?.untilGlobalSeq !== undefined) {
|
|
367
|
+
conditions.push('COALESCE(global_seq, 0) <= ?');
|
|
368
|
+
params.push(filters.untilGlobalSeq);
|
|
369
|
+
}
|
|
350
370
|
const where = conditions.length ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
371
|
+
const orderSql = filters?.order === 'asc'
|
|
372
|
+
? 'COALESCE(global_seq, 0) ASC, occurred_at ASC, event_id ASC'
|
|
373
|
+
: 'COALESCE(global_seq, 0) DESC, occurred_at DESC, event_id DESC';
|
|
351
374
|
const totalRow = this.db.prepare(`
|
|
352
375
|
SELECT COUNT(*) AS count FROM memory_events ${where}
|
|
353
376
|
`).get(...params);
|
|
@@ -355,7 +378,7 @@ export class EventStore {
|
|
|
355
378
|
SELECT ${MEMORY_EVENT_COLUMNS}
|
|
356
379
|
FROM memory_events
|
|
357
380
|
${where}
|
|
358
|
-
ORDER BY
|
|
381
|
+
ORDER BY ${orderSql}
|
|
359
382
|
LIMIT ? OFFSET ?
|
|
360
383
|
`).all(...params, safePageSize, offset);
|
|
361
384
|
return {
|
|
@@ -375,7 +398,10 @@ export class EventStore {
|
|
|
375
398
|
threadId: filters?.threadId,
|
|
376
399
|
sessionId: filters?.sessionId,
|
|
377
400
|
startTime: filters?.startTime,
|
|
378
|
-
endTime: filters?.endTime
|
|
401
|
+
endTime: filters?.endTime,
|
|
402
|
+
sinceGlobalSeq: filters?.sinceGlobalSeq,
|
|
403
|
+
untilGlobalSeq: filters?.untilGlobalSeq,
|
|
404
|
+
order: filters?.order,
|
|
379
405
|
}
|
|
380
406
|
};
|
|
381
407
|
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -122,12 +122,14 @@ cogmem normalize-transcript --input ./hermes-sessions.jsonl --output ./hermes.no
|
|
|
122
122
|
cogmem import-hermes --workspace . --project hermes --session ./hermes.normalized.md
|
|
123
123
|
```
|
|
124
124
|
|
|
125
|
-
Cogmem 3.6.4
|
|
125
|
+
Cogmem 3.6.4 and later skip import batch sealing for empty episode boundaries and skip legacy empty Dream jobs so one bad imported episode cannot block the queue.
|
|
126
126
|
|
|
127
127
|
After import, use this order:
|
|
128
128
|
|
|
129
129
|
```bash
|
|
130
|
+
cogmem memory plan --project hermes --json
|
|
130
131
|
cogmem memory status --project hermes --json
|
|
132
|
+
cogmem memory candidates --project hermes --json
|
|
131
133
|
cogmem episode status --project hermes --json
|
|
132
134
|
cogmem dream status --project hermes --json
|
|
133
135
|
cogmem dream tick --project hermes --mode auto --max-episodes 20 --json
|
|
@@ -135,9 +137,10 @@ cogmem memory candidates --project hermes --status candidate --json
|
|
|
135
137
|
cogmem memory govern --project hermes --limit 100 --json
|
|
136
138
|
cogmem memory candidates --project hermes --status needs_confirmation --json
|
|
137
139
|
cogmem memory review --project hermes --id <candidate-id> --action approve --actor <operator> --reason "confirmed by user" --confirmation-event <user-event-id> --json
|
|
140
|
+
cogmem memory recall --query "<verification question>" --project hermes --agent hermes --json
|
|
138
141
|
```
|
|
139
142
|
|
|
140
|
-
`needs_confirmation` is not the Dream backlog. `memory govern` does not approve it; use `memory review` with explicit evidence.
|
|
143
|
+
`memory plan` is the first agent-safe health and next-action command. Only run `dream_tick` when it appears in `nextActions`; if `nonBlocking` contains `raw_dream_ledger_lag` with `resolvableByDreamTick:false`, inspect raw sources or episode/import boundaries instead of retrying `dream tick`. Default `memory candidates --json` groups ordinary, `needs_confirmation`, and deferred review queues; use `--status` only when a human asks for one queue. `needs_confirmation` is not the Dream backlog. `memory govern` does not approve it; use `memory review` with explicit evidence.
|
|
141
144
|
|
|
142
145
|
## Active Memory Search
|
|
143
146
|
|
|
@@ -153,15 +156,18 @@ For inventory or product questions, use recall first and raw search as a forensi
|
|
|
153
156
|
cogmem memory recall --query "我们记录过哪些库存" --project hermes --agent hermes --json
|
|
154
157
|
cogmem memory search --query "エルビ 库存" --project hermes --json
|
|
155
158
|
cogmem memory show --event <event-id> --before 2 --after 2 --json
|
|
159
|
+
cogmem memory recall --query "几个月前我们是不是讨论过 Cogmem 的记忆黑盒" --intent historical_discussion --project hermes --agent hermes --json
|
|
160
|
+
cogmem memory list --project hermes --since <globalSeq> --order asc --json
|
|
156
161
|
```
|
|
157
162
|
|
|
158
163
|
`vectors: 0` does not mean Cogmem has no memory. It means the dense vector index has no hot vectors yet. `memory recall` still falls back to governed raw ledger search and returns `sourceContext` locators. Broad inventory questions are expanded into structured cues such as `库存管理`, `在库`, `产品コード`, and `数量`; if compiled-memory candidates miss those cues, raw ledger evidence is preferred.
|
|
159
164
|
|
|
160
|
-
`sourceContext` and `memory show --json` now share the same replay contract: each event has a `label`, optional `charRange` / `sourceRange`, and `sourceContext.window` / `window` metadata with requested counts, actual counts, `excludesAnchor`, `ordering`, `roleFilter`, and `overlapHandling`. Use those fields before quoting exact wording or explaining what happened before/after a recalled point.
|
|
165
|
+
`sourceContext` and `memory show --json` now share the same replay contract: each event has a `label`, optional `charRange` / `sourceRange`, and `sourceContext.window` / `window` metadata with requested counts, actual counts, `excludesAnchor`, `ordering`, `roleFilter`, and `overlapHandling`. Use those fields before quoting exact wording or explaining what happened before/after a recalled point. In 3.6.5, Raw Ledger list rows and Atlas search/explore evidence include `sourceLocator.command` and `sourceLocator.contextCommand`; run those commands before saying the source event is unavailable.
|
|
161
166
|
|
|
162
167
|
Check status with:
|
|
163
168
|
|
|
164
169
|
```bash
|
|
170
|
+
cogmem memory plan --project hermes --json
|
|
165
171
|
cogmem memory status --project hermes --json
|
|
166
172
|
```
|
|
167
173
|
|
|
@@ -221,12 +227,12 @@ Hermes has no automatic observation path in this integration. Use `cogmem_episod
|
|
|
221
227
|
|
|
222
228
|
Use `cogmem_topic_operate` with actor `user_explicit` only for explicit user naming/organization instructions. Model suggestions use `model_candidate` and remain reviewable; alias collisions fail closed. Use `cogmem_episode_repair` for split/merge/move/reclassify/requeue work so receipts, stale candidates, cross-references, Dream jobs, and audit records stay synchronized. Do not hand-edit the database.
|
|
223
229
|
|
|
224
|
-
Use `cogmem memory map --project hermes --json` or MCP `cogmem_memory_map` to inspect Memory Binding and Graph Recall counters. Bindings attach high-value user raw events to stable topic/entity paths, fuse same-claim evidence into claim-key clusters, and create graph anchors for source drill-down only; they are not verified facts, user preferences, or instructions. Correction bindings expose review flags and correction edges. If `cogmem memory tick --project hermes --json` suggests `bind_raw_events`, run `cogmem memory bind --project hermes --json
|
|
230
|
+
Use `cogmem memory map --project hermes --json` or MCP `cogmem_memory_map` to inspect Memory Binding and Graph Recall counters. Bindings attach high-value user raw events to stable topic/entity paths, fuse same-claim evidence into claim-key clusters, and create graph anchors for source drill-down only; they are not verified facts, user preferences, or instructions. Correction bindings expose review flags and correction edges. If `cogmem memory tick --project hermes --json` suggests `bind_raw_events`, run `cogmem memory bind --project hermes --json`; in 3.6.5 it scans the historical ledger by cursor and can resume with `--since <globalSeq>`. Re-run `cogmem connect hermes --workspace . --auto --force --json` after upgrades to patch existing MCP allow-lists with new Cogmem tools. Follow only `nextCommands` for unattended agent work; operator/host actions are in `nextSteps` with `safeForAutomation=false`.
|
|
225
231
|
|
|
226
232
|
Dream correction records require explicit user clarification; assistant self-correction and questions containing `是不是` are not user-owned contradictions. Invalid provider output is a rejected diagnostic. Maintenance ticks supersede `needs_confirmation` entries older than the default 30-day TTL and retain their evidence rows.
|
|
227
233
|
|
|
228
234
|
## Memory Atlas navigation
|
|
229
235
|
|
|
230
|
-
For broad inventory or historical questions call `cogmem_graph_explore`. Use `cogmem_graph_search` and `cogmem_graph_node` for known concepts, `cogmem_graph_neighbors`/`cogmem_graph_path` for relations, and `cogmem_graph_timeline` for ordered reconstruction. Use `cogmem_recall` for a direct fact and follow event IDs to `cogmem memory show` for exact wording.
|
|
236
|
+
For broad inventory or historical questions call `cogmem_graph_explore`. Use `cogmem_graph_search` and `cogmem_graph_node` for known concepts, `cogmem_graph_neighbors`/`cogmem_graph_path` for relations, and `cogmem_graph_timeline` for ordered reconstruction. Use `cogmem_recall` for a direct fact and follow event IDs or returned `sourceLocator` commands to `cogmem memory show` for exact wording.
|
|
231
237
|
|
|
232
238
|
Combine the filters present in the user's message, including project, time, topic, entity/target, memory kind, and ordinary cues. Do not force an entity + time + action tuple. A cold result is newly visible, not newly verified or promoted.
|