@devflow-tools/memory-engine 0.17.8 → 0.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/consolidator.d.ts.map +1 -1
- package/dist/consolidator.js +2 -0
- package/dist/consolidator.js.map +1 -1
- package/dist/embedding-model-store.d.ts +1 -4
- package/dist/embedding-model-store.d.ts.map +1 -1
- package/dist/embedding-model-store.js +3 -75
- package/dist/embedding-model-store.js.map +1 -1
- package/dist/embedding-provider.d.ts +3 -2
- package/dist/embedding-provider.d.ts.map +1 -1
- package/dist/embedding-provider.js +151 -43
- package/dist/embedding-provider.js.map +1 -1
- package/dist/hybrid-search.d.ts +5 -1
- package/dist/hybrid-search.d.ts.map +1 -1
- package/dist/hybrid-search.js +92 -56
- package/dist/hybrid-search.js.map +1 -1
- package/dist/index.d.ts +16 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/lease-store.d.ts.map +1 -1
- package/dist/lease-store.js +30 -0
- package/dist/lease-store.js.map +1 -1
- package/dist/memory-command-service.d.ts +73 -0
- package/dist/memory-command-service.d.ts.map +1 -0
- package/dist/memory-command-service.js +283 -0
- package/dist/memory-command-service.js.map +1 -0
- package/dist/memory-engine.d.ts +19 -15
- package/dist/memory-engine.d.ts.map +1 -1
- package/dist/memory-engine.js +207 -158
- package/dist/memory-engine.js.map +1 -1
- package/dist/memory-episode-repository.d.ts +77 -0
- package/dist/memory-episode-repository.d.ts.map +1 -0
- package/dist/memory-episode-repository.js +262 -0
- package/dist/memory-episode-repository.js.map +1 -0
- package/dist/memory-fact-provider-port.d.ts +83 -0
- package/dist/memory-fact-provider-port.d.ts.map +1 -0
- package/dist/memory-fact-provider-port.js +22 -0
- package/dist/memory-fact-provider-port.js.map +1 -0
- package/dist/memory-fact-validator.d.ts +16 -0
- package/dist/memory-fact-validator.d.ts.map +1 -0
- package/dist/memory-fact-validator.js +52 -0
- package/dist/memory-fact-validator.js.map +1 -0
- package/dist/memory-gate.d.ts +14 -17
- package/dist/memory-gate.d.ts.map +1 -1
- package/dist/memory-gate.js +369 -329
- package/dist/memory-gate.js.map +1 -1
- package/dist/memory-identity-compiler.d.ts +18 -0
- package/dist/memory-identity-compiler.d.ts.map +1 -0
- package/dist/memory-identity-compiler.js +26 -0
- package/dist/memory-identity-compiler.js.map +1 -0
- package/dist/memory-index-projector.d.ts +22 -0
- package/dist/memory-index-projector.d.ts.map +1 -0
- package/dist/memory-index-projector.js +11 -0
- package/dist/memory-index-projector.js.map +1 -0
- package/dist/memory-lifecycle-reducer.d.ts +21 -0
- package/dist/memory-lifecycle-reducer.d.ts.map +1 -0
- package/dist/memory-lifecycle-reducer.js +34 -0
- package/dist/memory-lifecycle-reducer.js.map +1 -0
- package/dist/memory-maintenance.d.ts +1 -4
- package/dist/memory-maintenance.d.ts.map +1 -1
- package/dist/memory-maintenance.js +3 -20
- package/dist/memory-maintenance.js.map +1 -1
- package/dist/memory-query-service.d.ts +24 -0
- package/dist/memory-query-service.d.ts.map +1 -0
- package/dist/memory-query-service.js +180 -0
- package/dist/memory-query-service.js.map +1 -0
- package/dist/memory-relevance.d.ts +16 -1
- package/dist/memory-relevance.d.ts.map +1 -1
- package/dist/memory-relevance.js +83 -139
- package/dist/memory-relevance.js.map +1 -1
- package/dist/memory-store.d.ts +52 -36
- package/dist/memory-store.d.ts.map +1 -1
- package/dist/memory-store.js +406 -704
- package/dist/memory-store.js.map +1 -1
- package/dist/semantic-memory-compiler.d.ts +0 -2
- package/dist/semantic-memory-compiler.d.ts.map +1 -1
- package/dist/semantic-memory-compiler.js +4 -4
- package/dist/semantic-memory-compiler.js.map +1 -1
- package/dist/session-summarizer.d.ts +3 -0
- package/dist/session-summarizer.d.ts.map +1 -1
- package/dist/session-summarizer.js.map +1 -1
- package/dist/signal-analyzer.d.ts +2 -2
- package/dist/signal-analyzer.d.ts.map +1 -1
- package/dist/signal-analyzer.js +10 -16
- package/dist/signal-analyzer.js.map +1 -1
- package/package.json +14 -5
- package/dist/lease-store.test.d.ts +0 -2
- package/dist/lease-store.test.d.ts.map +0 -1
- package/dist/lease-store.test.js +0 -182
- package/dist/lease-store.test.js.map +0 -1
- package/dist/migrations/v10-to-v11.d.ts +0 -3
- package/dist/migrations/v10-to-v11.d.ts.map +0 -1
- package/dist/migrations/v10-to-v11.js +0 -21
- package/dist/migrations/v10-to-v11.js.map +0 -1
- package/dist/migrations/v11-to-v12.d.ts +0 -66
- package/dist/migrations/v11-to-v12.d.ts.map +0 -1
- package/dist/migrations/v11-to-v12.js +0 -577
- package/dist/migrations/v11-to-v12.js.map +0 -1
- package/dist/migrations/v12-to-v13.d.ts +0 -3
- package/dist/migrations/v12-to-v13.d.ts.map +0 -1
- package/dist/migrations/v12-to-v13.js +0 -50
- package/dist/migrations/v12-to-v13.js.map +0 -1
- package/dist/migrations/v2-to-v3.d.ts +0 -3
- package/dist/migrations/v2-to-v3.d.ts.map +0 -1
- package/dist/migrations/v2-to-v3.js +0 -122
- package/dist/migrations/v2-to-v3.js.map +0 -1
- package/dist/migrations/v2-to-v3.test.d.ts +0 -2
- package/dist/migrations/v2-to-v3.test.d.ts.map +0 -1
- package/dist/migrations/v2-to-v3.test.js +0 -88
- package/dist/migrations/v2-to-v3.test.js.map +0 -1
- package/dist/migrations/v3-to-v4.d.ts +0 -3
- package/dist/migrations/v3-to-v4.d.ts.map +0 -1
- package/dist/migrations/v3-to-v4.js +0 -97
- package/dist/migrations/v3-to-v4.js.map +0 -1
- package/dist/migrations/v4-to-v5.d.ts +0 -3
- package/dist/migrations/v4-to-v5.d.ts.map +0 -1
- package/dist/migrations/v4-to-v5.js +0 -34
- package/dist/migrations/v4-to-v5.js.map +0 -1
- package/dist/migrations/v4-to-v5.test.d.ts +0 -2
- package/dist/migrations/v4-to-v5.test.d.ts.map +0 -1
- package/dist/migrations/v4-to-v5.test.js +0 -32
- package/dist/migrations/v4-to-v5.test.js.map +0 -1
- package/dist/migrations/v6-to-v7.d.ts +0 -3
- package/dist/migrations/v6-to-v7.d.ts.map +0 -1
- package/dist/migrations/v6-to-v7.js +0 -18
- package/dist/migrations/v6-to-v7.js.map +0 -1
- package/dist/migrations/v6-to-v7.test.d.ts +0 -2
- package/dist/migrations/v6-to-v7.test.d.ts.map +0 -1
- package/dist/migrations/v6-to-v7.test.js +0 -33
- package/dist/migrations/v6-to-v7.test.js.map +0 -1
- package/dist/migrations/v7-to-v8.d.ts +0 -3
- package/dist/migrations/v7-to-v8.d.ts.map +0 -1
- package/dist/migrations/v7-to-v8.js +0 -20
- package/dist/migrations/v7-to-v8.js.map +0 -1
- package/dist/migrations/v8-to-v9.d.ts +0 -3
- package/dist/migrations/v8-to-v9.d.ts.map +0 -1
- package/dist/migrations/v8-to-v9.js +0 -18
- package/dist/migrations/v8-to-v9.js.map +0 -1
- package/dist/migrations/v9-to-v10.d.ts +0 -3
- package/dist/migrations/v9-to-v10.d.ts.map +0 -1
- package/dist/migrations/v9-to-v10.js +0 -45
- package/dist/migrations/v9-to-v10.js.map +0 -1
- package/dist/migrations/v9-to-v10.test.d.ts +0 -2
- package/dist/migrations/v9-to-v10.test.d.ts.map +0 -1
- package/dist/migrations/v9-to-v10.test.js +0 -40
- package/dist/migrations/v9-to-v10.test.js.map +0 -1
- package/dist/preference-conflicts.test.d.ts +0 -2
- package/dist/preference-conflicts.test.d.ts.map +0 -1
- package/dist/preference-conflicts.test.js +0 -80
- package/dist/preference-conflicts.test.js.map +0 -1
- package/dist/semantic-memory.test.d.ts +0 -2
- package/dist/semantic-memory.test.d.ts.map +0 -1
- package/dist/semantic-memory.test.js +0 -61
- package/dist/semantic-memory.test.js.map +0 -1
package/dist/memory-engine.js
CHANGED
|
@@ -8,12 +8,13 @@ import { GraphStore } from './graph-store.js';
|
|
|
8
8
|
import { extractRuleMemories } from './rule-extractor.js';
|
|
9
9
|
import { isObservationType } from './observation-types.js';
|
|
10
10
|
import { classifyFailureCategory, evaluateStoredObservationQuality, observationClusterKey, } from './observation-quality.js';
|
|
11
|
-
import { buildPreferenceConflictDiagnostics,
|
|
11
|
+
import { buildPreferenceConflictDiagnostics, } from './preference-conflicts.js';
|
|
12
12
|
import { enrichExplicitMemory, } from './explicit-memory-enricher.js';
|
|
13
|
-
import { evaluateBootstrapMemory, MEMORY_SELECTION_THRESHOLD,
|
|
13
|
+
import { evaluateBootstrapMemory, MEMORY_SELECTION_THRESHOLD, } from './memory-relevance.js';
|
|
14
14
|
import { resolveCapabilityRollout } from '@devflow-tools/sdk/capability-rollout';
|
|
15
|
-
import {
|
|
15
|
+
import { authorizeMemoryMutation, compileRetrievalScope, stripMutableIdentityMetadata } from '@devflow-tools/sdk';
|
|
16
16
|
import { emitSemanticControlLog } from '@devflow-tools/telemetry';
|
|
17
|
+
import { MemoryCommandService } from './memory-command-service.js';
|
|
17
18
|
function estimateMemoryTokens(text) {
|
|
18
19
|
const cjkCharacters = text.match(/[\u3400-\u9fff\uf900-\ufaff]/g)?.length ?? 0;
|
|
19
20
|
return Math.max(1, cjkCharacters + Math.ceil((text.length - cjkCharacters) / 4));
|
|
@@ -57,25 +58,21 @@ export class MemoryEngine {
|
|
|
57
58
|
searcher;
|
|
58
59
|
graph;
|
|
59
60
|
rootPath;
|
|
60
|
-
|
|
61
|
+
projectIdentity;
|
|
62
|
+
commands;
|
|
63
|
+
constructor(rootPath, embedder, projectIdentity) {
|
|
61
64
|
this.rootPath = rootPath;
|
|
62
|
-
this.
|
|
65
|
+
this.projectIdentity = projectIdentity;
|
|
63
66
|
this.embedder = embedder ?? new EmbeddingProvider();
|
|
67
|
+
this.store = new MemoryStore(rootPath, this.embedder.getCapability('memory_retrieval').profile, projectIdentity);
|
|
64
68
|
this.searcher = new HybridSearch(this.store, this.embedder, this);
|
|
65
69
|
this.graph = new GraphStore(this.store);
|
|
66
|
-
this.
|
|
70
|
+
this.commands = new MemoryCommandService(rootPath, this.store, projectIdentity);
|
|
67
71
|
}
|
|
68
72
|
_initPromise = null;
|
|
69
73
|
_embedderAvailable = true;
|
|
70
|
-
_explicitPreferencesReconciled = false;
|
|
71
|
-
reconcileHistoricalExplicitPreferences() {
|
|
72
|
-
if (this._explicitPreferencesReconciled)
|
|
73
|
-
return;
|
|
74
|
-
this.store.reconcileExplicitPreferenceConflicts(this.rootPath);
|
|
75
|
-
this._explicitPreferencesReconciled = true;
|
|
76
|
-
}
|
|
77
74
|
getExplicitPreferenceConflictState() {
|
|
78
|
-
const conflicts = buildPreferenceConflictDiagnostics(this.store.listCurrentExplicitPreferences(this.
|
|
75
|
+
const conflicts = buildPreferenceConflictDiagnostics(this.store.listCurrentExplicitPreferences(this.projectIdentity.memoryNamespace));
|
|
79
76
|
return {
|
|
80
77
|
conflicts,
|
|
81
78
|
withheldIds: new Set(conflicts.flatMap(conflict => conflict.memoryIds)),
|
|
@@ -91,8 +88,25 @@ export class MemoryEngine {
|
|
|
91
88
|
async ensureInit() {
|
|
92
89
|
if (!this._initPromise) {
|
|
93
90
|
this._initPromise = (async () => {
|
|
94
|
-
this.
|
|
91
|
+
const before = this.embedder.getCapability('memory_retrieval');
|
|
95
92
|
this._embedderAvailable = await this.embedder.warmup();
|
|
93
|
+
const after = this.embedder.getCapability('memory_retrieval');
|
|
94
|
+
if (before.status !== after.status) {
|
|
95
|
+
emitSemanticControlLog({
|
|
96
|
+
event: 'embedding.capability.transitioned',
|
|
97
|
+
identity: { projectRoot: this.rootPath },
|
|
98
|
+
level: after.status === 'ready' ? 'info' : 'warn',
|
|
99
|
+
timestamp: Date.now(),
|
|
100
|
+
data: {
|
|
101
|
+
purpose: 'memory_retrieval',
|
|
102
|
+
profileHash: after.profile.profileHash,
|
|
103
|
+
generation: after.profile.generation,
|
|
104
|
+
from: before.status,
|
|
105
|
+
to: after.status,
|
|
106
|
+
failure: after.failure,
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
}
|
|
96
110
|
})();
|
|
97
111
|
}
|
|
98
112
|
return this._initPromise;
|
|
@@ -275,6 +289,7 @@ export class MemoryEngine {
|
|
|
275
289
|
executionId: normalized.executionId,
|
|
276
290
|
action: normalized.action,
|
|
277
291
|
requiredPolicyFacets: normalized.requiredPolicyFacets,
|
|
292
|
+
semanticTask: normalized.semanticTask,
|
|
278
293
|
scope: normalized.scopes?.length === 1 ? normalized.scopes[0] : undefined,
|
|
279
294
|
});
|
|
280
295
|
const results = searchOutcome.results;
|
|
@@ -487,7 +502,6 @@ export class MemoryEngine {
|
|
|
487
502
|
executionId: normalized.executionId,
|
|
488
503
|
};
|
|
489
504
|
this.store.recordRetrievalAccess({ ...access, accessType: 'selected' });
|
|
490
|
-
this.store.recordRetrievalAccess({ ...access, accessType: 'exposed' });
|
|
491
505
|
}
|
|
492
506
|
catch { /* access reinforcement is non-critical */ }
|
|
493
507
|
}
|
|
@@ -510,72 +524,49 @@ export class MemoryEngine {
|
|
|
510
524
|
relevant: [],
|
|
511
525
|
};
|
|
512
526
|
}
|
|
513
|
-
async saveExplicitMemoryIntent(content,
|
|
514
|
-
const
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
const
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
narrative: normalizedContent,
|
|
527
|
+
async saveExplicitMemoryIntent(content, provenance, sourceEventId) {
|
|
528
|
+
const observationSessionId = provenance.sessionId;
|
|
529
|
+
const event = sourceEventId ? this.store.getEvent(sourceEventId) : null;
|
|
530
|
+
const turnId = eventTurnId(event, sourceEventId ?? `turn:${observationSessionId}`);
|
|
531
|
+
const result = await this.commands.commitExplicitEpisode({
|
|
532
|
+
content, actorId: provenance.actorId, sessionId: observationSessionId, turnId,
|
|
533
|
+
sourceMessageId: sourceEventId ?? `explicit-message:${turnId}`,
|
|
521
534
|
sourceEventIds: sourceEventId ? [sourceEventId] : [],
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
semanticKey: observation.semantic_key,
|
|
525
|
-
facetKey: observation.facet_key,
|
|
526
|
-
resolutionState: observation.resolution_state,
|
|
527
|
-
})),
|
|
535
|
+
createdAt: event?.created_at,
|
|
536
|
+
supersedesEpisodeId: provenance.supersedesEpisodeId,
|
|
528
537
|
});
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
: null;
|
|
535
|
-
const rows = compilation.facts.map(fact => this.store.acceptExplicitPreference({
|
|
536
|
-
sessionId: observationSessionId,
|
|
537
|
-
project: this.rootPath,
|
|
538
|
-
title: fact.enrichment.title,
|
|
539
|
-
content: fact.enrichment.narrative,
|
|
540
|
-
sourceEventId,
|
|
541
|
-
enrichment: fact.enrichment,
|
|
542
|
-
}));
|
|
543
|
-
const row = rows[0];
|
|
544
|
-
for (let index = 1; index < rows.length; index++) {
|
|
545
|
-
this.store.addSemanticObservationRelation(row.id, rows[index].id, compilation.facts[index]?.relationToPrimary ?? 'related_to');
|
|
546
|
-
}
|
|
547
|
-
if (sourceEventId) {
|
|
548
|
-
const event = this.store.getEvent(sourceEventId);
|
|
549
|
-
if (!event) {
|
|
550
|
-
return {
|
|
551
|
-
id: row.id, type: row.type, title: row.title, content: row.narrative,
|
|
552
|
-
source: row.source, scope: row.project, confidence: row.confidence,
|
|
553
|
-
importance: row.importance, score: 1, createdAt: observationEffectiveTimestamp(row),
|
|
554
|
-
indexStatus: row.embedding ? 'indexed' : 'pending',
|
|
555
|
-
durable: true,
|
|
556
|
-
lexicallyReady: row.quality_status === 'accepted',
|
|
557
|
-
policyReady: row.quality_status === 'accepted' && compilation.facts[0].enrichment.facet !== 'generic',
|
|
558
|
-
vectorStatus: row.embedding ? 'ready' : 'pending',
|
|
559
|
-
relatedMemoryIds: rows.slice(1).map(item => item.id),
|
|
560
|
-
semanticDecision: {
|
|
561
|
-
status: compilation.status,
|
|
562
|
-
operations: compilation.facts.map(fact => fact.operation),
|
|
563
|
-
rejectedCount: compilation.rejected.length,
|
|
564
|
-
},
|
|
565
|
-
};
|
|
566
|
-
}
|
|
538
|
+
const rows = result.factIds.flatMap(id => {
|
|
539
|
+
const row = this.store.getObservation(id);
|
|
540
|
+
return row ? [row] : [];
|
|
541
|
+
});
|
|
542
|
+
if (sourceEventId && event) {
|
|
567
543
|
for (const saved of rows) {
|
|
568
544
|
this.store.recordPendingTurnObservation({
|
|
569
545
|
turnEventId: sourceEventId,
|
|
570
|
-
turnId
|
|
546
|
+
turnId,
|
|
571
547
|
sessionId: observationSessionId,
|
|
572
|
-
project: this.
|
|
548
|
+
project: this.projectIdentity.memoryNamespace,
|
|
573
549
|
observationId: saved.id,
|
|
574
|
-
createdByTurn:
|
|
550
|
+
createdByTurn: result.decisions.some(decision => decision.candidateId && decision.operation === 'add'),
|
|
575
551
|
createdAt: event.created_at,
|
|
576
552
|
});
|
|
577
553
|
}
|
|
578
554
|
}
|
|
555
|
+
const row = rows[0];
|
|
556
|
+
if (!row) {
|
|
557
|
+
return {
|
|
558
|
+
id: result.episode.episodeId, type: 'cold_episode',
|
|
559
|
+
title: result.episode.content.slice(0, 160), content: result.episode.content,
|
|
560
|
+
source: 'explicit_intent', scope: this.projectIdentity.memoryNamespace, confidence: 1, importance: 10,
|
|
561
|
+
score: 1, createdAt: result.episode.createdAt, indexStatus: 'pending', durable: true,
|
|
562
|
+
lexicallyReady: true, policyReady: false, vectorStatus: 'pending', relatedMemoryIds: [],
|
|
563
|
+
semanticDecision: {
|
|
564
|
+
status: result.status === 'held' ? 'semantic_rejected' : 'semantic_unavailable',
|
|
565
|
+
operations: result.decisions.map(decision => decision.operation),
|
|
566
|
+
rejectedCount: result.status === 'held' ? result.decisions.length : 0,
|
|
567
|
+
},
|
|
568
|
+
};
|
|
569
|
+
}
|
|
579
570
|
return {
|
|
580
571
|
id: row.id, type: row.type, title: row.title, content: row.narrative,
|
|
581
572
|
source: row.source, scope: row.project, confidence: row.confidence,
|
|
@@ -583,37 +574,40 @@ export class MemoryEngine {
|
|
|
583
574
|
indexStatus: row.embedding ? 'indexed' : 'pending',
|
|
584
575
|
durable: true,
|
|
585
576
|
lexicallyReady: row.quality_status === 'accepted',
|
|
586
|
-
policyReady: row.quality_status === 'accepted' &&
|
|
577
|
+
policyReady: row.quality_status === 'accepted' && row.facet_key !== null,
|
|
587
578
|
vectorStatus: row.embedding ? 'ready' : 'pending',
|
|
588
579
|
relatedMemoryIds: rows.slice(1).map(item => item.id),
|
|
589
580
|
semanticDecision: {
|
|
590
|
-
status:
|
|
591
|
-
operations:
|
|
592
|
-
rejectedCount:
|
|
581
|
+
status: 'semantic_ready',
|
|
582
|
+
operations: result.decisions.map(decision => decision.operation),
|
|
583
|
+
rejectedCount: result.decisions.filter(decision => decision.operation === 'hold').length,
|
|
593
584
|
},
|
|
594
585
|
};
|
|
595
586
|
}
|
|
596
587
|
enrichExplicitMemoryObservation(observationId) {
|
|
597
588
|
const observation = this.store.getObservation(observationId);
|
|
598
|
-
if (!observation)
|
|
599
|
-
|
|
589
|
+
if (!observation) {
|
|
590
|
+
const episode = this.commands.getEpisode(observationId);
|
|
591
|
+
if (!episode)
|
|
592
|
+
throw new Error(`Observation ${observationId} not found`);
|
|
593
|
+
return enrichExplicitMemory({
|
|
594
|
+
projectRoot: this.rootPath,
|
|
595
|
+
content: episode.content,
|
|
596
|
+
sourceEventIds: episode.sourceEventIds,
|
|
597
|
+
});
|
|
598
|
+
}
|
|
600
599
|
const enrichment = enrichExplicitMemory({
|
|
601
600
|
projectRoot: this.rootPath,
|
|
602
601
|
content: observation.narrative,
|
|
603
602
|
sourceEventIds: parseStringArray(observation.source_event_ids),
|
|
604
603
|
});
|
|
605
|
-
this.store.applyExplicitMemoryEnrichment(observationId, enrichment);
|
|
606
604
|
return enrichment;
|
|
607
605
|
}
|
|
608
606
|
attributeRetrievalBenefits(options) {
|
|
609
607
|
const selectedRows = this.store.listRetrievalAccessForAttribution(options.sessionId, options.executionId);
|
|
610
608
|
const unique = new Map(selectedRows.map(row => [row.source_id, row]));
|
|
611
|
-
const
|
|
612
|
-
|
|
613
|
-
const authoritativeAdoptedIds = options.adoptedSourceIds
|
|
614
|
-
? new Set(options.adoptedSourceIds)
|
|
615
|
-
: null;
|
|
616
|
-
let adopted = 0;
|
|
609
|
+
const consumedSourceIds = new Set(options.consumedSourceIds);
|
|
610
|
+
let consumed = 0;
|
|
617
611
|
let positive = 0;
|
|
618
612
|
let negative = 0;
|
|
619
613
|
const rollout = resolveCapabilityRollout(this.rootPath).config.outcomeAttribution;
|
|
@@ -622,23 +616,12 @@ export class MemoryEngine {
|
|
|
622
616
|
const observation = row.source_type === 'observation' ? this.store.getObservation(row.source_id) : null;
|
|
623
617
|
if (!memory && !observation)
|
|
624
618
|
continue;
|
|
625
|
-
|
|
626
|
-
const content = memory?.content ?? observation.narrative;
|
|
627
|
-
const concepts = observation ? parseStringArray(observation.concepts) : [];
|
|
628
|
-
const sourceEntities = normalizeMemoryEntities(`${title}\n${content}\n${concepts.join(' ')}`);
|
|
629
|
-
const entityAdopted = [...sourceEntities].some(entity => answerEntities.has(entity));
|
|
630
|
-
const factAdopted = parseStringArray(observation?.facts ?? '[]').some(fact => {
|
|
631
|
-
const normalizedFact = fact.normalize('NFKC').toLocaleLowerCase('en-US').trim();
|
|
632
|
-
return normalizedFact.length >= 12 && normalizedAnswer.includes(normalizedFact);
|
|
633
|
-
});
|
|
634
|
-
if (authoritativeAdoptedIds
|
|
635
|
-
? !authoritativeAdoptedIds.has(row.source_id)
|
|
636
|
-
: !entityAdopted && !factAdopted)
|
|
619
|
+
if (!consumedSourceIds.has(row.source_id))
|
|
637
620
|
continue;
|
|
638
621
|
this.store.recordRetrievalAccess({
|
|
639
622
|
sourceType: row.source_type,
|
|
640
623
|
sourceId: row.source_id,
|
|
641
|
-
accessType: '
|
|
624
|
+
accessType: 'consumed',
|
|
642
625
|
sessionId: row.session_id ?? options.sessionId,
|
|
643
626
|
requestId: row.request_id ?? undefined,
|
|
644
627
|
executionId: row.execution_id ?? options.executionId,
|
|
@@ -646,10 +629,27 @@ export class MemoryEngine {
|
|
|
646
629
|
purpose: row.purpose ?? 'session_final_answer',
|
|
647
630
|
rank: row.rank ?? undefined,
|
|
648
631
|
score: row.score ?? undefined,
|
|
649
|
-
idempotencyKey: `memory-
|
|
632
|
+
idempotencyKey: `memory-consumed:${options.sessionId}:${options.executionId ?? ''}:${row.source_type}:${row.source_id}`,
|
|
650
633
|
});
|
|
651
|
-
|
|
652
|
-
if (
|
|
634
|
+
consumed += 1;
|
|
635
|
+
if (options.verificationReceipt) {
|
|
636
|
+
this.store.recordRetrievalAccess({
|
|
637
|
+
sourceType: row.source_type,
|
|
638
|
+
sourceId: row.source_id,
|
|
639
|
+
accessType: 'verified',
|
|
640
|
+
sessionId: row.session_id ?? options.sessionId,
|
|
641
|
+
requestId: row.request_id ?? undefined,
|
|
642
|
+
executionId: row.execution_id ?? options.executionId,
|
|
643
|
+
query: row.query ?? undefined,
|
|
644
|
+
purpose: 'same_task_verification',
|
|
645
|
+
rank: row.rank ?? undefined,
|
|
646
|
+
score: row.score ?? undefined,
|
|
647
|
+
verificationReceipt: options.verificationReceipt,
|
|
648
|
+
idempotencyKey: `memory-verified:${options.sessionId}:${options.executionId ?? ''}:${row.source_type}:${row.source_id}`,
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
if (rollout !== 'off' && options.verificationReceipt
|
|
652
|
+
&& options.outcome && options.outcome !== 'unknown') {
|
|
653
653
|
const accessType = options.outcome === 'positive' ? 'positive_outcome' : 'negative_outcome';
|
|
654
654
|
this.store.recordRetrievalAccess({
|
|
655
655
|
sourceType: row.source_type,
|
|
@@ -675,7 +675,7 @@ export class MemoryEngine {
|
|
|
675
675
|
negative += 1;
|
|
676
676
|
}
|
|
677
677
|
}
|
|
678
|
-
return { selected: unique.size,
|
|
678
|
+
return { selected: unique.size, consumed, positive, negative, unknown: Math.max(0, unique.size - consumed) };
|
|
679
679
|
}
|
|
680
680
|
recordContextEnvelopeSelection(options) {
|
|
681
681
|
const entries = [...new Map(options.entries.map(entry => [entry.id, entry])).values()];
|
|
@@ -684,21 +684,19 @@ export class MemoryEngine {
|
|
|
684
684
|
const sourceType = this.store.getObservation(id) ? 'observation' : this.store.get(id) ? 'memory' : null;
|
|
685
685
|
if (!sourceType)
|
|
686
686
|
continue;
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
});
|
|
701
|
-
}
|
|
687
|
+
this.store.recordRetrievalAccess({
|
|
688
|
+
sourceType,
|
|
689
|
+
sourceId: id,
|
|
690
|
+
accessType: 'selected',
|
|
691
|
+
sessionId: options.sessionId,
|
|
692
|
+
executionId: options.executionId,
|
|
693
|
+
requestId: options.requestId,
|
|
694
|
+
query: options.query,
|
|
695
|
+
purpose: 'task_context_envelope',
|
|
696
|
+
rank: index + 1,
|
|
697
|
+
score: entry.score,
|
|
698
|
+
contextReceipt: options.contextReceipt,
|
|
699
|
+
});
|
|
702
700
|
}
|
|
703
701
|
}
|
|
704
702
|
listObservationMemories(source) {
|
|
@@ -729,18 +727,24 @@ export class MemoryEngine {
|
|
|
729
727
|
source: m.source,
|
|
730
728
|
}));
|
|
731
729
|
}
|
|
732
|
-
async approveCandidate(id) {
|
|
733
|
-
this.
|
|
730
|
+
async approveCandidate(id, provenance) {
|
|
731
|
+
await this.updateMemory(id, { status: 'approved' }, provenance);
|
|
734
732
|
}
|
|
735
|
-
async rejectCandidate(id) {
|
|
736
|
-
this.
|
|
733
|
+
async rejectCandidate(id, provenance) {
|
|
734
|
+
await this.deleteMemory(id, provenance);
|
|
737
735
|
}
|
|
738
736
|
// --- New API methods ---
|
|
739
737
|
async search(query, options) {
|
|
738
|
+
return (await this.searchWithDiagnostics(query, options)).results;
|
|
739
|
+
}
|
|
740
|
+
async searchWithDiagnostics(query, options) {
|
|
740
741
|
await this.ensureInit();
|
|
741
742
|
const { withheldIds } = this.getExplicitPreferenceConflictState();
|
|
742
|
-
const
|
|
743
|
-
return
|
|
743
|
+
const outcome = await this.searcher.searchWithDiagnostics(query, { ...options, excludedIds: withheldIds });
|
|
744
|
+
return {
|
|
745
|
+
...outcome,
|
|
746
|
+
results: outcome.results.filter(result => this.isSearchResultVisible(result, withheldIds)),
|
|
747
|
+
};
|
|
744
748
|
}
|
|
745
749
|
getRetrievalRevision() {
|
|
746
750
|
return this.store.getRetrievalRevision();
|
|
@@ -754,14 +758,12 @@ export class MemoryEngine {
|
|
|
754
758
|
});
|
|
755
759
|
return rows.map(r => this._rowToRecord(r));
|
|
756
760
|
}
|
|
757
|
-
async addMemory(entry) {
|
|
758
|
-
return this.addMemoryWithStatus(entry);
|
|
761
|
+
async addMemory(entry, provenance) {
|
|
762
|
+
return this.addMemoryWithStatus(entry, provenance);
|
|
759
763
|
}
|
|
760
|
-
async addMemoryWithStatus(entry, requestedStatus, options = {}) {
|
|
764
|
+
async addMemoryWithStatus(entry, provenance, requestedStatus, options = {}) {
|
|
761
765
|
await this.ensureInit();
|
|
762
|
-
|
|
763
|
-
await this.ensureSession(entry.sessionId, this.rootPath);
|
|
764
|
-
}
|
|
766
|
+
await this.ensureSession(provenance.sessionId, this.projectIdentity.memoryNamespace);
|
|
765
767
|
// 1. SHA-256 content hash exact dedup
|
|
766
768
|
const normalizedContent = entry.content
|
|
767
769
|
.replace(/\/Users\/[^/\s]+(\/\S+)?/g, '{PROJECT_ROOT}$1')
|
|
@@ -813,21 +815,21 @@ export class MemoryEngine {
|
|
|
813
815
|
if (bestSim > 0.85 && bestMatch) {
|
|
814
816
|
if (entry.content.length > bestMatch.content.length) {
|
|
815
817
|
const newRow = this.store.add({
|
|
818
|
+
actorId: provenance.actorId,
|
|
816
819
|
type: bestMatch.type,
|
|
817
|
-
category: entry.category,
|
|
818
820
|
content: entry.content,
|
|
819
821
|
originalText: entry.originalText,
|
|
820
822
|
source: entry.source,
|
|
821
823
|
status: requestedStatus ?? bestMatch.status,
|
|
822
824
|
confidence: entry.confidence ?? bestMatch.confidence,
|
|
823
825
|
scope: entry.scope ?? bestMatch.scope,
|
|
824
|
-
sessionId:
|
|
826
|
+
sessionId: provenance.sessionId,
|
|
825
827
|
});
|
|
826
|
-
this.
|
|
828
|
+
this.updateStoredRow(newRow, {
|
|
827
829
|
version: (bestMatch.version || 1) + 1,
|
|
828
830
|
supersedes: bestMatch.id,
|
|
829
831
|
});
|
|
830
|
-
this.
|
|
832
|
+
this.updateStoredRow(bestMatch, {
|
|
831
833
|
status: 'superseded',
|
|
832
834
|
superseded_by: newRow.id,
|
|
833
835
|
});
|
|
@@ -840,7 +842,7 @@ export class MemoryEngine {
|
|
|
840
842
|
reason: `superseded_by:${newRow.id}`,
|
|
841
843
|
});
|
|
842
844
|
emitSemanticControlLog({
|
|
843
|
-
event: 'memory.write.completed', identity: { projectRoot: this.rootPath, sessionId:
|
|
845
|
+
event: 'memory.write.completed', identity: { projectRoot: this.rootPath, sessionId: provenance.sessionId }, level: 'info', timestamp: Date.now(),
|
|
844
846
|
data: { stage: 'version', oldMemoryId: bestMatch.id, newMemoryId: newRow.id, version: (bestMatch.version || 1) + 1 },
|
|
845
847
|
});
|
|
846
848
|
return this._rowToRecord(newRow);
|
|
@@ -861,14 +863,17 @@ export class MemoryEngine {
|
|
|
861
863
|
const arbitration = await this.detectAndArbitrateContradiction(entry.content, newEmb);
|
|
862
864
|
if (arbitration.decision === 'skip') {
|
|
863
865
|
emitSemanticControlLog({
|
|
864
|
-
event: 'memory.write.completed', identity: { projectRoot: this.rootPath, sessionId:
|
|
866
|
+
event: 'memory.write.completed', identity: { projectRoot: this.rootPath, sessionId: provenance.sessionId }, level: 'info', timestamp: Date.now(),
|
|
865
867
|
data: { stage: 'contradiction_arbitration', operation: 'hold', reason: arbitration.reason },
|
|
866
868
|
});
|
|
867
869
|
return null;
|
|
868
870
|
}
|
|
869
871
|
if (arbitration.decision === 'merge' && arbitration.conflictingId) {
|
|
870
872
|
const mergedContent = arbitration.mergedContent || `${entry.content}`;
|
|
871
|
-
this.store.
|
|
873
|
+
const conflicting = this.store.get(arbitration.conflictingId);
|
|
874
|
+
if (!conflicting)
|
|
875
|
+
throw new Error('MEMORY_ID_NOT_FOUND');
|
|
876
|
+
this.updateStoredRow(conflicting, { content: mergedContent });
|
|
872
877
|
this.store.incrementAccess(arbitration.conflictingId);
|
|
873
878
|
this.store.recordHistory({
|
|
874
879
|
memoryId: arbitration.conflictingId,
|
|
@@ -877,7 +882,7 @@ export class MemoryEngine {
|
|
|
877
882
|
reason: `contradiction_merged: ${arbitration.reason ?? 'unknown'}`,
|
|
878
883
|
});
|
|
879
884
|
emitSemanticControlLog({
|
|
880
|
-
event: 'memory.write.completed', identity: { projectRoot: this.rootPath, sessionId:
|
|
885
|
+
event: 'memory.write.completed', identity: { projectRoot: this.rootPath, sessionId: provenance.sessionId }, level: 'info', timestamp: Date.now(),
|
|
881
886
|
data: { stage: 'contradiction_arbitration', operation: 'merge', conflictingMemoryId: arbitration.conflictingId, reason: arbitration.reason },
|
|
882
887
|
});
|
|
883
888
|
const mergedRow = this.store.get(arbitration.conflictingId);
|
|
@@ -888,20 +893,21 @@ export class MemoryEngine {
|
|
|
888
893
|
}
|
|
889
894
|
catch (err) {
|
|
890
895
|
emitSemanticControlLog({
|
|
891
|
-
event: 'memory.write.degraded', identity: { projectRoot: this.rootPath, sessionId:
|
|
896
|
+
event: 'memory.write.degraded', identity: { projectRoot: this.rootPath, sessionId: provenance.sessionId }, level: 'warn', timestamp: Date.now(),
|
|
892
897
|
data: { stage: 'contradiction_detection', reason: memoryEngineErrorReason(err), fallback: 'write_without_arbitration' },
|
|
893
898
|
});
|
|
894
899
|
}
|
|
895
900
|
}
|
|
896
901
|
const row = this.store.add({
|
|
897
|
-
|
|
902
|
+
actorId: provenance.actorId,
|
|
903
|
+
type: entry.category,
|
|
898
904
|
content: entry.content,
|
|
899
905
|
originalText: entry.originalText,
|
|
900
906
|
source: entry.source,
|
|
901
907
|
status: insertStatus,
|
|
902
908
|
confidence: entry.confidence,
|
|
903
909
|
scope: entry.scope,
|
|
904
|
-
sessionId:
|
|
910
|
+
sessionId: provenance.sessionId,
|
|
905
911
|
});
|
|
906
912
|
// 4. Audit: record ADD event
|
|
907
913
|
this.store.recordHistory({
|
|
@@ -927,10 +933,42 @@ export class MemoryEngine {
|
|
|
927
933
|
}
|
|
928
934
|
reconcileRuleStatus(row, requestedStatus) {
|
|
929
935
|
if (requestedStatus && row.source === 'rule_engine' && row.status !== requestedStatus) {
|
|
930
|
-
this.
|
|
936
|
+
this.updateStoredRow(row, { status: requestedStatus });
|
|
931
937
|
row.status = requestedStatus;
|
|
932
938
|
}
|
|
933
939
|
}
|
|
940
|
+
authorizeMutation(row, provenance) {
|
|
941
|
+
const requestScope = compileRetrievalScope({
|
|
942
|
+
channel: 'memory', operation: 'write',
|
|
943
|
+
...this.projectIdentity, ...provenance,
|
|
944
|
+
});
|
|
945
|
+
const authorized = authorizeMemoryMutation({
|
|
946
|
+
id: row.id,
|
|
947
|
+
requestScope,
|
|
948
|
+
storedScope: {
|
|
949
|
+
projectId: row.project_id,
|
|
950
|
+
memoryNamespace: row.memory_namespace,
|
|
951
|
+
actorId: row.actor_id,
|
|
952
|
+
sessionId: row.session_id ?? '',
|
|
953
|
+
},
|
|
954
|
+
});
|
|
955
|
+
return {
|
|
956
|
+
id: authorized.id,
|
|
957
|
+
projectId: authorized.scope.projectId,
|
|
958
|
+
memoryNamespace: authorized.scope.memoryNamespace,
|
|
959
|
+
actorId: authorized.scope.actorId,
|
|
960
|
+
sessionId: authorized.scope.sessionId,
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
updateStoredRow(row, updates) {
|
|
964
|
+
this.store.updateScoped({
|
|
965
|
+
id: row.id,
|
|
966
|
+
projectId: row.project_id,
|
|
967
|
+
memoryNamespace: row.memory_namespace,
|
|
968
|
+
actorId: row.actor_id,
|
|
969
|
+
sessionId: row.session_id ?? '',
|
|
970
|
+
}, updates);
|
|
971
|
+
}
|
|
934
972
|
/** Map internal MemoryRow → SDK MemoryEntryRecord */
|
|
935
973
|
_rowToRecord(row) {
|
|
936
974
|
const sdkStatus = row.status === 'active' ? 'approved' :
|
|
@@ -938,6 +976,9 @@ export class MemoryEngine {
|
|
|
938
976
|
row.status;
|
|
939
977
|
return {
|
|
940
978
|
id: row.id,
|
|
979
|
+
projectId: row.project_id,
|
|
980
|
+
memoryNamespace: row.memory_namespace,
|
|
981
|
+
actorId: row.actor_id,
|
|
941
982
|
category: row.type,
|
|
942
983
|
content: row.content,
|
|
943
984
|
originalText: row.original_text ?? undefined,
|
|
@@ -950,9 +991,12 @@ export class MemoryEngine {
|
|
|
950
991
|
approvedAt: (row.status === 'active' || row.status === 'approved') ? row.updated_at : undefined,
|
|
951
992
|
};
|
|
952
993
|
}
|
|
953
|
-
async updateMemory(id, updates) {
|
|
994
|
+
async updateMemory(id, updates, provenance) {
|
|
954
995
|
const old = this.store.get(id);
|
|
955
|
-
|
|
996
|
+
if (!old)
|
|
997
|
+
throw new Error('MEMORY_ID_NOT_FOUND');
|
|
998
|
+
const scope = this.authorizeMutation(old, provenance);
|
|
999
|
+
this.store.updateScoped(scope, stripMutableIdentityMetadata(updates));
|
|
956
1000
|
if (old) {
|
|
957
1001
|
this.store.recordHistory({
|
|
958
1002
|
memoryId: id,
|
|
@@ -965,31 +1009,32 @@ export class MemoryEngine {
|
|
|
965
1009
|
});
|
|
966
1010
|
}
|
|
967
1011
|
}
|
|
968
|
-
async deleteMemory(id) {
|
|
1012
|
+
async deleteMemory(id, provenance) {
|
|
969
1013
|
const old = this.store.get(id);
|
|
970
|
-
if (old)
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
1014
|
+
if (!old)
|
|
1015
|
+
throw new Error('MEMORY_ID_NOT_FOUND');
|
|
1016
|
+
const scope = this.authorizeMutation(old, provenance);
|
|
1017
|
+
this.store.recordHistory({
|
|
1018
|
+
memoryId: id,
|
|
1019
|
+
event: 'DELETE',
|
|
1020
|
+
oldContent: old.content,
|
|
1021
|
+
reason: 'manual_delete',
|
|
1022
|
+
});
|
|
1023
|
+
this.store.deleteScoped(scope);
|
|
979
1024
|
}
|
|
980
1025
|
async getPendingMemories() {
|
|
981
1026
|
return this.listMemories({ status: 'pending' });
|
|
982
1027
|
}
|
|
983
|
-
async approveMemory(id) {
|
|
984
|
-
this.
|
|
1028
|
+
async approveMemory(id, provenance) {
|
|
1029
|
+
await this.updateMemory(id, { status: 'approved' }, provenance);
|
|
985
1030
|
this.store.recordHistory({
|
|
986
1031
|
memoryId: id,
|
|
987
1032
|
event: 'REINFORCE',
|
|
988
1033
|
reason: 'user_approved',
|
|
989
1034
|
});
|
|
990
1035
|
}
|
|
991
|
-
async rejectMemory(id) {
|
|
992
|
-
this.
|
|
1036
|
+
async rejectMemory(id, provenance) {
|
|
1037
|
+
await this.deleteMemory(id, provenance);
|
|
993
1038
|
}
|
|
994
1039
|
// --- Session Consolidation & Contradiction Detection ---
|
|
995
1040
|
async consolidateSession(sessionId) {
|
|
@@ -1014,7 +1059,7 @@ export class MemoryEngine {
|
|
|
1014
1059
|
source: 'auto',
|
|
1015
1060
|
confidence: 0.5,
|
|
1016
1061
|
scope: sessionId,
|
|
1017
|
-
});
|
|
1062
|
+
}, { actorId: 'actor:devflow-consolidator', sessionId });
|
|
1018
1063
|
const expiresAt = Date.now() + 30 * 24 * 60 * 60 * 1000;
|
|
1019
1064
|
this.store.setExpires(entry.id, expiresAt);
|
|
1020
1065
|
return entry;
|
|
@@ -1062,6 +1107,7 @@ export class MemoryEngine {
|
|
|
1062
1107
|
return { decision: 'keep' };
|
|
1063
1108
|
}
|
|
1064
1109
|
close() {
|
|
1110
|
+
this.commands.close();
|
|
1065
1111
|
this.store.close();
|
|
1066
1112
|
}
|
|
1067
1113
|
// --- Event Recording & Extraction Pipeline ---
|
|
@@ -1106,7 +1152,7 @@ export class MemoryEngine {
|
|
|
1106
1152
|
const existing = existingByContent.get(candidate.content);
|
|
1107
1153
|
if (existing) {
|
|
1108
1154
|
if (existing.source === 'rule_engine' && existing.status !== desiredStatus) {
|
|
1109
|
-
this.
|
|
1155
|
+
this.updateStoredRow(existing, { status: desiredStatus });
|
|
1110
1156
|
existing.status = desiredStatus;
|
|
1111
1157
|
}
|
|
1112
1158
|
continue;
|
|
@@ -1118,6 +1164,9 @@ export class MemoryEngine {
|
|
|
1118
1164
|
source: 'rule_engine',
|
|
1119
1165
|
confidence: candidate.confidence,
|
|
1120
1166
|
scope: '',
|
|
1167
|
+
}, {
|
|
1168
|
+
actorId: 'actor:devflow-rule-engine',
|
|
1169
|
+
sessionId: 'session:rule-engine',
|
|
1121
1170
|
}, desiredStatus, { indexDerived: false });
|
|
1122
1171
|
saved.push(entry);
|
|
1123
1172
|
existingByContent.set(candidate.content, this.store.get(entry.id));
|