cogmem 3.7.0 → 3.7.2
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 +18 -0
- package/MEMORY_ATLAS.md +16 -6
- package/README.md +16 -11
- package/RELEASE_CHECKLIST.md +4 -4
- package/dist/agent/AgentMemoryBackend.d.ts +11 -2
- package/dist/agent/AgentMemoryBackend.js +185 -36
- package/dist/agent/AgentRecallQueryCompiler.d.ts +1 -1
- package/dist/agent/AgentRecallQueryCompiler.js +13 -1
- package/dist/agent/MemoryUsageReceipt.js +8 -10
- package/dist/agent/SessionWorkingState.js +3 -2
- package/dist/agent/UntrustedMemorySerializer.d.ts +2 -0
- package/dist/agent/UntrustedMemorySerializer.js +14 -0
- package/dist/atlas/ActionFrameExtractor.js +2 -0
- package/dist/atlas/EpisodeTitleGenerator.js +42 -3
- package/dist/atlas/FacetQueryPlanner.d.ts +2 -0
- package/dist/atlas/FacetQueryPlanner.js +57 -16
- package/dist/atlas/GraphCurator.d.ts +3 -0
- package/dist/atlas/GraphCurator.js +118 -14
- package/dist/atlas/MemoryAtlasIndexer.d.ts +12 -0
- package/dist/atlas/MemoryAtlasIndexer.js +36 -0
- package/dist/atlas/MemoryAtlasQueryCompiler.js +10 -7
- package/dist/atlas/MemoryAtlasService.d.ts +1 -0
- package/dist/atlas/MemoryAtlasService.js +102 -26
- package/dist/atlas/MemoryAtlasTypes.d.ts +7 -0
- package/dist/bin/memory.js +14 -4
- package/dist/factory.d.ts +18 -0
- package/dist/factory.js +41 -2
- package/dist/host/openclaw/AutoMemoryPluginInstaller.js +71 -44
- package/dist/mcp/server.js +1 -1
- package/dist/recall/RecallExplanation.d.ts +1 -1
- package/dist/store/MemoryAtlasStore.d.ts +4 -1
- package/dist/store/MemoryAtlasStore.js +203 -25
- package/dist/utils/ActionKindRegistry.d.ts +10 -0
- package/dist/utils/ActionKindRegistry.js +18 -0
- package/dist/utils/EntityCueExtractor.d.ts +13 -0
- package/dist/utils/EntityCueExtractor.js +81 -0
- package/examples/hermes-backend/AGENTS.md +1 -1
- package/examples/hermes-backend/README.md +1 -1
- package/examples/hermes-backend/SKILL.md +10 -3
- package/examples/hermes-backend/references/operations.md +9 -8
- package/examples/openclaw-backend/AGENTS.md +1 -1
- package/examples/openclaw-backend/README.md +2 -2
- package/examples/openclaw-backend/SKILL.md +24 -7
- package/examples/openclaw-backend/references/operations.md +26 -10
- package/package.json +1 -1
package/dist/bin/memory.js
CHANGED
|
@@ -28,6 +28,7 @@ function readArgs(argv) {
|
|
|
28
28
|
command,
|
|
29
29
|
query: stringArg(values, 'query') || stringArg(values, 'q'),
|
|
30
30
|
eventId: stringArg(values, 'event') || stringArg(values, 'event-id'),
|
|
31
|
+
episodeId: stringArg(values, 'episode') || stringArg(values, 'episode-id'),
|
|
31
32
|
nodeId: stringArg(values, 'id') || stringArg(values, 'node-id'),
|
|
32
33
|
fromId: stringArg(values, 'from'),
|
|
33
34
|
toId: stringArg(values, 'to'),
|
|
@@ -96,6 +97,7 @@ function usage() {
|
|
|
96
97
|
' graph-neighbors expand --id by --hops 1..2',
|
|
97
98
|
' graph-path find a bounded path from --from to --to',
|
|
98
99
|
' graph-timeline reconstruct entity/time/action history for --query',
|
|
100
|
+
' graph-reindex reproject one Atlas episode/card by --event or --episode without rebuilding the whole graph',
|
|
99
101
|
'',
|
|
100
102
|
'Common options:',
|
|
101
103
|
' --project <id> scope to one project',
|
|
@@ -122,7 +124,7 @@ function usage() {
|
|
|
122
124
|
' --interval-ms <n> watch sleep interval, default 300000',
|
|
123
125
|
' --max-runs <n> stop watch after n iterations; omit for long-running worker',
|
|
124
126
|
' --agent <id> agent id for governed recall, default openclaw',
|
|
125
|
-
' --intent <intent> memory_recall, previous_session_summary, forensic_quote, or
|
|
127
|
+
' --intent <intent> memory_recall, previous_session_summary, forensic_quote, historical_discussion, or action_history',
|
|
126
128
|
' --db <memory.db> open an explicit database path',
|
|
127
129
|
' --config <toml> open a cogmem TOML config',
|
|
128
130
|
' --include-evidence include bounded raw excerpts; event ids are always returned',
|
|
@@ -158,7 +160,8 @@ function isMemoryCommand(value) {
|
|
|
158
160
|
|| value === 'graph-node'
|
|
159
161
|
|| value === 'graph-neighbors'
|
|
160
162
|
|| value === 'graph-path'
|
|
161
|
-
|| value === 'graph-timeline'
|
|
163
|
+
|| value === 'graph-timeline'
|
|
164
|
+
|| value === 'graph-reindex';
|
|
162
165
|
}
|
|
163
166
|
function reviewActionArg(values, key) {
|
|
164
167
|
const raw = stringArg(values, key);
|
|
@@ -172,9 +175,9 @@ function recallIntentArg(values, key) {
|
|
|
172
175
|
const raw = stringArg(values, key);
|
|
173
176
|
if (!raw)
|
|
174
177
|
return undefined;
|
|
175
|
-
if (raw === 'memory_recall' || raw === 'previous_session_summary' || raw === 'forensic_quote' || raw === 'historical_discussion')
|
|
178
|
+
if (raw === 'memory_recall' || raw === 'previous_session_summary' || raw === 'forensic_quote' || raw === 'historical_discussion' || raw === 'action_history')
|
|
176
179
|
return raw;
|
|
177
|
-
throw new Error(`--${key} must be one of memory_recall, previous_session_summary, forensic_quote, historical_discussion`);
|
|
180
|
+
throw new Error(`--${key} must be one of memory_recall, previous_session_summary, forensic_quote, historical_discussion, action_history`);
|
|
178
181
|
}
|
|
179
182
|
function orderArg(values, key) {
|
|
180
183
|
const raw = stringArg(values, key);
|
|
@@ -603,6 +606,13 @@ function runGraphCommand(kernel, args) {
|
|
|
603
606
|
evidenceLimit: args.evidenceLimit, now: args.now,
|
|
604
607
|
refresh: args.refresh ? true : (args.staleOk ? false : undefined),
|
|
605
608
|
staleOk: args.staleOk || !args.refresh };
|
|
609
|
+
if (args.command === 'graph-reindex') {
|
|
610
|
+
if (!args.eventId && !args.episodeId)
|
|
611
|
+
throw new Error(`graph-reindex requires --event or --episode.\n${usage()}`);
|
|
612
|
+
if (args.eventId && args.episodeId)
|
|
613
|
+
throw new Error(`graph-reindex accepts exactly one of --event or --episode.\n${usage()}`);
|
|
614
|
+
return kernel.reindexMemoryAtlas({ projectId, eventId: args.eventId, episodeId: args.episodeId });
|
|
615
|
+
}
|
|
606
616
|
if (args.command === 'graph')
|
|
607
617
|
return kernel.graphOverview(options);
|
|
608
618
|
if (args.command === 'graph-search') {
|
package/dist/factory.d.ts
CHANGED
|
@@ -326,9 +326,15 @@ export type EpisodeRepairInput = {
|
|
|
326
326
|
};
|
|
327
327
|
export interface EpisodeRepairResult {
|
|
328
328
|
repairId: string;
|
|
329
|
+
applied: boolean;
|
|
329
330
|
operation: EpisodeRepairInput['operation'];
|
|
330
331
|
affectedEpisodeIds: string[];
|
|
332
|
+
changedFields: string[];
|
|
331
333
|
staleCandidateIds: string[];
|
|
334
|
+
requeuedDream: boolean;
|
|
335
|
+
graphRefreshNeeded: boolean;
|
|
336
|
+
nextCommands: string[];
|
|
337
|
+
note: string;
|
|
332
338
|
}
|
|
333
339
|
export interface ToolCallMemoryEventInput {
|
|
334
340
|
projectId?: string;
|
|
@@ -635,6 +641,18 @@ export declare class MemoryKernel {
|
|
|
635
641
|
documents: number;
|
|
636
642
|
actions: number;
|
|
637
643
|
};
|
|
644
|
+
reindexMemoryAtlas(options: {
|
|
645
|
+
projectId: string;
|
|
646
|
+
eventId?: string;
|
|
647
|
+
episodeId?: string;
|
|
648
|
+
}): {
|
|
649
|
+
projectId: string;
|
|
650
|
+
episodeIds: string[];
|
|
651
|
+
refreshed: boolean;
|
|
652
|
+
curatedEpisodes: number;
|
|
653
|
+
facetEdges: number;
|
|
654
|
+
reviewNeeded: number;
|
|
655
|
+
};
|
|
638
656
|
ensureMemoryAtlas(options: {
|
|
639
657
|
projectId: string;
|
|
640
658
|
}): {
|
package/dist/factory.js
CHANGED
|
@@ -83,7 +83,7 @@ import { SqliteVecStore } from './store/SqliteVecStore.js';
|
|
|
83
83
|
import { VectorStore } from './store/VectorStore.js';
|
|
84
84
|
import { config } from './utils/Config.js';
|
|
85
85
|
import { KernelRunningError, SnapshotExporter, SnapshotImporter, } from './snapshot/index.js';
|
|
86
|
-
const CORE_VERSION = '3.7.
|
|
86
|
+
const CORE_VERSION = '3.7.2';
|
|
87
87
|
const LATEST_SCHEMA_VERSION = 27;
|
|
88
88
|
export class MemoryKernel {
|
|
89
89
|
options;
|
|
@@ -1056,6 +1056,7 @@ export class MemoryKernel {
|
|
|
1056
1056
|
const affected = new Set();
|
|
1057
1057
|
const before = {};
|
|
1058
1058
|
const previousStatuses = new Map();
|
|
1059
|
+
let requeuedDream = input.operation === 'requeue-dream' || input.operation === 'invalidate-dream-run';
|
|
1059
1060
|
if (input.operation === 'move-event') {
|
|
1060
1061
|
const link = this.episodeStore.getEventLink(input.eventId);
|
|
1061
1062
|
const source = link ? this.episodeStore.getEpisode(link.episodeId) : undefined;
|
|
@@ -1169,11 +1170,30 @@ export class MemoryKernel {
|
|
|
1169
1170
|
const episode = this.episodeStore.getEpisode(episodeId);
|
|
1170
1171
|
if (episode?.eventCount && episode.status === 'sealed') {
|
|
1171
1172
|
this.episodeStore.requeueDreamForRepair(episodeId, input.operation === 'invalidate-dream-run' ? input.mode || 'normal' : 'normal', now);
|
|
1173
|
+
requeuedDream = true;
|
|
1172
1174
|
}
|
|
1173
1175
|
}
|
|
1174
1176
|
const after = Object.fromEntries([...affected].map((episodeId) => [episodeId, this.episodeStore.getEpisode(episodeId)]));
|
|
1175
1177
|
const repairId = this.episodeStore.recordRepairAudit({ projectId: input.projectId, operation: input.operation, payload: input, before, after, now });
|
|
1176
|
-
|
|
1178
|
+
const affectedEpisodeIds = [...affected];
|
|
1179
|
+
const nextCommands = affectedEpisodeIds.length
|
|
1180
|
+
? affectedEpisodeIds.map((episodeId) => `cogmem memory graph-reindex --project ${input.projectId} --episode ${episodeId} --json`)
|
|
1181
|
+
: [];
|
|
1182
|
+
return {
|
|
1183
|
+
repairId,
|
|
1184
|
+
applied: true,
|
|
1185
|
+
operation: input.operation,
|
|
1186
|
+
affectedEpisodeIds,
|
|
1187
|
+
changedFields: changedFieldsForRepair(input),
|
|
1188
|
+
staleCandidateIds,
|
|
1189
|
+
requeuedDream,
|
|
1190
|
+
graphRefreshNeeded: affectedEpisodeIds.length > 0,
|
|
1191
|
+
nextCommands: [
|
|
1192
|
+
...nextCommands,
|
|
1193
|
+
`cogmem memory graph-explore --project ${input.projectId} --query "<query>" --json`,
|
|
1194
|
+
],
|
|
1195
|
+
note: 'repairId is an audit id, not a dream candidate id; do not run memory dream --promote just because repairId exists.',
|
|
1196
|
+
};
|
|
1177
1197
|
}
|
|
1178
1198
|
listDreamCandidates(options = {}) {
|
|
1179
1199
|
return this.deepWriteCandidateStore.listCandidates(options);
|
|
@@ -1265,6 +1285,9 @@ export class MemoryKernel {
|
|
|
1265
1285
|
rebuildMemoryAtlas(options = {}) {
|
|
1266
1286
|
return this.memoryAtlasIndexer.rebuild(options);
|
|
1267
1287
|
}
|
|
1288
|
+
reindexMemoryAtlas(options) {
|
|
1289
|
+
return this.memoryAtlasIndexer.reindex(options);
|
|
1290
|
+
}
|
|
1268
1291
|
ensureMemoryAtlas(options) {
|
|
1269
1292
|
return this.memoryAtlasIndexer.ensureFresh(options);
|
|
1270
1293
|
}
|
|
@@ -2002,6 +2025,22 @@ export function createMemoryKernelFromConfig(input = {}) {
|
|
|
2002
2025
|
const { configPath: _configPath, cwd: _cwd, env: _env, ...explicitOptions } = options;
|
|
2003
2026
|
return createMemoryKernel({ ...loaded.options, ...explicitOptions });
|
|
2004
2027
|
}
|
|
2028
|
+
function changedFieldsForRepair(input) {
|
|
2029
|
+
if (input.operation === 'reclassify') {
|
|
2030
|
+
return [
|
|
2031
|
+
input.episodeType ? 'episodeType' : '',
|
|
2032
|
+
input.topicPath ? 'topicPath' : '',
|
|
2033
|
+
input.importance !== undefined ? 'importance' : '',
|
|
2034
|
+
].filter(Boolean);
|
|
2035
|
+
}
|
|
2036
|
+
if (input.operation === 'move-event')
|
|
2037
|
+
return ['episodeEvents'];
|
|
2038
|
+
if (input.operation === 'split' || input.operation === 'merge')
|
|
2039
|
+
return ['episodeEvents', 'crossRefs'];
|
|
2040
|
+
if (input.operation === 'requeue-dream' || input.operation === 'invalidate-dream-run')
|
|
2041
|
+
return ['dreamQueue'];
|
|
2042
|
+
return [];
|
|
2043
|
+
}
|
|
2005
2044
|
function requiredGovernancePayloadString(payload, field) {
|
|
2006
2045
|
const value = payload[field];
|
|
2007
2046
|
if (typeof value !== 'string' || value.trim() === '')
|
|
@@ -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.7.
|
|
8
|
+
const PLUGIN_VERSION = '0.7.1';
|
|
9
9
|
function defaultPublicEntrypoint() {
|
|
10
10
|
return join(resolve(dirname(fileURLToPath(import.meta.url)), '../..'), 'public.js');
|
|
11
11
|
}
|
|
@@ -516,15 +516,29 @@ function createMemoryUsageReceipt(input) {
|
|
|
516
516
|
|
|
517
517
|
function formatSourceAnchor(anchor) {
|
|
518
518
|
return [
|
|
519
|
-
anchor.memoryId ? 'memory:' + anchor.memoryId : '',
|
|
520
|
-
anchor.eventId ? 'event:' + anchor.eventId : '',
|
|
521
|
-
anchor.sessionId ? 'session:' + anchor.sessionId : '',
|
|
522
|
-
anchor.role ? 'role:' + anchor.role : '',
|
|
519
|
+
anchor.memoryId ? 'memory:' + serializeUntrustedMemory(anchor.memoryId, 160) : '',
|
|
520
|
+
anchor.eventId ? 'event:' + serializeUntrustedMemory(anchor.eventId, 160) : '',
|
|
521
|
+
anchor.sessionId ? 'session:' + serializeUntrustedMemory(anchor.sessionId, 160) : '',
|
|
522
|
+
anchor.role ? 'role:' + serializeUntrustedMemory(anchor.role, 80) : '',
|
|
523
523
|
].filter(Boolean).join('; ');
|
|
524
524
|
}
|
|
525
525
|
|
|
526
526
|
function listLines(values) {
|
|
527
|
-
return values.length ? values.map((value) => '- ' + value) : ['- none'];
|
|
527
|
+
return values.length ? values.map((value) => '- ' + serializeUntrustedMemory(value, 260)) : ['- none'];
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
function serializeUntrustedMemory(input, limit) {
|
|
531
|
+
const clean = stripCogmemRecallBlocks(String(input || '')).text
|
|
532
|
+
.replace(new RegExp('</?COGMEM_[A-Z0-9_:-]+[^>]*>', 'gi'), '')
|
|
533
|
+
.replace(new RegExp('[\\\\u0000-\\\\u0008\\\\u000b\\\\u000c\\\\u000e-\\\\u001f\\\\u007f]', 'g'), ' ')
|
|
534
|
+
.replace(new RegExp('\\\\s+', 'g'), ' ')
|
|
535
|
+
.trim()
|
|
536
|
+
.replace(/&/g, '&')
|
|
537
|
+
.replace(/</g, '<')
|
|
538
|
+
.replace(/>/g, '>')
|
|
539
|
+
.replace(/"/g, '"')
|
|
540
|
+
.replace(/'/g, ''');
|
|
541
|
+
return clean.slice(0, Math.max(0, Number(limit || 500)));
|
|
528
542
|
}
|
|
529
543
|
|
|
530
544
|
function clampBlock(text, closingTag, maxChars) {
|
|
@@ -535,14 +549,14 @@ function clampBlock(text, closingTag, maxChars) {
|
|
|
535
549
|
|
|
536
550
|
function formatMemoryUsageBridge(receipt, maxChars) {
|
|
537
551
|
const lines = [
|
|
538
|
-
'<COGMEM_TURN_BRIDGE turn_id="' +
|
|
552
|
+
'<COGMEM_TURN_BRIDGE turn_id="' + serializeUntrustedMemory(receipt.turnId, 180) + '" source="cogmem" compact="true" ttl_turns="' + receipt.ttlTurns + '" compile_allowed="false">',
|
|
539
553
|
'Previous assistant answer used Cogmem memory.',
|
|
540
554
|
'',
|
|
541
555
|
'Used memory themes:',
|
|
542
556
|
...listLines(receipt.usedThemes || []),
|
|
543
557
|
'',
|
|
544
558
|
'Working conclusion produced in that turn:',
|
|
545
|
-
'- ' + (receipt.workingConclusion || 'No compact conclusion recorded.'),
|
|
559
|
+
'- ' + serializeUntrustedMemory(receipt.workingConclusion || 'No compact conclusion recorded.', 260),
|
|
546
560
|
'',
|
|
547
561
|
'Source anchors:',
|
|
548
562
|
...listLines((receipt.sourceAnchors || []).map(formatSourceAnchor)),
|
|
@@ -608,7 +622,7 @@ function formatSessionWorkingState(state, maxChars) {
|
|
|
608
622
|
const lines = [
|
|
609
623
|
'<COGMEM_SESSION_STATE scope="current_session" compact="true" persistence="session_only" compile_allowed="false">',
|
|
610
624
|
'Current working topic:',
|
|
611
|
-
'- ' + (state.currentTopic || 'unspecified'),
|
|
625
|
+
'- ' + serializeUntrustedMemory(state.currentTopic || 'unspecified', 180),
|
|
612
626
|
'',
|
|
613
627
|
'Current design direction:',
|
|
614
628
|
...listLines(state.designDirection || []),
|
|
@@ -663,6 +677,9 @@ function classifyRecallIntent(query) {
|
|
|
663
677
|
if (/原话|怎么说的|完整对话|上一句|下一句|exact quote|verbatim/.test(text)) {
|
|
664
678
|
return 'forensic_quote';
|
|
665
679
|
}
|
|
680
|
+
if (/(让你.{0,20}(对|给|把)?.{0,20}(做过|做了|启动|安装|配置|修改|重启|停止|操作|处理|执行)|对.{0,30}(做过什么|做了什么|哪些操作)|what did (i ask you to do|you do) to|operations? on)/i.test(text)) {
|
|
681
|
+
return 'action_history';
|
|
682
|
+
}
|
|
666
683
|
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
684
|
return 'historical_discussion';
|
|
668
685
|
}
|
|
@@ -1314,7 +1331,7 @@ async function recallPayload(input, config, kernel, memory, formatStrategyContex
|
|
|
1314
1331
|
let result = await memory.recall({
|
|
1315
1332
|
agentId: config.agentId || 'openclaw', projectId: config.projectId || 'openclaw',
|
|
1316
1333
|
query: input.query || '', sessionId: input.sessionId, threadId: input.threadId,
|
|
1317
|
-
excludeSessionId: input.excludeSessionId, intent: input.intent ||
|
|
1334
|
+
excludeSessionId: input.excludeSessionId, intent: input.intent || undefined,
|
|
1318
1335
|
anchorEventId: input.anchorEventId, anchorText: input.anchorText,
|
|
1319
1336
|
limit: Number(config.limit || 3), retrievalPolicy: strategyCapsule && strategyCapsule.retrievalPolicy,
|
|
1320
1337
|
});
|
|
@@ -1327,7 +1344,7 @@ async function recallPayload(input, config, kernel, memory, formatStrategyContex
|
|
|
1327
1344
|
result = await memory.recall({
|
|
1328
1345
|
agentId: config.agentId || 'openclaw', projectId: config.projectId || 'openclaw',
|
|
1329
1346
|
query: input.query || '', sessionId: input.sessionId, threadId: input.threadId,
|
|
1330
|
-
excludeSessionId: input.excludeSessionId, intent: input.intent ||
|
|
1347
|
+
excludeSessionId: input.excludeSessionId, intent: input.intent || undefined,
|
|
1331
1348
|
anchorEventId: input.anchorEventId, anchorText: input.anchorText,
|
|
1332
1349
|
limit: Number(config.limit || 3), retrievalPolicy: strategyCapsule && strategyCapsule.retrievalPolicy,
|
|
1333
1350
|
});
|
|
@@ -1368,7 +1385,7 @@ async function recallPayload(input, config, kernel, memory, formatStrategyContex
|
|
|
1368
1385
|
return {
|
|
1369
1386
|
context: recallContext ? (strategyCapsule ? formatStrategyContext(strategyCapsule) + '\n\n' : '') + recallContext : '',
|
|
1370
1387
|
items: compactRecallItems(plannedResult.items, config), itemCount: plannedResult.items.length,
|
|
1371
|
-
recallMode: result.recallMode, fallbackUsed: result.fallbackUsed, intent: input.intent || 'memory_recall',
|
|
1388
|
+
recallMode: result.recallMode, fallbackUsed: result.fallbackUsed, intent: (result.queryPlan && result.queryPlan.intent) || input.intent || 'memory_recall',
|
|
1372
1389
|
anchorEventId: anchorItem && anchorItem.sourceAnchor && anchorItem.sourceAnchor.eventId,
|
|
1373
1390
|
anchorText: anchorItem && anchorItem.text, queryPlan: result.queryPlan, decisionTrace: result.decisionTrace,
|
|
1374
1391
|
atlasCards: result.atlasCards, selectedEpisodeCards,
|
|
@@ -1470,10 +1487,20 @@ function formatAtlasContext(result, maxChars) {
|
|
|
1470
1487
|
}
|
|
1471
1488
|
|
|
1472
1489
|
function safeAtlasText(input, limit) {
|
|
1490
|
+
return serializeUntrustedMemory(input, limit);
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
function serializeUntrustedMemory(input, limit) {
|
|
1473
1494
|
const clean = stripCogmemRecallBlocks(String(input || '')).text
|
|
1474
|
-
.replace(
|
|
1495
|
+
.replace(/<\/?COGMEM_[A-Z0-9_:-]+[^>]*>/gi, '')
|
|
1496
|
+
.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, ' ')
|
|
1475
1497
|
.replace(/\s+/g, ' ')
|
|
1476
|
-
.trim()
|
|
1498
|
+
.trim()
|
|
1499
|
+
.replace(/&/g, '&')
|
|
1500
|
+
.replace(/</g, '<')
|
|
1501
|
+
.replace(/>/g, '>')
|
|
1502
|
+
.replace(/"/g, '"')
|
|
1503
|
+
.replace(/'/g, ''');
|
|
1477
1504
|
return clean.slice(0, Math.max(0, Number(limit || 500)));
|
|
1478
1505
|
}
|
|
1479
1506
|
|
|
@@ -1773,43 +1800,43 @@ function formatRecallContext(result, config) {
|
|
|
1773
1800
|
lines.push('selectedMemoryCards=' + result.atlasCards.slice(0, Number(config.memoryContextMaxItems || config.limit || 3)).map(formatAtlasCard).join(' | '));
|
|
1774
1801
|
}
|
|
1775
1802
|
if (Array.isArray(result.relaxationTrace) && result.relaxationTrace.length) {
|
|
1776
|
-
lines.push('relaxationTrace=' + result.relaxationTrace.map((step) => step.from + ' -> ' + step.to + ' (' + step.reason + ')').join(' | '));
|
|
1803
|
+
lines.push('relaxationTrace=' + result.relaxationTrace.map((step) => serializeUntrustedMemory(step.from, 80) + ' -> ' + serializeUntrustedMemory(step.to, 80) + ' (' + serializeUntrustedMemory(step.reason, 160) + ')').join(' | '));
|
|
1777
1804
|
}
|
|
1778
1805
|
if (Array.isArray(result.relatedButNotSelected) && result.relatedButNotSelected.length) {
|
|
1779
|
-
lines.push('relatedButNotSelected=' + result.relatedButNotSelected.slice(0, 4).map((item) => item.displayTitle + ' [' + item.reason + ']').join(' | '));
|
|
1806
|
+
lines.push('relatedButNotSelected=' + result.relatedButNotSelected.slice(0, 4).map((item) => serializeUntrustedMemory(item.displayTitle, 160) + ' [' + serializeUntrustedMemory(item.reason, 160) + ']').join(' | '));
|
|
1780
1807
|
}
|
|
1781
1808
|
lines.push('');
|
|
1782
1809
|
if (result.narrative && result.narrative.summary) {
|
|
1783
|
-
lines.push(result.narrative.summary);
|
|
1810
|
+
lines.push(serializeUntrustedMemory(result.narrative.summary, 800));
|
|
1784
1811
|
}
|
|
1785
1812
|
const maxItems = Number(config.memoryContextMaxItems || config.limit || 3);
|
|
1786
1813
|
const sourceWindowMaxChars = Number(config.sourceWindowMaxChars || 1200);
|
|
1787
1814
|
const includeSourceWindow = config.includeSourceWindowByDefault === true;
|
|
1788
1815
|
for (const item of result.items.slice(0, maxItems)) {
|
|
1789
|
-
const source = item.source ? ' [' + item.source + ']' : '';
|
|
1790
|
-
lines.push('- ' + item.text + source);
|
|
1816
|
+
const source = item.source ? ' [' + serializeUntrustedMemory(item.source, 220) + ']' : '';
|
|
1817
|
+
lines.push('- ' + serializeUntrustedMemory(item.text, 500) + source);
|
|
1791
1818
|
const sourceType = item.sourceType || 'compiled_memory';
|
|
1792
1819
|
const quote = item.canAnswerExactQuote === true ? 'true' : 'false';
|
|
1793
1820
|
const confidence = Number.isFinite(item.confidence) ? String(item.confidence) : 'unknown';
|
|
1794
|
-
const anchor = item.sourceAnchor ? '; anchorEvent=' + (item.sourceAnchor.eventId || 'unknown')
|
|
1795
|
-
+ (item.sourceAnchor.sessionId ? '; session=' + item.sourceAnchor.sessionId : '')
|
|
1796
|
-
+ (item.sourceAnchor.role ? '; role=' + item.sourceAnchor.role : '') : '';
|
|
1797
|
-
const why = item.whyMatched ? '; whyMatched=' + item.whyMatched : '';
|
|
1798
|
-
const canonical = item.canonicalId ? '; canonicalId=' + item.canonicalId : '';
|
|
1821
|
+
const anchor = item.sourceAnchor ? '; anchorEvent=' + serializeUntrustedMemory(item.sourceAnchor.eventId || 'unknown', 160)
|
|
1822
|
+
+ (item.sourceAnchor.sessionId ? '; session=' + serializeUntrustedMemory(item.sourceAnchor.sessionId, 160) : '')
|
|
1823
|
+
+ (item.sourceAnchor.role ? '; role=' + serializeUntrustedMemory(item.sourceAnchor.role, 80) : '') : '';
|
|
1824
|
+
const why = item.whyMatched ? '; whyMatched=' + serializeUntrustedMemory(item.whyMatched, 180) : '';
|
|
1825
|
+
const canonical = item.canonicalId ? '; canonicalId=' + serializeUntrustedMemory(item.canonicalId, 180) : '';
|
|
1799
1826
|
const matchedFacets = Array.isArray(item.matchedFacets) && item.matchedFacets.length
|
|
1800
|
-
? '; matchedFacets=' + item.matchedFacets.map((facet) => facet.type + ':' + facet.value).join(',')
|
|
1827
|
+
? '; matchedFacets=' + item.matchedFacets.map((facet) => serializeUntrustedMemory(facet.type, 80) + ':' + serializeUntrustedMemory(facet.value, 160)).join(',')
|
|
1801
1828
|
: '';
|
|
1802
|
-
lines.push(' sourceType=' + sourceType + '; confidence=' + confidence + '; canAnswerExactQuote=' + quote + canonical + matchedFacets + anchor + why);
|
|
1829
|
+
lines.push(' sourceType=' + serializeUntrustedMemory(sourceType, 120) + '; confidence=' + confidence + '; canAnswerExactQuote=' + quote + canonical + matchedFacets + anchor + why);
|
|
1803
1830
|
if (item.sourceContext && item.sourceContext.event) {
|
|
1804
1831
|
const anchorEvent = item.sourceContext.event;
|
|
1805
1832
|
const anchorFormatted = formatContextEvent(anchorEvent, Math.min(220, sourceWindowMaxChars));
|
|
1806
1833
|
lines.push(' sourceContext=' + anchorFormatted.line);
|
|
1807
1834
|
lines.push(' sourceWindow=' + formatSourceWindow(item.sourceContext.window, item.sourceContext));
|
|
1808
1835
|
if (item.sourceContext.locator && item.sourceContext.locator.command) {
|
|
1809
|
-
lines.push(' sourceLocator=' + item.sourceContext.locator.command);
|
|
1836
|
+
lines.push(' sourceLocator=' + serializeUntrustedMemory(item.sourceContext.locator.command, 500));
|
|
1810
1837
|
} else if (item.sourceContext.event.eventId) {
|
|
1811
1838
|
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');
|
|
1839
|
+
lines.push(' sourceLocator=' + serializeUntrustedMemory('cogmem memory show --event ' + item.sourceContext.event.eventId + project + ' --before 2 --after 2 --json', 500));
|
|
1813
1840
|
}
|
|
1814
1841
|
const seenEventIds = new Set([anchorEvent.eventId].filter(Boolean));
|
|
1815
1842
|
const before = uniqueWindowEvents(Array.isArray(item.sourceContext.before) ? item.sourceContext.before : [], seenEventIds).slice(-2);
|
|
@@ -1854,16 +1881,16 @@ function formatRecallDecision(trace) {
|
|
|
1854
1881
|
|
|
1855
1882
|
function formatAtlasCard(card) {
|
|
1856
1883
|
const facets = Array.isArray(card && card.matchedFacets)
|
|
1857
|
-
? card.matchedFacets.map((facet) => facet.type + ':' + facet.value).join(',')
|
|
1884
|
+
? card.matchedFacets.map((facet) => serializeUntrustedMemory(facet.type, 80) + ':' + serializeUntrustedMemory(facet.value, 160)).join(',')
|
|
1858
1885
|
: '';
|
|
1859
1886
|
const paths = Array.isArray(card && card.matchedPaths)
|
|
1860
|
-
? card.matchedPaths.map((path) => path.relation + '@' + (path.facet && path.facet.nodeId || 'facet')).slice(0, 6).join(',')
|
|
1887
|
+
? card.matchedPaths.map((path) => serializeUntrustedMemory(path.relation, 80) + '@' + serializeUntrustedMemory(path.facet && path.facet.nodeId || 'facet', 180)).slice(0, 6).join(',')
|
|
1861
1888
|
: '';
|
|
1862
|
-
const locator = card && card.sourceLocator && card.sourceLocator.command ? '; sourceLocator=' + card.sourceLocator.command : '';
|
|
1863
|
-
const why = card && card.whyMatched ? '; whyMatched=' +
|
|
1864
|
-
return (card && card.canonicalId || 'episode:unknown')
|
|
1865
|
-
+ '; title=' +
|
|
1866
|
-
+ '; summary=' +
|
|
1889
|
+
const locator = card && card.sourceLocator && card.sourceLocator.command ? '; sourceLocator=' + serializeUntrustedMemory(card.sourceLocator.command, 500) : '';
|
|
1890
|
+
const why = card && card.whyMatched ? '; whyMatched=' + serializeUntrustedMemory(card.whyMatched, 180) : '';
|
|
1891
|
+
return serializeUntrustedMemory(card && card.canonicalId || 'episode:unknown', 180)
|
|
1892
|
+
+ '; title=' + serializeUntrustedMemory(card && card.displayTitle, 120)
|
|
1893
|
+
+ '; summary=' + serializeUntrustedMemory(card && card.oneLineSummary, 180)
|
|
1867
1894
|
+ (facets ? '; matchedFacets=' + facets : '')
|
|
1868
1895
|
+ (paths ? '; matchedPaths=' + paths : '')
|
|
1869
1896
|
+ why
|
|
@@ -1878,10 +1905,10 @@ function clampRecallContext(text, maxChars) {
|
|
|
1878
1905
|
}
|
|
1879
1906
|
|
|
1880
1907
|
function formatContextEvent(event, limit) {
|
|
1881
|
-
const truncation = truncateLineWithMeta(event && event.text, limit);
|
|
1882
|
-
const label = contextEventLabel(event);
|
|
1883
|
-
const role = event && event.role ? event.role : 'unknown';
|
|
1884
|
-
const eventId = event && event.eventId ? event.eventId : 'unknown';
|
|
1908
|
+
const truncation = truncateLineWithMeta(serializeUntrustedMemory(event && event.text, limit), limit);
|
|
1909
|
+
const label = serializeUntrustedMemory(contextEventLabel(event), 120);
|
|
1910
|
+
const role = serializeUntrustedMemory(event && event.role ? event.role : 'unknown', 80);
|
|
1911
|
+
const eventId = serializeUntrustedMemory(event && event.eventId ? event.eventId : 'unknown', 160);
|
|
1885
1912
|
const charRange = event && event.charRange ? '; charRange=' + event.charRange.start + '-' + event.charRange.end : '';
|
|
1886
1913
|
const sourceRange = formatSourceRange(event && event.sourceRange);
|
|
1887
1914
|
const textLength = Number.isFinite(event && event.textLength) ? event.textLength : truncation.originalChars;
|
|
@@ -1903,17 +1930,17 @@ function formatSourceWindow(window, context) {
|
|
|
1903
1930
|
const dropped = Array.isArray(window && window.droppedOverlapEventIds) ? window.droppedOverlapEventIds : [];
|
|
1904
1931
|
return 'before=' + formatWindowSide(before)
|
|
1905
1932
|
+ '; after=' + formatWindowSide(after)
|
|
1906
|
-
+ '; overlap=' + (overlapEventIds.length ? overlapEventIds.join(',') : 'none')
|
|
1907
|
-
+ '; droppedOverlap=' + (dropped.length ? dropped.join(',') : 'none')
|
|
1908
|
-
+ '; overlapHandling=' + ((window && window.overlapHandling) || 'drop_from_after');
|
|
1933
|
+
+ '; overlap=' + (overlapEventIds.length ? overlapEventIds.map((id) => serializeUntrustedMemory(id, 120)).join(',') : 'none')
|
|
1934
|
+
+ '; droppedOverlap=' + (dropped.length ? dropped.map((id) => serializeUntrustedMemory(id, 120)).join(',') : 'none')
|
|
1935
|
+
+ '; overlapHandling=' + serializeUntrustedMemory((window && window.overlapHandling) || 'drop_from_after', 120);
|
|
1909
1936
|
}
|
|
1910
1937
|
|
|
1911
1938
|
function formatWindowSide(side) {
|
|
1912
1939
|
return 'requestedCount=' + Number(side.requestedCount || 0)
|
|
1913
1940
|
+ ', count=' + Number(side.count || 0)
|
|
1914
1941
|
+ ', excludesAnchor=' + (side.excludesAnchor !== false)
|
|
1915
|
-
+ ', ordering=' + (side.ordering || 'chronological')
|
|
1916
|
-
+ ', roleFilter=' + (side.roleFilter || 'all');
|
|
1942
|
+
+ ', ordering=' + serializeUntrustedMemory(side.ordering || 'chronological', 80)
|
|
1943
|
+
+ ', roleFilter=' + serializeUntrustedMemory(side.roleFilter || 'all', 80);
|
|
1917
1944
|
}
|
|
1918
1945
|
|
|
1919
1946
|
function uniqueWindowEvents(events, seenEventIds) {
|
package/dist/mcp/server.js
CHANGED
|
@@ -44,7 +44,7 @@ export interface RecallExplanation {
|
|
|
44
44
|
projectId?: string;
|
|
45
45
|
agentId?: string;
|
|
46
46
|
collection?: string;
|
|
47
|
-
recallMode: MemoryKernelNavigationResult['recallMode'] | 'raw_ledger_fallback';
|
|
47
|
+
recallMode: MemoryKernelNavigationResult['recallMode'] | 'raw_ledger_fallback' | 'atlas_facet_recall' | 'atlas_raw_grounded_recall';
|
|
48
48
|
fallbackUsed: boolean;
|
|
49
49
|
narrative?: NonNullable<MemoryKernelNavigationResult['navigation']>['narrative'];
|
|
50
50
|
pulseTrace?: NonNullable<MemoryKernelNavigationResult['navigation']>['pulse']['trace'];
|
|
@@ -2,7 +2,7 @@ import type Database from 'bun:sqlite';
|
|
|
2
2
|
import type { FacetQueryPlan } from '../atlas/FacetQueryPlanner.js';
|
|
3
3
|
import type { MemoryAtlasAction, MemoryAtlasCard, MemoryAtlasEdge, MemoryAtlasNode, MemoryAtlasRelatedCard } from '../atlas/MemoryAtlasTypes.js';
|
|
4
4
|
export declare const MEMORY_ATLAS_PROJECTION_NAME = "memory_atlas.v2";
|
|
5
|
-
export declare const MEMORY_ATLAS_PROJECTION_SCHEMA_VERSION = "3.7.
|
|
5
|
+
export declare const MEMORY_ATLAS_PROJECTION_SCHEMA_VERSION = "3.7.2";
|
|
6
6
|
export declare class MemoryAtlasStore {
|
|
7
7
|
readonly db: Database;
|
|
8
8
|
constructor(db: Database);
|
|
@@ -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: {
|
|
@@ -52,6 +53,8 @@ export declare class MemoryAtlasStore {
|
|
|
52
53
|
decay(projectId?: string, factor?: number, now?: number): number;
|
|
53
54
|
projectionNeedsRefresh(projectId: string): boolean;
|
|
54
55
|
markProjectionClean(projectId: string, metadata?: Record<string, unknown>, now?: number): void;
|
|
56
|
+
markProjectionDirty(projectId: string, metadata?: Record<string, unknown>, now?: number): void;
|
|
57
|
+
aggregateFacetNodeSupport(projectId: string, now?: number): void;
|
|
55
58
|
markProjectionFailed(projectId: string, error: string, now?: number): void;
|
|
56
59
|
getProjectionState(projectId: string): {
|
|
57
60
|
status: string;
|