cogmem 3.7.1 → 3.7.3
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/MEMORY_ATLAS.md +2 -2
- package/MEMORY_MODEL.md +1 -1
- package/README.md +6 -5
- package/RELEASE_CHECKLIST.md +5 -5
- package/dist/atlas/MemoryAtlasService.d.ts +1 -0
- package/dist/atlas/MemoryAtlasService.js +36 -4
- package/dist/atlas/MemoryAtlasTypes.d.ts +7 -0
- package/dist/belief/BeliefStore.d.ts +3 -1
- package/dist/belief/BeliefStore.js +10 -2
- package/dist/bin/episode.js +107 -16
- package/dist/bin/import-support.js +28 -11
- package/dist/config/CogmemConfig.d.ts +1 -0
- package/dist/config/CogmemConfig.js +45 -0
- package/dist/core/MemoryGraph.js +25 -8
- package/dist/dream/DreamScheduler.d.ts +6 -1
- package/dist/dream/DreamScheduler.js +36 -6
- package/dist/engine/CognitiveGraphCompiler.js +1 -1
- package/dist/engine/ConsolidationPipeline.js +4 -4
- package/dist/engine/DeepWritePromotionPolicy.d.ts +2 -0
- package/dist/engine/DeepWritePromotionPolicy.js +25 -4
- package/dist/engine/DreamCuratorWorker.d.ts +3 -0
- package/dist/engine/DreamCuratorWorker.js +18 -3
- package/dist/engine/FactCompiler.js +2 -2
- package/dist/entity/EntityGovernanceService.js +1 -1
- package/dist/episode/EpisodeActiveScopeGuard.d.ts +3 -0
- package/dist/episode/EpisodeActiveScopeGuard.js +46 -0
- package/dist/episode/EpisodeAssembler.d.ts +20 -2
- package/dist/episode/EpisodeAssembler.js +458 -98
- package/dist/episode/EpisodeBoundaryAuditService.d.ts +70 -0
- package/dist/episode/EpisodeBoundaryAuditService.js +276 -0
- package/dist/episode/EpisodeBoundaryPolicy.d.ts +75 -0
- package/dist/episode/EpisodeBoundaryPolicy.js +189 -0
- package/dist/episode/EpisodeBoundaryReplayEngine.d.ts +65 -0
- package/dist/episode/EpisodeBoundaryReplayEngine.js +272 -0
- package/dist/episode/EpisodeImportIdentity.d.ts +1 -0
- package/dist/episode/EpisodeImportIdentity.js +1 -0
- package/dist/episode/EpisodeInvariantValidator.d.ts +16 -0
- package/dist/episode/EpisodeInvariantValidator.js +99 -0
- package/dist/episode/EpisodeSplitPlanner.d.ts +89 -0
- package/dist/episode/EpisodeSplitPlanner.js +225 -0
- package/dist/episode/EpisodeStore.d.ts +67 -2
- package/dist/episode/EpisodeStore.js +443 -59
- package/dist/episode/EpisodeTypes.d.ts +2 -2
- package/dist/episode/TurnRelationClassifier.d.ts +9 -0
- package/dist/episode/TurnRelationClassifier.js +42 -12
- package/dist/episode/index.d.ts +3 -0
- package/dist/episode/index.js +3 -0
- package/dist/factory.d.ts +62 -3
- package/dist/factory.js +308 -58
- package/dist/mcp/CoreMcpTools.js +103 -8
- package/dist/mcp/server.js +1 -1
- package/dist/migrations/0028_episode_boundary_guardrails.d.ts +3 -0
- package/dist/migrations/0028_episode_boundary_guardrails.js +47 -0
- package/dist/migrations/0029_episode_active_scope_guard.d.ts +3 -0
- package/dist/migrations/0029_episode_active_scope_guard.js +16 -0
- package/dist/migrations/0030_memory_event_local_date_source.d.ts +3 -0
- package/dist/migrations/0030_memory_event_local_date_source.js +111 -0
- package/dist/migrations/0031_episode_boundary_integrity_repair.d.ts +7 -0
- package/dist/migrations/0031_episode_boundary_integrity_repair.js +124 -0
- package/dist/migrations/SchemaMigrationRunner.d.ts +2 -0
- package/dist/migrations/SchemaMigrationRunner.js +46 -15
- package/dist/migrations/index.d.ts +6 -1
- package/dist/migrations/index.js +15 -1
- package/dist/public.d.ts +1 -1
- package/dist/public.js +1 -1
- package/dist/recall/BrainRecall.js +1 -1
- package/dist/store/DeepWriteCandidateStore.d.ts +21 -3
- package/dist/store/DeepWriteCandidateStore.js +118 -11
- package/dist/store/EntityStore.d.ts +4 -0
- package/dist/store/EntityStore.js +24 -12
- package/dist/store/EventStore.d.ts +7 -2
- package/dist/store/EventStore.js +144 -23
- package/dist/store/FactStore.js +6 -5
- package/dist/store/MemoryAtlasStore.d.ts +1 -0
- package/dist/store/MemoryAtlasStore.js +54 -0
- package/dist/types/ExtensionPoints.d.ts +3 -1
- package/dist/types/index.d.ts +1 -0
- package/examples/hermes-backend/AGENTS.md +1 -1
- package/examples/hermes-backend/README.md +1 -1
- package/examples/hermes-backend/SKILL.md +2 -2
- package/examples/hermes-backend/references/operations.md +2 -2
- package/examples/openclaw-backend/AGENTS.md +1 -1
- package/examples/openclaw-backend/README.md +1 -1
- package/examples/openclaw-backend/SKILL.md +2 -2
- package/examples/openclaw-backend/references/operations.md +2 -2
- package/package.json +1 -1
package/dist/store/EventStore.js
CHANGED
|
@@ -3,7 +3,7 @@ import { createHash, randomUUID } from 'crypto';
|
|
|
3
3
|
const MEMORY_EVENT_COLUMNS = `
|
|
4
4
|
event_id, global_seq, stream_id, stream_type, event_type, raw_event_type, event_version, project_id,
|
|
5
5
|
workspace_id, actor_id, causation_id, correlation_id, source_neuron_id, source_id,
|
|
6
|
-
content_hash, thread_id, session_id, local_date, thread_seq, turn_id, turn_seq,
|
|
6
|
+
content_hash, thread_id, session_id, local_date, local_date_source, thread_seq, turn_id, turn_seq,
|
|
7
7
|
event_ordinal, role, parent_event_id, prev_event_id, next_event_id, causality_type,
|
|
8
8
|
source_offset, line_start, line_end, char_start, char_end, ordering_confidence,
|
|
9
9
|
occurred_at, payload_json, payload_hash, created_at
|
|
@@ -11,9 +11,16 @@ const MEMORY_EVENT_COLUMNS = `
|
|
|
11
11
|
export class EventStore {
|
|
12
12
|
encryptionProvider;
|
|
13
13
|
db;
|
|
14
|
+
ownsDb = true;
|
|
14
15
|
constructor(dbPath = ':memory:', encryptionProvider) {
|
|
15
16
|
this.encryptionProvider = encryptionProvider;
|
|
16
|
-
|
|
17
|
+
if (dbPath instanceof Database) {
|
|
18
|
+
this.db = dbPath;
|
|
19
|
+
this.ownsDb = false;
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
this.db = new Database(dbPath);
|
|
23
|
+
}
|
|
17
24
|
this.initializeSchema();
|
|
18
25
|
}
|
|
19
26
|
initializeSchema() {
|
|
@@ -37,6 +44,7 @@ export class EventStore {
|
|
|
37
44
|
thread_id TEXT,
|
|
38
45
|
session_id TEXT,
|
|
39
46
|
local_date TEXT,
|
|
47
|
+
local_date_source TEXT NOT NULL DEFAULT 'legacy_unknown',
|
|
40
48
|
thread_seq INTEGER,
|
|
41
49
|
turn_id TEXT,
|
|
42
50
|
turn_seq INTEGER,
|
|
@@ -115,6 +123,7 @@ export class EventStore {
|
|
|
115
123
|
addColumn('thread_id', 'thread_id TEXT');
|
|
116
124
|
addColumn('session_id', 'session_id TEXT');
|
|
117
125
|
addColumn('local_date', 'local_date TEXT');
|
|
126
|
+
addColumn('local_date_source', "local_date_source TEXT NOT NULL DEFAULT 'legacy_unknown'");
|
|
118
127
|
addColumn('thread_seq', 'thread_seq INTEGER');
|
|
119
128
|
addColumn('turn_id', 'turn_id TEXT');
|
|
120
129
|
addColumn('turn_seq', 'turn_seq INTEGER');
|
|
@@ -134,6 +143,17 @@ export class EventStore {
|
|
|
134
143
|
this.db.exec(`CREATE INDEX IF NOT EXISTS idx_memory_events_thread_order ON memory_events(thread_id, thread_seq, event_ordinal, global_seq);`);
|
|
135
144
|
this.db.exec(`CREATE INDEX IF NOT EXISTS idx_memory_events_parent ON memory_events(parent_event_id);`);
|
|
136
145
|
this.db.exec(`
|
|
146
|
+
CREATE TABLE IF NOT EXISTS import_source_anchors (
|
|
147
|
+
project_id TEXT NOT NULL,
|
|
148
|
+
source_id TEXT NOT NULL,
|
|
149
|
+
import_anchor TEXT NOT NULL,
|
|
150
|
+
event_id TEXT NOT NULL UNIQUE,
|
|
151
|
+
content_hash TEXT NOT NULL,
|
|
152
|
+
created_at INTEGER NOT NULL,
|
|
153
|
+
PRIMARY KEY (project_id, source_id, import_anchor)
|
|
154
|
+
);
|
|
155
|
+
`);
|
|
156
|
+
this.db.exec(`
|
|
137
157
|
CREATE VIRTUAL TABLE IF NOT EXISTS memory_events_fts USING fts5(
|
|
138
158
|
event_id UNINDEXED,
|
|
139
159
|
text,
|
|
@@ -149,9 +169,11 @@ export class EventStore {
|
|
|
149
169
|
`);
|
|
150
170
|
this.rebuildRawEventFtsIfNeeded();
|
|
151
171
|
}
|
|
152
|
-
append(input) {
|
|
172
|
+
append(input, retry = 0) {
|
|
153
173
|
const eventVersion = input.eventVersion ?? this.getNextEventVersion(input.streamId);
|
|
154
174
|
const occurredAt = input.occurredAt ?? Date.now();
|
|
175
|
+
if (!Number.isFinite(occurredAt) || Math.abs(occurredAt) > 8_640_000_000_000_000)
|
|
176
|
+
throw new Error('invalid_event_timestamp');
|
|
155
177
|
const payloadJson = JSON.stringify(input.payload);
|
|
156
178
|
const storedPayloadJson = this.encodePayload(payloadJson);
|
|
157
179
|
const payloadHash = createHash('sha256').update(payloadJson).digest('hex');
|
|
@@ -159,6 +181,14 @@ export class EventStore {
|
|
|
159
181
|
const threadSeq = input.threadSeq ?? (threadId ? this.getNextThreadSeq(threadId) : undefined);
|
|
160
182
|
const globalSeq = this.getNextGlobalSeq();
|
|
161
183
|
const createdAt = Date.now();
|
|
184
|
+
const localDateSource = input.localDateSource ?? (input.localDate ? 'explicit' : 'generated_utc');
|
|
185
|
+
if (localDateSource !== 'explicit' && localDateSource !== 'generated_utc' && localDateSource !== 'legacy_unknown')
|
|
186
|
+
throw new Error('invalid_local_date_source');
|
|
187
|
+
if (localDateSource === 'explicit' && !input.localDate)
|
|
188
|
+
throw new Error('explicit_local_date_required');
|
|
189
|
+
const generatedUtcDate = new Date(occurredAt).toISOString().slice(0, 10);
|
|
190
|
+
if (localDateSource === 'generated_utc' && input.localDate && input.localDate !== generatedUtcDate)
|
|
191
|
+
throw new Error('generated_utc_local_date_mismatch');
|
|
162
192
|
const event = {
|
|
163
193
|
eventId: input.eventId || `evt-${randomUUID()}`,
|
|
164
194
|
globalSeq,
|
|
@@ -178,6 +208,7 @@ export class EventStore {
|
|
|
178
208
|
threadId,
|
|
179
209
|
sessionId: input.sessionId,
|
|
180
210
|
localDate: input.localDate ?? new Date(occurredAt).toISOString().slice(0, 10),
|
|
211
|
+
localDateSource,
|
|
181
212
|
threadSeq,
|
|
182
213
|
turnId: input.turnId,
|
|
183
214
|
turnSeq: input.turnSeq,
|
|
@@ -192,25 +223,80 @@ export class EventStore {
|
|
|
192
223
|
lineEnd: input.lineEnd,
|
|
193
224
|
charStart: input.charStart,
|
|
194
225
|
charEnd: input.charEnd,
|
|
195
|
-
orderingConfidence: input.orderingConfidence ?? (threadSeq || input.eventOrdinal ? 'high' : 'low'),
|
|
226
|
+
orderingConfidence: input.orderingConfidence ?? (threadSeq !== undefined || input.eventOrdinal !== undefined || input.sourceOffset !== undefined || input.lineStart !== undefined ? 'high' : 'low'),
|
|
196
227
|
occurredAt,
|
|
197
228
|
payload: input.payload,
|
|
198
229
|
payloadHash,
|
|
199
230
|
createdAt,
|
|
200
231
|
ingestedAt: createdAt
|
|
201
232
|
};
|
|
202
|
-
this.db.prepare(`
|
|
233
|
+
const insert = () => this.db.prepare(`
|
|
203
234
|
INSERT INTO memory_events (
|
|
204
235
|
event_id, global_seq, stream_id, stream_type, event_type, raw_event_type, event_version, project_id,
|
|
205
236
|
workspace_id, actor_id, causation_id, correlation_id, source_neuron_id, source_id,
|
|
206
|
-
content_hash, thread_id, session_id, local_date, thread_seq, turn_id, turn_seq,
|
|
237
|
+
content_hash, thread_id, session_id, local_date, local_date_source, thread_seq, turn_id, turn_seq,
|
|
207
238
|
event_ordinal, role, parent_event_id, prev_event_id, next_event_id, causality_type,
|
|
208
239
|
source_offset, line_start, line_end, char_start, char_end, ordering_confidence,
|
|
209
240
|
occurred_at, payload_json, payload_hash, created_at
|
|
210
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
211
|
-
`).run(event.eventId, event.globalSeq ?? null, event.streamId, event.streamType, event.eventType, event.rawEventType || null, event.eventVersion, event.projectId || null, event.workspaceId || null, event.actorId || null, event.causationId || null, event.correlationId || null, event.sourceNeuronId || null, event.sourceId || null, event.contentHash || null, event.threadId || null, event.sessionId || null, event.localDate || null, event.
|
|
212
|
-
|
|
213
|
-
|
|
241
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
242
|
+
`).run(event.eventId, event.globalSeq ?? null, event.streamId, event.streamType, event.eventType, event.rawEventType || null, event.eventVersion, event.projectId || null, event.workspaceId || null, event.actorId || null, event.causationId || null, event.correlationId || null, event.sourceNeuronId || null, event.sourceId || null, event.contentHash || null, event.threadId || null, event.sessionId || null, event.localDate || null, event.localDateSource || 'legacy_unknown', event.threadSeq ?? null, event.turnId || null, event.turnSeq ?? null, event.eventOrdinal ?? null, event.role || null, event.parentEventId || null, event.prevEventId || null, event.nextEventId || null, event.causalityType || null, event.sourceOffset ?? null, event.lineStart ?? null, event.lineEnd ?? null, event.charStart ?? null, event.charEnd ?? null, event.orderingConfidence || null, event.occurredAt, storedPayloadJson, event.payloadHash, event.createdAt);
|
|
243
|
+
try {
|
|
244
|
+
this.db.transaction(() => {
|
|
245
|
+
insert();
|
|
246
|
+
this.upsertImportAnchor(event);
|
|
247
|
+
this.upsertRawEventFts(event);
|
|
248
|
+
})();
|
|
249
|
+
return event;
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
253
|
+
const streamConflict = /UNIQUE constraint failed: memory_events\.(stream_id|global_seq)/.test(message);
|
|
254
|
+
const anchorConflict = message === 'import_anchor_already_exists';
|
|
255
|
+
const autoEventVersion = input.eventVersion === undefined;
|
|
256
|
+
const autoThreadSeq = input.threadSeq === undefined;
|
|
257
|
+
if ((streamConflict || message.includes('database is locked')) && autoEventVersion && retry < 5) {
|
|
258
|
+
return this.append({ ...input, eventVersion: undefined, threadSeq: autoThreadSeq ? undefined : input.threadSeq }, retry + 1);
|
|
259
|
+
}
|
|
260
|
+
if (!anchorConflict)
|
|
261
|
+
throw error;
|
|
262
|
+
const metadata = event.payload?.metadata;
|
|
263
|
+
const anchor = typeof metadata?.importAnchor === 'string' ? metadata.importAnchor : undefined;
|
|
264
|
+
const existing = anchor && event.projectId && event.sourceId
|
|
265
|
+
? this.findImportedEventAnchor(event.projectId, event.sourceId, anchor)
|
|
266
|
+
: null;
|
|
267
|
+
if (!existing)
|
|
268
|
+
throw error;
|
|
269
|
+
if (existing.contentHash !== event.contentHash)
|
|
270
|
+
throw new Error(`import_anchor_content_conflict:${anchor}`);
|
|
271
|
+
return existing;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
upsertImportAnchor(event) {
|
|
275
|
+
const metadata = event.payload?.metadata;
|
|
276
|
+
const anchor = typeof metadata?.importAnchor === 'string' ? metadata.importAnchor : undefined;
|
|
277
|
+
if (!anchor || !event.projectId || !event.sourceId || !event.contentHash)
|
|
278
|
+
return;
|
|
279
|
+
this.db.prepare(`
|
|
280
|
+
INSERT INTO import_source_anchors (project_id, source_id, import_anchor, event_id, content_hash, created_at)
|
|
281
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
282
|
+
ON CONFLICT(project_id, source_id, import_anchor) DO NOTHING
|
|
283
|
+
`).run(event.projectId, event.sourceId, anchor, event.eventId, event.contentHash, event.createdAt);
|
|
284
|
+
const inserted = this.db.prepare(`SELECT event_id FROM import_source_anchors WHERE project_id = ? AND source_id = ? AND import_anchor = ?`)
|
|
285
|
+
.get(event.projectId, event.sourceId, anchor);
|
|
286
|
+
if (inserted?.event_id !== event.eventId) {
|
|
287
|
+
const existingEvent = inserted?.event_id
|
|
288
|
+
? this.db.prepare(`SELECT 1 FROM memory_events WHERE event_id = ?`).get(inserted.event_id)
|
|
289
|
+
: null;
|
|
290
|
+
if (!existingEvent) {
|
|
291
|
+
this.db.prepare(`
|
|
292
|
+
UPDATE import_source_anchors
|
|
293
|
+
SET event_id = ?, content_hash = ?, created_at = ?
|
|
294
|
+
WHERE project_id = ? AND source_id = ? AND import_anchor = ?
|
|
295
|
+
`).run(event.eventId, event.contentHash, event.createdAt, event.projectId, event.sourceId, anchor);
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
throw new Error('import_anchor_already_exists');
|
|
299
|
+
}
|
|
214
300
|
}
|
|
215
301
|
getNextGlobalSeq() {
|
|
216
302
|
const row = this.db.prepare(`
|
|
@@ -249,9 +335,18 @@ export class EventStore {
|
|
|
249
335
|
FROM memory_events
|
|
250
336
|
WHERE (? IS NULL OR occurred_at > ?)
|
|
251
337
|
ORDER BY COALESCE(global_seq, 0) ASC, occurred_at ASC, event_id ASC
|
|
252
|
-
`).all(lastEventTime
|
|
338
|
+
`).all(lastEventTime ?? null, lastEventTime ?? null);
|
|
253
339
|
return rows.map((row) => this.mapRow(row));
|
|
254
340
|
}
|
|
341
|
+
findImportedEventAnchor(projectId, sourceId, importAnchor) {
|
|
342
|
+
const row = this.db.prepare(`
|
|
343
|
+
SELECT ${qualifiedMemoryEventColumns('e')}
|
|
344
|
+
FROM import_source_anchors a
|
|
345
|
+
JOIN memory_events e ON e.event_id = a.event_id
|
|
346
|
+
WHERE a.project_id = ? AND a.source_id = ? AND a.import_anchor = ?
|
|
347
|
+
`).get(projectId, sourceId, importAnchor);
|
|
348
|
+
return row ? this.mapRow(row) : null;
|
|
349
|
+
}
|
|
255
350
|
getLatestEvent() {
|
|
256
351
|
const row = this.db.prepare(`
|
|
257
352
|
SELECT ${MEMORY_EVENT_COLUMNS}
|
|
@@ -430,9 +525,10 @@ export class EventStore {
|
|
|
430
525
|
conditions.push('local_date = ?');
|
|
431
526
|
params.push(options.localDate);
|
|
432
527
|
}
|
|
433
|
-
const
|
|
434
|
-
|
|
435
|
-
|
|
528
|
+
const boundedLimit = options.limit === undefined ? undefined : Math.max(1, Math.min(Math.trunc(options.limit), 10_000));
|
|
529
|
+
const limitSql = boundedLimit === undefined ? '' : 'LIMIT ?';
|
|
530
|
+
if (boundedLimit !== undefined)
|
|
531
|
+
params.push(boundedLimit);
|
|
436
532
|
const rows = this.db.prepare(`
|
|
437
533
|
SELECT ${MEMORY_EVENT_COLUMNS}
|
|
438
534
|
FROM memory_events
|
|
@@ -564,7 +660,8 @@ export class EventStore {
|
|
|
564
660
|
`).run(checkpoint.projectionName, checkpoint.lastEventId || null, checkpoint.lastEventTime || null, checkpoint.lastRebuildAt || null, checkpoint.lastFullCount, checkpoint.lastChecksum || null, checkpoint.status, checkpoint.metadata ? JSON.stringify(checkpoint.metadata) : null);
|
|
565
661
|
}
|
|
566
662
|
close() {
|
|
567
|
-
this.
|
|
663
|
+
if (this.ownsDb)
|
|
664
|
+
this.db.close();
|
|
568
665
|
}
|
|
569
666
|
mapRow(row) {
|
|
570
667
|
return {
|
|
@@ -586,20 +683,21 @@ export class EventStore {
|
|
|
586
683
|
threadId: row.thread_id || (row.stream_type === 'thread' ? row.stream_id : undefined),
|
|
587
684
|
sessionId: row.session_id || undefined,
|
|
588
685
|
localDate: row.local_date || undefined,
|
|
589
|
-
|
|
686
|
+
localDateSource: row.local_date_source || legacyLocalDateSource(row.payload_json),
|
|
687
|
+
threadSeq: row.thread_seq ?? undefined,
|
|
590
688
|
turnId: row.turn_id || undefined,
|
|
591
|
-
turnSeq: row.turn_seq
|
|
592
|
-
eventOrdinal: row.event_ordinal
|
|
689
|
+
turnSeq: row.turn_seq ?? undefined,
|
|
690
|
+
eventOrdinal: row.event_ordinal ?? undefined,
|
|
593
691
|
role: row.role || undefined,
|
|
594
692
|
parentEventId: row.parent_event_id || undefined,
|
|
595
693
|
prevEventId: row.prev_event_id || undefined,
|
|
596
694
|
nextEventId: row.next_event_id || undefined,
|
|
597
695
|
causalityType: row.causality_type || undefined,
|
|
598
|
-
sourceOffset: row.source_offset
|
|
599
|
-
lineStart: row.line_start
|
|
600
|
-
lineEnd: row.line_end
|
|
601
|
-
charStart: row.char_start
|
|
602
|
-
charEnd: row.char_end
|
|
696
|
+
sourceOffset: row.source_offset ?? undefined,
|
|
697
|
+
lineStart: row.line_start ?? undefined,
|
|
698
|
+
lineEnd: row.line_end ?? undefined,
|
|
699
|
+
charStart: row.char_start ?? undefined,
|
|
700
|
+
charEnd: row.char_end ?? undefined,
|
|
603
701
|
orderingConfidence: row.ordering_confidence || undefined,
|
|
604
702
|
occurredAt: row.occurred_at,
|
|
605
703
|
payload: JSON.parse(this.decodePayload(row.payload_json)),
|
|
@@ -720,6 +818,29 @@ export class EventStore {
|
|
|
720
818
|
return this.encryptionProvider?.decrypt(payloadJson) ?? payloadJson;
|
|
721
819
|
}
|
|
722
820
|
}
|
|
821
|
+
function qualifiedMemoryEventColumns(alias) {
|
|
822
|
+
return MEMORY_EVENT_COLUMNS.split(',').map((column) => `${alias}.${column.trim()}`).join(', ');
|
|
823
|
+
}
|
|
723
824
|
function escapeSqlLike(value) {
|
|
724
825
|
return value.replace(/[\\%_]/g, (character) => `\\${character}`);
|
|
725
826
|
}
|
|
827
|
+
function validCalendarDate(value) {
|
|
828
|
+
if (!/^\d{4}-\d{2}-\d{2}$/u.test(value))
|
|
829
|
+
return false;
|
|
830
|
+
const [year, month, day] = value.split('-').map(Number);
|
|
831
|
+
const date = new Date(Date.UTC(year, month - 1, day));
|
|
832
|
+
return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day;
|
|
833
|
+
}
|
|
834
|
+
function legacyLocalDateSource(payloadJson) {
|
|
835
|
+
try {
|
|
836
|
+
const payload = JSON.parse(payloadJson);
|
|
837
|
+
if (payload.metadata?.localDateSource === 'event_store_utc_default')
|
|
838
|
+
return 'generated_utc';
|
|
839
|
+
if (payload.metadata?.localDateSource === 'explicit')
|
|
840
|
+
return 'explicit';
|
|
841
|
+
}
|
|
842
|
+
catch {
|
|
843
|
+
return 'legacy_unknown';
|
|
844
|
+
}
|
|
845
|
+
return 'legacy_unknown';
|
|
846
|
+
}
|
package/dist/store/FactStore.js
CHANGED
|
@@ -122,7 +122,7 @@ export class FactStore {
|
|
|
122
122
|
const rows = this.db.prepare(`
|
|
123
123
|
SELECT *
|
|
124
124
|
FROM facts
|
|
125
|
-
WHERE neuron_id IN (${placeholders})
|
|
125
|
+
WHERE neuron_id IN (${placeholders}) AND status IN ('provisional', 'provisional_enriched', 'verified')
|
|
126
126
|
ORDER BY valid_from DESC, fact_id DESC
|
|
127
127
|
LIMIT ?
|
|
128
128
|
`).all(...neuronIds, limit);
|
|
@@ -139,14 +139,15 @@ export class FactStore {
|
|
|
139
139
|
SELECT *
|
|
140
140
|
FROM facts
|
|
141
141
|
WHERE entity_id IN (${entityPlaceholders})
|
|
142
|
+
AND status IN ('provisional', 'provisional_enriched', 'verified')
|
|
142
143
|
AND predicate_family IN (${predicateFamilies.map(() => '?').join(', ')})
|
|
143
144
|
ORDER BY valid_from DESC, fact_id DESC
|
|
144
145
|
LIMIT ?
|
|
145
146
|
`).all(...entityIds, ...predicateFamilies, limit)
|
|
146
147
|
: this.db.prepare(`
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
148
|
+
SELECT *
|
|
149
|
+
FROM facts
|
|
150
|
+
WHERE entity_id IN (${entityPlaceholders}) AND status IN ('provisional', 'provisional_enriched', 'verified')
|
|
150
151
|
ORDER BY valid_from DESC, fact_id DESC
|
|
151
152
|
LIMIT ?
|
|
152
153
|
`).all(...entityIds, limit);
|
|
@@ -159,7 +160,7 @@ export class FactStore {
|
|
|
159
160
|
const rows = this.db.prepare(`
|
|
160
161
|
SELECT DISTINCT neuron_id
|
|
161
162
|
FROM facts
|
|
162
|
-
WHERE entity_id IN (${placeholders})
|
|
163
|
+
WHERE entity_id IN (${placeholders}) AND status IN ('provisional', 'provisional_enriched', 'verified')
|
|
163
164
|
ORDER BY valid_from DESC
|
|
164
165
|
LIMIT ?
|
|
165
166
|
`).all(...entityIds, limit);
|
|
@@ -33,6 +33,7 @@ export declare class MemoryAtlasStore {
|
|
|
33
33
|
evidenceTotal(nodeId: string, projectId: string): number;
|
|
34
34
|
listEdges(projectId: string): MemoryAtlasEdge[];
|
|
35
35
|
listEdgesForNodes(projectId: string, nodeIds: string[], limit?: number): MemoryAtlasEdge[];
|
|
36
|
+
listEdgesWithinNodes(projectId: string, nodeIds: string[], limit?: number): MemoryAtlasEdge[];
|
|
36
37
|
findEdgesBetween(projectId: string, leftNodeId: string, rightNodeId: string): MemoryAtlasEdge[];
|
|
37
38
|
findEdgesFromNodesToTarget(projectId: string, leftNodeIds: string[], rightNodeId: string): MemoryAtlasEdge[];
|
|
38
39
|
listActions(projectId: string, options: {
|
|
@@ -301,6 +301,54 @@ export class MemoryAtlasStore {
|
|
|
301
301
|
edges.push(...this.topicRelationEdges(projectId, topicPaths, Math.max(1, Math.min(limit, 2000))));
|
|
302
302
|
return Array.from(new Map(edges.map((edge) => [`${edge.source}\0${edge.relation}\0${edge.target}`, edge])).values()).slice(0, Math.max(1, Math.min(limit, 4000)));
|
|
303
303
|
}
|
|
304
|
+
listEdgesWithinNodes(projectId, nodeIds, limit = 4000) {
|
|
305
|
+
const selectedIds = new Set(Array.from(new Set(nodeIds)));
|
|
306
|
+
const parsed = Array.from(selectedIds).map((id) => parseNodeId(id, projectId)).filter((item) => Boolean(item));
|
|
307
|
+
if (!parsed.length)
|
|
308
|
+
return [];
|
|
309
|
+
const edges = [];
|
|
310
|
+
const cap = Math.max(1, Math.min(limit, 4000));
|
|
311
|
+
for (const sourceChunk of chunked(parsed, 120)) {
|
|
312
|
+
for (const targetChunk of chunked(parsed, 120)) {
|
|
313
|
+
const sourceClause = sourceChunk.map(() => '(source_type=? AND source_id=?)').join(' OR ');
|
|
314
|
+
const targetClause = targetChunk.map(() => '(target_type=? AND target_id=?)').join(' OR ');
|
|
315
|
+
const rows = this.db.prepare(`
|
|
316
|
+
SELECT * FROM memory_edges
|
|
317
|
+
WHERE project_id=? AND status IN ('active','weak')
|
|
318
|
+
AND (${sourceClause}) AND (${targetClause})
|
|
319
|
+
ORDER BY confidence DESC LIMIT ?
|
|
320
|
+
`).all(projectId, ...sourceChunk.flatMap((item) => [item.type, item.id]), ...targetChunk.flatMap((item) => [item.type, item.id]), cap);
|
|
321
|
+
for (const row of rows)
|
|
322
|
+
edges.push({
|
|
323
|
+
source: nodeId(String(row.source_type), String(row.source_id), projectId), relation: String(row.relation_type),
|
|
324
|
+
target: nodeId(String(row.target_type), String(row.target_id), projectId), confidence: Number(row.confidence),
|
|
325
|
+
evidenceEventIds: parseStringArray(String(row.evidence_event_ids_json || '[]')),
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
const actionIds = parsed.filter((item) => item.type === 'action').map((item) => item.id);
|
|
330
|
+
if (actionIds.length) {
|
|
331
|
+
for (const chunk of chunked(actionIds, 400)) {
|
|
332
|
+
const actions = this.db.prepare(`SELECT action_id,target_entity_id,occurred_at,confidence FROM memory_action_frames
|
|
333
|
+
WHERE project_id=? AND action_id IN (${chunk.map(() => '?').join(',')})`).all(projectId, ...chunk);
|
|
334
|
+
for (const action of actions) {
|
|
335
|
+
const evidenceEventIds = this.actionEvidenceIds(action.action_id, projectId);
|
|
336
|
+
if (action.target_entity_id && selectedIds.has(`entity:${action.target_entity_id}`)) {
|
|
337
|
+
edges.push({ source: `action:${action.action_id}`, relation: 'TARGETS', target: `entity:${action.target_entity_id}`, confidence: action.confidence, evidenceEventIds });
|
|
338
|
+
}
|
|
339
|
+
const timeId = timeNodeId(projectId, action.occurred_at);
|
|
340
|
+
if (selectedIds.has(timeId))
|
|
341
|
+
edges.push({ source: `action:${action.action_id}`, relation: 'OCCURRED_IN', target: timeId, confidence: 1, evidenceEventIds });
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
const topicPaths = parsed.filter((item) => item.type === 'topic').map((item) => item.id);
|
|
346
|
+
if (topicPaths.length) {
|
|
347
|
+
edges.push(...this.topicRelationEdges(projectId, topicPaths, cap)
|
|
348
|
+
.filter((edge) => selectedIds.has(edge.source) && selectedIds.has(edge.target)));
|
|
349
|
+
}
|
|
350
|
+
return Array.from(new Map(edges.map((edge) => [`${edge.source}\0${edge.relation}\0${edge.target}`, edge])).values());
|
|
351
|
+
}
|
|
304
352
|
findEdgesBetween(projectId, leftNodeId, rightNodeId) {
|
|
305
353
|
const left = parseNodeId(leftNodeId, projectId);
|
|
306
354
|
const right = parseNodeId(rightNodeId, projectId);
|
|
@@ -725,6 +773,12 @@ function stringArray(value) {
|
|
|
725
773
|
function optionalMetadataString(value) {
|
|
726
774
|
return typeof value === 'string' && value ? value : undefined;
|
|
727
775
|
}
|
|
776
|
+
function chunked(values, size) {
|
|
777
|
+
const chunks = [];
|
|
778
|
+
for (let index = 0; index < values.length; index += size)
|
|
779
|
+
chunks.push(values.slice(index, index + size));
|
|
780
|
+
return chunks;
|
|
781
|
+
}
|
|
728
782
|
function facetFromEdge(edge, projectId) {
|
|
729
783
|
return {
|
|
730
784
|
type: edge.target_type,
|
|
@@ -106,9 +106,11 @@ export interface GraphEdgeRecordLike {
|
|
|
106
106
|
status?: string;
|
|
107
107
|
}
|
|
108
108
|
export interface GraphEdgeStoreLike {
|
|
109
|
+
getDatabase(): import('bun:sqlite').default;
|
|
109
110
|
list?(options?: unknown): GraphEdgeRecordLike[];
|
|
110
111
|
listActiveNeighborEdges?(entityIds: string[], edgeTypes?: string[], limit?: number): GraphEdgeRecordLike[];
|
|
111
|
-
appendEdge
|
|
112
|
+
appendEdge(input: unknown): GraphEdgeRecordLike;
|
|
113
|
+
invalidateEdge(edgeRecordId: string, metadata?: Record<string, unknown>): boolean;
|
|
112
114
|
}
|
|
113
115
|
export interface ProposalLedgerLike {
|
|
114
116
|
append?(items: unknown[]): void;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -277,6 +277,7 @@ export interface MemoryEvent<TPayload = Record<string, unknown>> {
|
|
|
277
277
|
threadId?: string;
|
|
278
278
|
sessionId?: string;
|
|
279
279
|
localDate?: string;
|
|
280
|
+
localDateSource?: 'explicit' | 'generated_utc' | 'legacy_unknown';
|
|
280
281
|
threadSeq?: number;
|
|
281
282
|
turnId?: string;
|
|
282
283
|
turnSeq?: number;
|
|
@@ -227,7 +227,7 @@ Hermes has no automatic observation path in this integration. Use `cogmem_episod
|
|
|
227
227
|
|
|
228
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.
|
|
229
229
|
|
|
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`; it scans the historical ledger by cursor and can resume with `--since <globalSeq>`. For old-discussion questions, use `cogmem memory recall --intent historical_discussion ...` or `cogmem_graph_explore`; in 3.7.
|
|
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`; it scans the historical ledger by cursor and can resume with `--since <globalSeq>`. For old-discussion questions, use `cogmem memory recall --intent historical_discussion ...` or `cogmem_graph_explore`; in 3.7.2 cards carry `canonicalId`, `matchedFacets`, `matchedPaths`, `relatedButNotSelected`, and `sourceLocator`. Answer from the selected canonical card, not from a side card. 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`.
|
|
231
231
|
|
|
232
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.
|
|
233
233
|
|
|
@@ -109,7 +109,7 @@ Dream stores explicit user clarification as organizational correction evidence r
|
|
|
109
109
|
|
|
110
110
|
After upgrades, reload MCP. Rerun `cogmem connect hermes --workspace . --auto --force --json` when MCP wiring, allow-listed tools, or the installed skill bundle changed. Follow only JSON `nextCommands` for unattended agent work; operator/host actions are in `nextSteps` with `safeForAutomation=false`.
|
|
111
111
|
|
|
112
|
-
Cogmem 3.7.
|
|
112
|
+
Cogmem 3.7.2 exposes read-only/idempotent Memory Atlas query tools plus explicit `cogmem_graph_touch`, installs from npm by default, prevents empty imported episodes from blocking Dream, and adds an agent-safe operations protocol. Use `memory plan` for the next safe command, grouped `memory candidates --json` for queue state, `historical_discussion` recall for old-discussion questions, and returned `sourceLocator` commands for exact evidence. Only run `dream_tick` when it appears in `memory plan.nextActions`; `raw_dream_ledger_lag` in `nonBlocking` has `resolvableByDreamTick:false` and is not fixed by looping `dream tick`. Use explore for broad memory inventory/history, search and node for a known concept, path/neighbors for relations, timeline for ordered reconstruction, and normal recall for a direct fact. Query facets combine the user's actual project, day/month/year, topic, issue, entity/target, session/thread, memory-kind, action-kind, and keyword conditions like table filters, so cold memory can be revived without requiring an entity-time-action tuple. Unicode entity cues are supported, but alias merges still require governed evidence. Prefer returned `cards[]` for historical discussion: `canonicalId` dedupes the episode, `matchedFacets` explains why it matched, `relatedButNotSelected` is only side context, `relaxationTrace` marks day/month/year or issue/topic fallback, and `sourceLocator` opens raw evidence. Touch only nodes actually used, and follow returned event IDs or `sourceLocator` commands before quoting exact wording.
|
|
113
113
|
|
|
114
114
|
## Runtime
|
|
115
115
|
|
|
@@ -114,7 +114,7 @@ timeout_ms = 60000
|
|
|
114
114
|
|
|
115
115
|
## Migrate Existing Hermes Memory
|
|
116
116
|
|
|
117
|
-
Upgrade a 3.5.2 database, an existing 3.6.x database, or a pre-release schema-25 test database to the current 3.7.
|
|
117
|
+
Upgrade a 3.5.2 database, an existing 3.6.x database, or a pre-release schema-25 test database to the current 3.7.2 schema/projection set in one backed-up command:
|
|
118
118
|
|
|
119
119
|
```bash
|
|
120
120
|
cogmem migrate --yes --backup --json
|
|
@@ -216,7 +216,7 @@ Choose the graph tool from the question shape:
|
|
|
216
216
|
|
|
217
217
|
Atlas combines whatever conditions the user supplies like table filters. Do not require entity + time + action. Project, day/month/year, topic, issue, entity/target, session/thread, memory kind, action kind, and ordinary cues may revive cold nodes together. Entity cue extraction accepts Unicode letter/number names, but it is not full NER and must not be treated as proof that two aliases are the same person/tool. Activation is visibility, not truth. Atlas summaries are hints and never replace raw evidence.
|
|
218
218
|
|
|
219
|
-
In 3.7.
|
|
219
|
+
In 3.7.2, `cogmem_graph_search`, `cogmem_graph_explore`, and historical recall can return canonical `cards[]`. Prefer cards over raw node labels for past-discussion questions. A card's `canonicalId` is the single episode identity; `matchedFacets` explains time/topic/issue/entity matches; `matchedPaths` explains how the card was reached; `relatedButNotSelected` is context only and must not replace the selected answer; `sourceLocator.command` is the command to inspect exact original text. If `relaxationTrace` exists, say the answer is a relaxed match, not an exact hit; supported relaxations include day → month → year and issue → parent topic.
|
|
220
220
|
|
|
221
221
|
When the prompt does not contain enough injected Cogmem context, do not search legacy memory files first. Ask Cogmem directly:
|
|
222
222
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Cogmem 3.7.
|
|
1
|
+
# Cogmem 3.7.2 Operations Reference for Hermes
|
|
2
2
|
|
|
3
3
|
Read this file when installing, upgrading, importing, repairing, or operating Cogmem. `SKILL.md` contains the decision rules; this file records the operational commands.
|
|
4
4
|
|
|
@@ -76,7 +76,7 @@ cogmem doctor
|
|
|
76
76
|
cogmem connect hermes --workspace . --auto --force --json
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
-
The backed-up command upgrades 3.5.2 schema 24, an existing 3.6.x database, or a pre-release schema-25 test database to the current 3.7.
|
|
79
|
+
The backed-up command upgrades 3.5.2 schema 24, an existing 3.6.x database, or a pre-release schema-25 test database to the current 3.7.2 schema/projection state in one run and preserves Raw Ledger evidence. `--dry-run` is read-only and does not create `_schema_migrations`. Reload MCP after reconnecting.
|
|
80
80
|
|
|
81
81
|
## Import Hermes memory
|
|
82
82
|
|
|
@@ -185,7 +185,7 @@ Recall behavior:
|
|
|
185
185
|
- If `cogmem memory tick --project openclaw --json` suggests `bind_raw_events`, run `cogmem memory bind --project openclaw --json` to backfill imported or adapter-written raw user events into the binding graph.
|
|
186
186
|
- For “did we discuss this before?”, “几个月前是不是聊过…”, “记忆黑盒”, or missing injection cases, run `cogmem memory recall --query "<past discussion question>" --intent historical_discussion --project openclaw --agent openclaw --json` before claiming no memory.
|
|
187
187
|
- For ledger audits or resumable checks, use `cogmem memory list --project openclaw --since <globalSeq> --order asc --json`; list rows and Atlas search/explore evidence include `sourceLocator` commands.
|
|
188
|
-
- For old-discussion or "do you remember" questions, prefer `cogmem memory recall --intent historical_discussion ...` or `cogmem memory graph-explore ... --json`. In 3.7.
|
|
188
|
+
- For old-discussion or "do you remember" questions, prefer `cogmem memory recall --intent historical_discussion ...` or `cogmem memory graph-explore ... --json`. In 3.7.2, cards carry `canonicalId`, `matchedFacets`, `matchedPaths`, `relatedButNotSelected`, and `sourceLocator`; answer from the selected canonical card, not from a related-but-not-selected side card.
|
|
189
189
|
|
|
190
190
|
Installing the workspace skill makes the kernel procedure discoverable to OpenClaw agents. Installing the local auto wrapper makes future turns call the memory kernel automatically:
|
|
191
191
|
|
|
@@ -84,7 +84,7 @@ cogmem memory tick --project openclaw --json
|
|
|
84
84
|
cogmem memory bind --project openclaw --json
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
-
Cogmem 3.7.
|
|
87
|
+
Cogmem 3.7.2 keeps npm as the default install/update channel and upgrades Memory Atlas into a multi-dimensional navigation graph. Use `memory plan` for the next safe command, grouped `memory candidates --json` for queue state, `historical_discussion` recall for old-discussion questions, and returned `sourceLocator` commands for exact evidence. Only run `dream_tick` when it appears in `memory plan.nextActions`; `raw_dream_ledger_lag` in `nonBlocking` has `resolvableByDreamTick:false` and is not fixed by looping `dream tick`. The auto plugin uses one shared bridge/kernel lifecycle for graph exploration, evidence-bearing node/timeline drill-down, and recall, so OpenClaw does not need MCP for broad inventory/history questions. Atlas combines the query's actual project, day/month/year, topic, issue, entity/target, session/thread, memory-kind, action-kind, and keyword conditions like table filters; no fixed entity-time-action tuple is required. Unicode entity cues are supported, but alias merges still require governed evidence. Prefer returned `cards[]` for historical discussion: `canonicalId` dedupes the episode, `matchedFacets` explains why it matched, `relatedButNotSelected` is only side context, `relaxationTrace` marks day/month/year or issue/topic fallback, and `sourceLocator` opens the raw evidence.
|
|
88
88
|
|
|
89
89
|
```bash
|
|
90
90
|
cogmem memory plan --project openclaw --json
|
|
@@ -114,7 +114,7 @@ timeout_ms = 60000
|
|
|
114
114
|
|
|
115
115
|
## Migrate Existing OpenClaw Memory
|
|
116
116
|
|
|
117
|
-
Upgrade a 3.5.2 database, an existing 3.6.x database, or a pre-release schema-25 test database to the current 3.7.
|
|
117
|
+
Upgrade a 3.5.2 database, an existing 3.6.x database, or a pre-release schema-25 test database to the current 3.7.2 schema/projection set in one backed-up command:
|
|
118
118
|
|
|
119
119
|
```bash
|
|
120
120
|
cogmem migrate --yes --backup --json
|
|
@@ -282,7 +282,7 @@ Graph reads default to stale-safe diagnostics. If a drainer or gateway has SQLit
|
|
|
282
282
|
|
|
283
283
|
Atlas combines the conditions present in the user's message like table filters. Do not force every question into entity + time + action. Project, day/month/year, topic, issue, entity/target, session/thread, memory kind, action kind, and ordinary cues may independently or jointly revive cold nodes. Entity cue extraction accepts Unicode letter/number names, but it is not full NER and must not be treated as proof that two aliases are the same person/tool. Graph reads do not change activation; explicitly touch only nodes the agent actually uses. Activation changes visibility only.
|
|
284
284
|
|
|
285
|
-
In 3.7.
|
|
285
|
+
In 3.7.2, `graph-search`, `graph-explore`, and historical recall can return `cards[]`. Prefer cards over raw node labels for past-discussion questions. A card's `canonicalId` is the single episode identity; `matchedFacets` explains time/topic/issue/entity matches; `matchedPaths` explains how the card was reached; `relatedButNotSelected` is context only and must not replace the selected answer; `sourceLocator.command` is the command to inspect exact original text. If `relaxationTrace` exists, say the answer is a relaxed match, not an exact hit; supported relaxations include day → month → year and issue → parent topic.
|
|
286
286
|
|
|
287
287
|
Follow returned `sourceLocator.command` or event IDs with `cogmem memory show`; never treat an Atlas summary or title as evidence.
|
|
288
288
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Cogmem 3.7.
|
|
1
|
+
# Cogmem 3.7.2 Operations Reference for OpenClaw
|
|
2
2
|
|
|
3
3
|
Read this file when installing, upgrading, importing, repairing, or operating Cogmem. `SKILL.md` contains the decision rules; this file is the command reference.
|
|
4
4
|
|
|
@@ -86,7 +86,7 @@ openclaw gateway restart
|
|
|
86
86
|
cogmem openclaw diagnose --workspace . --json
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
-
The second command upgrades 3.5.2 schema 24, an existing 3.6.x database, or a pre-release schema-25 test database to the current 3.7.
|
|
89
|
+
The second command upgrades 3.5.2 schema 24, an existing 3.6.x database, or a pre-release schema-25 test database to the current 3.7.2 schema/projection state in one run. It preserves Raw Ledger evidence. Keep the returned `backupPath` until verification passes. `--dry-run` is read-only and does not create `_schema_migrations`.
|
|
90
90
|
|
|
91
91
|
After upgrading the package/database, refresh OpenClaw's generated plugin files. `doctor --plugin-only` avoids opening the Cogmem kernel, so it can repair stale `extensions/cogmem-auto-memory/index.js` and `bridge.mjs` even when an old drainer has SQLite busy. Use `connect --auto --force` when intentionally reinstalling the full integration and patching OpenClaw config:
|
|
92
92
|
|