@jungjaehoon/mama-core 1.7.0 → 1.8.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/db/migrations/038-create-vnext-operator-contracts.sql +57 -0
- package/db/migrations/039-add-connector-event-operator-ingest-seq.sql +96 -0
- package/db/migrations/040-create-operator-memory-commit-intents.sql +19 -0
- package/db/migrations/041-enforce-operator-memory-commit-claim-invariant.sql +74 -0
- package/db/migrations/042-embedding-prefix-scheme.sql +46 -0
- package/dist/db-adapter/node-sqlite-adapter.d.ts +14 -1
- package/dist/db-adapter/node-sqlite-adapter.js +323 -6
- package/dist/db-manager.d.ts +9 -10
- package/dist/db-manager.js +59 -4
- package/dist/embedding-client.d.ts +3 -1
- package/dist/embedding-client.js +3 -2
- package/dist/embedding-server/index.d.ts +2 -0
- package/dist/embedding-server/index.js +10 -2
- package/dist/embeddings.d.ts +5 -3
- package/dist/embeddings.js +16 -10
- package/dist/index.d.ts +3 -2
- package/dist/index.js +7 -4
- package/dist/mama-api.js +1 -1
- package/dist/memory/api.d.ts +6 -1
- package/dist/memory/api.js +223 -29
- package/dist/memory/provenance.d.ts +1 -0
- package/dist/memory/provenance.js +1 -0
- package/dist/memory-inject.js +1 -1
- package/dist/provenance/source-ref.d.ts +29 -0
- package/dist/provenance/source-ref.js +204 -0
- package/package.json +2 -1
|
@@ -18,6 +18,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
18
18
|
};
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
20
|
exports.PORT_FILE = exports.HOST = exports.DEFAULT_PORT = exports.SHUTDOWN_TOKEN = void 0;
|
|
21
|
+
exports.resolveEmbeddingRole = resolveEmbeddingRole;
|
|
21
22
|
exports.cleanupPortFile = cleanupPortFile;
|
|
22
23
|
exports.startEmbeddingServer = startEmbeddingServer;
|
|
23
24
|
exports.warmModel = warmModel;
|
|
@@ -63,6 +64,11 @@ const connections = new Set();
|
|
|
63
64
|
// Graph handler (optional, passed from standalone)
|
|
64
65
|
let graphHandler = null;
|
|
65
66
|
let _wssInstance = null;
|
|
67
|
+
// Resolve the e5 role from an HTTP body value. Absent/unknown -> passage, so old
|
|
68
|
+
// clients that never send a role keep getting passage vectors (backward compatible).
|
|
69
|
+
function resolveEmbeddingRole(raw) {
|
|
70
|
+
return raw === 'query' ? 'query' : 'passage';
|
|
71
|
+
}
|
|
66
72
|
/**
|
|
67
73
|
* Read request body as JSON
|
|
68
74
|
*/
|
|
@@ -158,7 +164,8 @@ async function handleRequest(req, res) {
|
|
|
158
164
|
res.end(JSON.stringify({ error: 'Missing or invalid "text" field' }));
|
|
159
165
|
return;
|
|
160
166
|
}
|
|
161
|
-
const
|
|
167
|
+
const role = resolveEmbeddingRole(body.role);
|
|
168
|
+
const embedding = await (0, embeddings_js_1.generateEmbedding)(body.text, role);
|
|
162
169
|
modelLoaded = true;
|
|
163
170
|
const latency = Date.now() - startTime;
|
|
164
171
|
res.writeHead(200);
|
|
@@ -186,7 +193,8 @@ async function handleRequest(req, res) {
|
|
|
186
193
|
res.end(JSON.stringify({ error: 'Missing or invalid "texts" array' }));
|
|
187
194
|
return;
|
|
188
195
|
}
|
|
189
|
-
const
|
|
196
|
+
const role = resolveEmbeddingRole(body.role);
|
|
197
|
+
const embeddings = await Promise.all(body.texts.map((text) => (0, embeddings_js_1.generateEmbedding)(text, role).then((e) => Array.from(e))));
|
|
190
198
|
modelLoaded = true;
|
|
191
199
|
const latency = Date.now() - startTime;
|
|
192
200
|
res.writeHead(200);
|
package/dist/embeddings.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ import { embeddingCache } from './embedding-cache.js';
|
|
|
13
13
|
import { loadConfig, getModelName, getEmbeddingDim, getQuantized } from './config-loader.js';
|
|
14
14
|
export declare const EMBEDDING_MODEL_MAX_LENGTH = 512;
|
|
15
15
|
export declare const EMBEDDING_MAX_TOKENISH_SEGMENTS = 480;
|
|
16
|
+
export type EmbeddingRole = 'passage' | 'query';
|
|
17
|
+
export declare const EMBEDDING_PREFIX_SCHEME = "e5-prefixed-v1";
|
|
16
18
|
export declare function prepareEmbeddingText(text: string): string;
|
|
17
19
|
/**
|
|
18
20
|
* Decision object for enhanced embedding generation
|
|
@@ -38,7 +40,7 @@ export interface DecisionForEmbedding {
|
|
|
38
40
|
* @returns Embedding vector (dimension from config)
|
|
39
41
|
* @throws Error if text is empty or embedding fails
|
|
40
42
|
*/
|
|
41
|
-
export declare function generateEmbedding(text: string): Promise<Float32Array>;
|
|
43
|
+
export declare function generateEmbedding(text: string, role?: EmbeddingRole): Promise<Float32Array>;
|
|
42
44
|
/**
|
|
43
45
|
* Generate enhanced embedding with content + metadata
|
|
44
46
|
*
|
|
@@ -48,7 +50,7 @@ export declare function generateEmbedding(text: string): Promise<Float32Array>;
|
|
|
48
50
|
* @param decision - Decision object
|
|
49
51
|
* @returns 1024-dim enhanced embedding
|
|
50
52
|
*/
|
|
51
|
-
export declare function generateEnhancedEmbedding(decision: DecisionForEmbedding): Promise<Float32Array>;
|
|
53
|
+
export declare function generateEnhancedEmbedding(decision: DecisionForEmbedding, role?: EmbeddingRole): Promise<Float32Array>;
|
|
52
54
|
/**
|
|
53
55
|
* Batch generate embeddings (optimized)
|
|
54
56
|
*
|
|
@@ -60,7 +62,7 @@ export declare function generateEnhancedEmbedding(decision: DecisionForEmbedding
|
|
|
60
62
|
* @param texts - Array of texts to embed (max 10 per batch)
|
|
61
63
|
* @returns Array of embeddings
|
|
62
64
|
*/
|
|
63
|
-
export declare function generateBatchEmbeddings(texts: string[]): Promise<Float32Array[]>;
|
|
65
|
+
export declare function generateBatchEmbeddings(texts: string[], role?: EmbeddingRole): Promise<Float32Array[]>;
|
|
64
66
|
/**
|
|
65
67
|
* Calculate cosine similarity between two embeddings
|
|
66
68
|
*
|
package/dist/embeddings.js
CHANGED
|
@@ -14,7 +14,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
14
14
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.getQuantized = exports.getEmbeddingDim = exports.getModelName = exports.loadConfig = exports.MODEL_NAME = exports.EMBEDDING_DIM = exports.embeddingCache = exports.EMBEDDING_MAX_TOKENISH_SEGMENTS = exports.EMBEDDING_MODEL_MAX_LENGTH = void 0;
|
|
17
|
+
exports.getQuantized = exports.getEmbeddingDim = exports.getModelName = exports.loadConfig = exports.MODEL_NAME = exports.EMBEDDING_DIM = exports.embeddingCache = exports.EMBEDDING_PREFIX_SCHEME = exports.EMBEDDING_MAX_TOKENISH_SEGMENTS = exports.EMBEDDING_MODEL_MAX_LENGTH = void 0;
|
|
18
18
|
exports.prepareEmbeddingText = prepareEmbeddingText;
|
|
19
19
|
exports.generateEmbedding = generateEmbedding;
|
|
20
20
|
exports.generateEnhancedEmbedding = generateEnhancedEmbedding;
|
|
@@ -36,6 +36,10 @@ const DEFAULT_CACHE_DIR = path_1.default.join(os_1.default.homedir(), '.cache',
|
|
|
36
36
|
const TIER3_ENV_VALUES = new Set(['1', 'true', 'yes']);
|
|
37
37
|
exports.EMBEDDING_MODEL_MAX_LENGTH = 512;
|
|
38
38
|
exports.EMBEDDING_MAX_TOKENISH_SEGMENTS = 480;
|
|
39
|
+
exports.EMBEDDING_PREFIX_SCHEME = 'e5-prefixed-v1';
|
|
40
|
+
function applyRolePrefix(preparedText, role) {
|
|
41
|
+
return `${role}: ${preparedText}`;
|
|
42
|
+
}
|
|
39
43
|
const TOKENISH_SEGMENT_PATTERN = /[\p{Script=Hangul}\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}]|\S+/gu;
|
|
40
44
|
// Singleton pattern for model loading
|
|
41
45
|
let embeddingPipeline = null;
|
|
@@ -128,14 +132,15 @@ async function loadModel() {
|
|
|
128
132
|
* @returns Embedding vector (dimension from config)
|
|
129
133
|
* @throws Error if text is empty or embedding fails
|
|
130
134
|
*/
|
|
131
|
-
async function generateEmbedding(text) {
|
|
135
|
+
async function generateEmbedding(text, role = 'passage') {
|
|
132
136
|
if (typeof text !== 'string' || text.trim().length === 0) {
|
|
133
137
|
throw new Error('Text cannot be empty');
|
|
134
138
|
}
|
|
135
139
|
assertEmbeddingsEnabled();
|
|
136
140
|
const preparedText = prepareEmbeddingText(text);
|
|
137
|
-
|
|
138
|
-
|
|
141
|
+
const modelInput = applyRolePrefix(preparedText, role); // e5 instruction prefix
|
|
142
|
+
// Cache key is the PREFIXED input: a passage vector can never be served for a query.
|
|
143
|
+
const cached = embedding_cache_js_1.embeddingCache.get(modelInput);
|
|
139
144
|
if (cached) {
|
|
140
145
|
return cached;
|
|
141
146
|
}
|
|
@@ -144,7 +149,7 @@ async function generateEmbedding(text) {
|
|
|
144
149
|
const model = await loadModel();
|
|
145
150
|
const expectedDim = (0, config_loader_js_1.getEmbeddingDim)();
|
|
146
151
|
// Generate embedding
|
|
147
|
-
const output = await model(
|
|
152
|
+
const output = await model(modelInput, {
|
|
148
153
|
pooling: 'mean', // Mean pooling over tokens
|
|
149
154
|
normalize: true, // L2 normalization
|
|
150
155
|
truncation: true,
|
|
@@ -159,7 +164,7 @@ async function generateEmbedding(text) {
|
|
|
159
164
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
160
165
|
const _latency = Date.now() - startTime;
|
|
161
166
|
// Task 2: Store in cache (AC #3)
|
|
162
|
-
embedding_cache_js_1.embeddingCache.set(
|
|
167
|
+
embedding_cache_js_1.embeddingCache.set(modelInput, embedding);
|
|
163
168
|
return embedding;
|
|
164
169
|
}
|
|
165
170
|
catch (error) {
|
|
@@ -176,7 +181,7 @@ async function generateEmbedding(text) {
|
|
|
176
181
|
* @param decision - Decision object
|
|
177
182
|
* @returns 1024-dim enhanced embedding
|
|
178
183
|
*/
|
|
179
|
-
async function generateEnhancedEmbedding(decision) {
|
|
184
|
+
async function generateEnhancedEmbedding(decision, role = 'passage') {
|
|
180
185
|
// Construct enriched text representation with narrative fields (Story 2.2)
|
|
181
186
|
const parts = [
|
|
182
187
|
`Topic: ${decision.topic}`,
|
|
@@ -207,7 +212,7 @@ async function generateEnhancedEmbedding(decision) {
|
|
|
207
212
|
parts.push(`Risks: ${decision.risks}`);
|
|
208
213
|
}
|
|
209
214
|
const enrichedText = parts.join('\n').trim();
|
|
210
|
-
return generateEmbedding(enrichedText);
|
|
215
|
+
return generateEmbedding(enrichedText, role);
|
|
211
216
|
}
|
|
212
217
|
/**
|
|
213
218
|
* Batch generate embeddings (optimized)
|
|
@@ -220,7 +225,7 @@ async function generateEnhancedEmbedding(decision) {
|
|
|
220
225
|
* @param texts - Array of texts to embed (max 10 per batch)
|
|
221
226
|
* @returns Array of embeddings
|
|
222
227
|
*/
|
|
223
|
-
async function generateBatchEmbeddings(texts) {
|
|
228
|
+
async function generateBatchEmbeddings(texts, role = 'passage') {
|
|
224
229
|
if (!Array.isArray(texts) || texts.length === 0) {
|
|
225
230
|
throw new Error('Texts must be a non-empty array');
|
|
226
231
|
}
|
|
@@ -236,13 +241,14 @@ async function generateBatchEmbeddings(texts) {
|
|
|
236
241
|
throw new Error('All texts must be non-empty strings');
|
|
237
242
|
}
|
|
238
243
|
}
|
|
244
|
+
const modelInputs = preparedTexts.map((t) => applyRolePrefix(t, role));
|
|
239
245
|
const startTime = Date.now();
|
|
240
246
|
try {
|
|
241
247
|
const model = await loadModel();
|
|
242
248
|
const expectedDim = (0, config_loader_js_1.getEmbeddingDim)();
|
|
243
249
|
// Native batch processing - single model forward pass
|
|
244
250
|
// This is significantly faster than sequential calls
|
|
245
|
-
const outputs = await model(
|
|
251
|
+
const outputs = await model(modelInputs, {
|
|
246
252
|
pooling: 'mean',
|
|
247
253
|
normalize: true,
|
|
248
254
|
truncation: true,
|
package/dist/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* @module mama-core
|
|
8
8
|
* @version 1.0.0
|
|
9
9
|
*/
|
|
10
|
-
export { generateEmbedding, generateEnhancedEmbedding, generateBatchEmbeddings, cosineSimilarity, embeddingCache, EMBEDDING_DIM, MODEL_NAME, } from './embeddings.js';
|
|
10
|
+
export { generateEmbedding, generateEnhancedEmbedding, generateBatchEmbeddings, cosineSimilarity, embeddingCache, EMBEDDING_DIM, MODEL_NAME, EMBEDDING_PREFIX_SCHEME, type EmbeddingRole, } from './embeddings.js';
|
|
11
11
|
export { EmbeddingCache } from './embedding-cache.js';
|
|
12
12
|
export { getEmbeddingFromServer, getServerStatus, isServerRunning, getServerPort, DEFAULT_PORT, HOST, TIMEOUT_MS, type ServerStatus, } from './embedding-client.js';
|
|
13
13
|
export { initDB, getDB, getAdapter, closeDB, insertEmbedding, vectorSearch, queryVectorSearch, insertDecisionWithEmbedding, queryDecisionGraph, querySemanticEdges, updateDecisionOutcome, getPreparedStmt, getDbPath, fts5Search, reindexEmbeddings, type DatabaseAdapter as DBManagerAdapter, type PreparedStatement, type DecisionRecord, type OutcomeData, type VectorSearchParams, type SemanticEdges, type SemanticEdgeItem, type DecisionInput, } from './db-manager.js';
|
|
@@ -16,7 +16,7 @@ export { getSessionDecisions, incrementUsageSuccess, incrementUsageFailure, getD
|
|
|
16
16
|
import mama from './mama-api.js';
|
|
17
17
|
export { mama };
|
|
18
18
|
export { MEMORY_SCOPE_KINDS, MEMORY_KINDS, MEMORY_STATUSES, MEMORY_EDGE_TYPES, createEmptyRecallBundle, type MemoryScopeKind, type MemoryKind, type MemoryStatus, type MemoryEdgeType, type MemoryScopeRef, type MemorySourceRef, type MemoryRecord, type MemoryWriteProvenance, type MemoryProvenanceRecord, type PublicSaveMemoryInput, type PublicIngestMemoryInput, type PublicIngestConversationInput, type MemorySearchResultHit, type MemoryEdge, type ProfileSnapshot, type RecallBundle, type ConversationMessage, type IngestConversationInput, type ExtractedMemoryUnit, type IngestConversationResult, } from './memory/types.js';
|
|
19
|
-
export { saveMemory, saveMemoryWithTrustedProvenance, recallMemory, buildProfile, ingestMemory, ingestWithTrustedProvenance, evolveMemory, buildMemoryBootstrap, createAuditAck, recordMemoryAudit, ingestConversation, ingestConversationWithTrustedProvenance, setExtractionFn, upsertChannelSummary, getChannelSummary, } from './memory/api.js';
|
|
19
|
+
export { saveMemory, saveMemoryWithTrustedProvenance, promoteMemoryStatus, recallMemory, buildProfile, ingestMemory, ingestWithTrustedProvenance, evolveMemory, buildMemoryBootstrap, createAuditAck, recordMemoryAudit, ingestConversation, ingestConversationWithTrustedProvenance, setExtractionFn, upsertChannelSummary, getChannelSummary, } from './memory/api.js';
|
|
20
20
|
export { buildExtractionPrompt, parseExtractionResponse } from './memory/extraction-prompt.js';
|
|
21
21
|
export { appendMemoryEvent, insertMemoryEventInTransaction, listMemoryEventsForMemory, listRecentMemoryEvents, } from './memory/event-store.js';
|
|
22
22
|
export { createTrustedProvenanceCapability, assertTrustedProvenanceCapability, type TrustedProvenanceCapability, type TrustedMemoryWriteOptions, } from './memory/provenance.js';
|
|
@@ -30,6 +30,7 @@ export { TWIN_EDGE_SOURCES, TWIN_EDGE_TYPES, TWIN_REF_KINDS, type InsertTwinEdge
|
|
|
30
30
|
export { getTwinEdge, insertTwinEdge, listTwinEdgesForRefs, mapTwinEdgeRow, } from './edges/store.js';
|
|
31
31
|
export { assertTwinRefsVisibleToScopes, listVisibleTwinEdgesForRefs, } from './edges/ref-validation.js';
|
|
32
32
|
export * from './context-compile/index.js';
|
|
33
|
+
export * from './provenance/source-ref.js';
|
|
33
34
|
export * from './agent-situation/index.js';
|
|
34
35
|
export * from './agent-graph/index.js';
|
|
35
36
|
export * from './entities/types.js';
|
package/dist/index.js
CHANGED
|
@@ -26,10 +26,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
26
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
exports.
|
|
30
|
-
exports.
|
|
31
|
-
exports.
|
|
32
|
-
exports.notifyInsight = exports.FALLBACK_MODEL = exports.DEFAULT_MODEL = exports.listModels = exports.isAvailable = exports.analyzeQueryIntent = exports.analyzeDecision = exports.generate = exports.extractTopicKeywords = exports.analyzeIntent = exports.injectDecisionContext = exports.RECENT_WINDOW_MS = exports.PARTIAL_INDICATORS = exports.SUCCESS_INDICATORS = exports.FAILURE_INDICATORS = exports.onUserPromptSubmit = exports.markOutcome = exports.getEvidenceImpact = exports.calculateDurationDays = exports.getRecentDecision = exports.extractFailureReason = exports.matchesPartialIndicators = exports.matchesSuccessIndicators = exports.matchesFailureIndicators = exports.analyzeOutcome = exports.extractCodeExample = exports.extractQuickAnswer = exports.estimateTokens = exports.ensureTokenBudget = exports.formatTrustContext = exports.formatInstantAnswer = exports.formatTeaser = exports.formatList = exports.formatRecall = exports.formatLegacyContext = exports.formatContext = exports.isMAMAError = exports.wrapError = exports.ErrorCodes = exports.TimeoutError = exports.RateLimitError = exports.LinkError = exports.ConfigurationError = exports.EmbeddingError = void 0;
|
|
29
|
+
exports.saveMemory = exports.createEmptyRecallBundle = exports.MEMORY_EDGE_TYPES = exports.MEMORY_STATUSES = exports.MEMORY_KINDS = exports.MEMORY_SCOPE_KINDS = exports.mama = exports.DEFAULT_DB_PATH = exports.LEGACY_DB_PATH = exports.DB_DIR = exports.DB_PATH = exports.traverseDecisionChain = exports.getDecisionById = exports.incrementUsageFailure = exports.incrementUsageSuccess = exports.getSessionDecisions = exports.SQLiteAdapter = exports.DatabaseAdapter = exports.createAdapter = exports.reindexEmbeddings = exports.fts5Search = exports.getDbPath = exports.getPreparedStmt = exports.updateDecisionOutcome = exports.querySemanticEdges = exports.queryDecisionGraph = exports.insertDecisionWithEmbedding = exports.queryVectorSearch = exports.vectorSearch = exports.insertEmbedding = exports.closeDB = exports.getAdapter = exports.getDB = exports.initDB = exports.TIMEOUT_MS = exports.HOST = exports.DEFAULT_PORT = exports.getServerPort = exports.isServerRunning = exports.getServerStatus = exports.getEmbeddingFromServer = exports.EmbeddingCache = exports.EMBEDDING_PREFIX_SCHEME = exports.MODEL_NAME = exports.EMBEDDING_DIM = exports.embeddingCache = exports.cosineSimilarity = exports.generateBatchEmbeddings = exports.generateEnhancedEmbedding = exports.generateEmbedding = void 0;
|
|
30
|
+
exports.assertTwinRefsVisibleToScopes = exports.mapTwinEdgeRow = exports.listTwinEdgesForRefs = exports.insertTwinEdge = exports.getTwinEdge = exports.TWIN_REF_KINDS = exports.TWIN_EDGE_TYPES = exports.TWIN_EDGE_SOURCES = exports.listToolTracesForRun = exports.appendToolTrace = exports.getModelRunInAdapter = exports.getModelRun = exports.failModelRunInAdapter = exports.failModelRun = exports.commitModelRunInAdapter = exports.commitModelRun = exports.beginModelRunInAdapter = exports.beginModelRun = exports.MODEL_RUN_STATUSES = exports.listMemoryProvenanceAudit = exports.getMemoryProvenanceAudit = exports.backfillConnectorEventScopeMetadata = exports.backfillLegacyMemoryProvenance = exports.listMemoriesByModelRunId = exports.listMemoriesByGatewayCallId = exports.listMemoriesByEnvelopeHash = exports.getMemoryProvenance = exports.assertTrustedProvenanceCapability = exports.createTrustedProvenanceCapability = exports.listRecentMemoryEvents = exports.listMemoryEventsForMemory = exports.insertMemoryEventInTransaction = exports.appendMemoryEvent = exports.parseExtractionResponse = exports.buildExtractionPrompt = exports.getChannelSummary = exports.upsertChannelSummary = exports.setExtractionFn = exports.ingestConversationWithTrustedProvenance = exports.ingestConversation = exports.recordMemoryAudit = exports.createAuditAck = exports.buildMemoryBootstrap = exports.evolveMemory = exports.ingestWithTrustedProvenance = exports.ingestMemory = exports.buildProfile = exports.recallMemory = exports.promoteMemoryStatus = exports.saveMemoryWithTrustedProvenance = void 0;
|
|
31
|
+
exports.NotFoundError = exports.MAMAError = exports.formatTimeAgo = exports.DebugLogger = exports.error = exports.warn = exports.info = exports.debug = exports.logSearching = exports.logLoading = exports.logInfo = exports.logError = exports.logFailed = exports.logComplete = exports.logProgress = exports.getTierBanner = exports.getTierDescription = exports.checkDatabase = exports.checkEmbeddings = exports.checkSQLite = exports.checkNodeVersion = exports.validateTier = exports.VALID_EDGE_TYPES = exports.updateConfidence = exports.getSupersededChainDepth = exports.createEdgesFromReasoning = exports.parseReasoningForRelationships = exports.detectRefinement = exports.calculateCombinedConfidence = exports.markSuperseded = exports.createSupersedesEdge = exports.createEdge = exports.getPreviousDecision = exports.generateDecisionId = exports.learnDecision = exports.testRelevanceScoring = exports.formatTopNContext = exports.selectTopDecisions = exports.calculateRelevance = exports.DEFAULT_CONFIG = exports.getConfigPath = exports.updateConfig = exports.getCacheDir = exports.getEmbeddingDim = exports.getModelName = exports.loadConfig = exports.CanonicalizeError = exports.targetRefHash = exports.canonicalizeJSON = exports.listVisibleTwinEdgesForRefs = void 0;
|
|
32
|
+
exports.notifyInsight = exports.FALLBACK_MODEL = exports.DEFAULT_MODEL = exports.listModels = exports.isAvailable = exports.analyzeQueryIntent = exports.analyzeDecision = exports.generate = exports.extractTopicKeywords = exports.analyzeIntent = exports.injectDecisionContext = exports.RECENT_WINDOW_MS = exports.PARTIAL_INDICATORS = exports.SUCCESS_INDICATORS = exports.FAILURE_INDICATORS = exports.onUserPromptSubmit = exports.markOutcome = exports.getEvidenceImpact = exports.calculateDurationDays = exports.getRecentDecision = exports.extractFailureReason = exports.matchesPartialIndicators = exports.matchesSuccessIndicators = exports.matchesFailureIndicators = exports.analyzeOutcome = exports.extractCodeExample = exports.extractQuickAnswer = exports.estimateTokens = exports.ensureTokenBudget = exports.formatTrustContext = exports.formatInstantAnswer = exports.formatTeaser = exports.formatList = exports.formatRecall = exports.formatLegacyContext = exports.formatContext = exports.isMAMAError = exports.wrapError = exports.ErrorCodes = exports.TimeoutError = exports.RateLimitError = exports.LinkError = exports.ConfigurationError = exports.EmbeddingError = exports.DatabaseError = exports.ValidationError = void 0;
|
|
33
33
|
var embeddings_js_1 = require("./embeddings.js");
|
|
34
34
|
Object.defineProperty(exports, "generateEmbedding", { enumerable: true, get: function () { return embeddings_js_1.generateEmbedding; } });
|
|
35
35
|
Object.defineProperty(exports, "generateEnhancedEmbedding", { enumerable: true, get: function () { return embeddings_js_1.generateEnhancedEmbedding; } });
|
|
@@ -38,6 +38,7 @@ Object.defineProperty(exports, "cosineSimilarity", { enumerable: true, get: func
|
|
|
38
38
|
Object.defineProperty(exports, "embeddingCache", { enumerable: true, get: function () { return embeddings_js_1.embeddingCache; } });
|
|
39
39
|
Object.defineProperty(exports, "EMBEDDING_DIM", { enumerable: true, get: function () { return embeddings_js_1.EMBEDDING_DIM; } });
|
|
40
40
|
Object.defineProperty(exports, "MODEL_NAME", { enumerable: true, get: function () { return embeddings_js_1.MODEL_NAME; } });
|
|
41
|
+
Object.defineProperty(exports, "EMBEDDING_PREFIX_SCHEME", { enumerable: true, get: function () { return embeddings_js_1.EMBEDDING_PREFIX_SCHEME; } });
|
|
41
42
|
var embedding_cache_js_1 = require("./embedding-cache.js");
|
|
42
43
|
Object.defineProperty(exports, "EmbeddingCache", { enumerable: true, get: function () { return embedding_cache_js_1.EmbeddingCache; } });
|
|
43
44
|
var embedding_client_js_1 = require("./embedding-client.js");
|
|
@@ -89,6 +90,7 @@ Object.defineProperty(exports, "createEmptyRecallBundle", { enumerable: true, ge
|
|
|
89
90
|
var api_js_1 = require("./memory/api.js");
|
|
90
91
|
Object.defineProperty(exports, "saveMemory", { enumerable: true, get: function () { return api_js_1.saveMemory; } });
|
|
91
92
|
Object.defineProperty(exports, "saveMemoryWithTrustedProvenance", { enumerable: true, get: function () { return api_js_1.saveMemoryWithTrustedProvenance; } });
|
|
93
|
+
Object.defineProperty(exports, "promoteMemoryStatus", { enumerable: true, get: function () { return api_js_1.promoteMemoryStatus; } });
|
|
92
94
|
Object.defineProperty(exports, "recallMemory", { enumerable: true, get: function () { return api_js_1.recallMemory; } });
|
|
93
95
|
Object.defineProperty(exports, "buildProfile", { enumerable: true, get: function () { return api_js_1.buildProfile; } });
|
|
94
96
|
Object.defineProperty(exports, "ingestMemory", { enumerable: true, get: function () { return api_js_1.ingestMemory; } });
|
|
@@ -151,6 +153,7 @@ var ref_validation_js_1 = require("./edges/ref-validation.js");
|
|
|
151
153
|
Object.defineProperty(exports, "assertTwinRefsVisibleToScopes", { enumerable: true, get: function () { return ref_validation_js_1.assertTwinRefsVisibleToScopes; } });
|
|
152
154
|
Object.defineProperty(exports, "listVisibleTwinEdgesForRefs", { enumerable: true, get: function () { return ref_validation_js_1.listVisibleTwinEdgesForRefs; } });
|
|
153
155
|
__exportStar(require("./context-compile/index.js"), exports);
|
|
156
|
+
__exportStar(require("./provenance/source-ref.js"), exports);
|
|
154
157
|
__exportStar(require("./agent-situation/index.js"), exports);
|
|
155
158
|
__exportStar(require("./agent-graph/index.js"), exports);
|
|
156
159
|
__exportStar(require("./entities/types.js"), exports);
|
package/dist/mama-api.js
CHANGED
|
@@ -1118,7 +1118,7 @@ async function suggest(userQuestion, options = {}) {
|
|
|
1118
1118
|
throw new Error('Vector search not available');
|
|
1119
1119
|
}
|
|
1120
1120
|
// Generate query embedding
|
|
1121
|
-
const queryEmbedding = await (0, embeddings_js_1.generateEmbedding)(userQuestion);
|
|
1121
|
+
const queryEmbedding = await (0, embeddings_js_1.generateEmbedding)(userQuestion, 'query');
|
|
1122
1122
|
// Adaptive threshold (shorter queries need higher confidence)
|
|
1123
1123
|
const wordCount = userQuestion.split(/\s+/).length;
|
|
1124
1124
|
const adaptiveThreshold = threshold !== undefined ? threshold : wordCount < 3 ? 0.7 : 0.6;
|
package/dist/memory/api.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { type WikiPageIndexRecord } from '../cases/wiki-page-index.js';
|
|
|
2
2
|
import { resolveMemoryEvolution } from './evolution-engine.js';
|
|
3
3
|
import { getChannelSummary, upsertChannelSummary } from './channel-summary-store.js';
|
|
4
4
|
import { type SearchHitDiagnostics } from '../search/search-quality.js';
|
|
5
|
-
import type { MemoryAgentBootstrap, MemoryAuditAck, MemoryEdge, MemoryRecord, MemoryScopeRef, ProfileSnapshot, PublicIngestMemoryInput, PublicSaveMemoryInput, RecallBundle, IngestConversationInput, ExtractedMemoryUnit, IngestConversationResult, RecallMemoryOptions } from './types.js';
|
|
5
|
+
import type { MemoryAgentBootstrap, MemoryAuditAck, MemoryEdge, MemoryRecord, MemoryScopeRef, MemoryStatus, ProfileSnapshot, PublicIngestMemoryInput, PublicSaveMemoryInput, RecallBundle, IngestConversationInput, ExtractedMemoryUnit, IngestConversationResult, RecallMemoryOptions } from './types.js';
|
|
6
6
|
import { type TrustedMemoryWriteOptions } from './provenance.js';
|
|
7
7
|
type SaveMemoryInput = PublicSaveMemoryInput;
|
|
8
8
|
type IngestMemoryInput = PublicIngestMemoryInput;
|
|
@@ -27,6 +27,11 @@ type SaveMemoryResult = {
|
|
|
27
27
|
};
|
|
28
28
|
export declare function saveMemory(input: SaveMemoryInput): Promise<SaveMemoryResult>;
|
|
29
29
|
export declare function saveMemoryWithTrustedProvenance(input: SaveMemoryInput, options: TrustedMemoryWriteOptions): Promise<SaveMemoryResult>;
|
|
30
|
+
export declare function promoteMemoryStatus(input: {
|
|
31
|
+
memoryId: string;
|
|
32
|
+
status: MemoryStatus;
|
|
33
|
+
nowMs?: number;
|
|
34
|
+
}): Promise<void>;
|
|
30
35
|
export declare function buildProfile(scopes: MemoryScopeRef[]): Promise<ProfileSnapshot>;
|
|
31
36
|
export declare function recallMemory(query: string, options?: RecallMemoryOptions): Promise<RecallBundle>;
|
|
32
37
|
export declare function ingestMemory(input: IngestMemoryInput): Promise<{
|
package/dist/memory/api.js
CHANGED
|
@@ -8,6 +8,7 @@ exports.getLexicalQueryTokens = getLexicalQueryTokens;
|
|
|
8
8
|
exports.loadEdgesForIds = loadEdgesForIds;
|
|
9
9
|
exports.saveMemory = saveMemory;
|
|
10
10
|
exports.saveMemoryWithTrustedProvenance = saveMemoryWithTrustedProvenance;
|
|
11
|
+
exports.promoteMemoryStatus = promoteMemoryStatus;
|
|
11
12
|
exports.buildProfile = buildProfile;
|
|
12
13
|
exports.recallMemory = recallMemory;
|
|
13
14
|
exports.ingestMemory = ingestMemory;
|
|
@@ -442,11 +443,16 @@ async function saveMemoryInternal(input, options) {
|
|
|
442
443
|
const id = buildDecisionId(input.topic);
|
|
443
444
|
const now = Date.now();
|
|
444
445
|
const provenance = (0, provenance_js_1.normalizeMemoryWriteProvenance)(options);
|
|
446
|
+
const targetStatus = input.status ?? 'active';
|
|
447
|
+
const shouldApplyEvolution = targetStatus === 'active';
|
|
445
448
|
// Find evolution candidates: exact topic match first, then semantic search fallback
|
|
446
449
|
const primaryScope = input.scopes.length > 0 ? input.scopes[0] : null;
|
|
447
450
|
const excludeIds = new Set(input.excludeIds ?? []);
|
|
448
451
|
let existingCandidates;
|
|
449
|
-
if (
|
|
452
|
+
if (!shouldApplyEvolution) {
|
|
453
|
+
existingCandidates = [];
|
|
454
|
+
}
|
|
455
|
+
else if (primaryScope) {
|
|
450
456
|
const scopeId = await (0, db_manager_js_1.ensureMemoryScope)(primaryScope.kind, primaryScope.id);
|
|
451
457
|
existingCandidates = adapter
|
|
452
458
|
.prepare(`
|
|
@@ -475,11 +481,14 @@ async function saveMemoryInternal(input, options) {
|
|
|
475
481
|
.all(input.topic).filter((c) => !excludeIds.has(c.id));
|
|
476
482
|
}
|
|
477
483
|
// Semantic fallback: if no exact topic match, find similar memories via vector search
|
|
478
|
-
if (existingCandidates.length === 0) {
|
|
484
|
+
if (shouldApplyEvolution && existingCandidates.length === 0) {
|
|
479
485
|
try {
|
|
480
486
|
const queryText = `${input.topic} ${input.summary}`;
|
|
481
|
-
const embedding = await (0, embeddings_js_1.generateEmbedding)(queryText);
|
|
482
|
-
|
|
487
|
+
const embedding = await (0, embeddings_js_1.generateEmbedding)(queryText, 'query');
|
|
488
|
+
// Exclude superseded history so a dense supersede chain cannot fill all 3
|
|
489
|
+
// candidate slots and hide the prior ACTIVE decision (which would break the
|
|
490
|
+
// evolution link and create an active duplicate).
|
|
491
|
+
const semanticResults = await (0, db_manager_js_1.vectorSearch)(embedding, 3, 0.82, undefined, Array.from(EXCLUDED_STATUSES));
|
|
483
492
|
// Scope-filter semantic candidates when a primary scope is available
|
|
484
493
|
let scopeFiltered = semanticResults;
|
|
485
494
|
if (primaryScope) {
|
|
@@ -509,13 +518,15 @@ async function saveMemoryInternal(input, options) {
|
|
|
509
518
|
// Semantic search unavailable — proceed with empty candidates
|
|
510
519
|
}
|
|
511
520
|
}
|
|
512
|
-
const evolution =
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
521
|
+
const evolution = shouldApplyEvolution
|
|
522
|
+
? (0, evolution_engine_js_1.resolveMemoryEvolution)({
|
|
523
|
+
incoming: { topic: input.topic, summary: input.summary, kind: input.kind },
|
|
524
|
+
existing: existingCandidates.map((c) => ({
|
|
525
|
+
...c,
|
|
526
|
+
kind: (c.kind || 'fact'),
|
|
527
|
+
})),
|
|
528
|
+
})
|
|
529
|
+
: { edges: [] };
|
|
519
530
|
const supersedesTarget = evolution.edges.find((edge) => edge.type === 'supersedes')?.to_id ?? null;
|
|
520
531
|
const entityObservationIds = Array.from(new Set(input.entityObservationIds ?? []));
|
|
521
532
|
const eventDateTime = typeof input.eventDateTime === 'number'
|
|
@@ -556,7 +567,7 @@ async function saveMemoryInternal(input, options) {
|
|
|
556
567
|
SET kind = ?, status = ?, summary = ?, is_static = ?, trust_context = ?, updated_at = ?
|
|
557
568
|
WHERE id = ?
|
|
558
569
|
`)
|
|
559
|
-
.run(input.kind,
|
|
570
|
+
.run(input.kind, targetStatus, input.summary, input.kind === 'preference' || input.kind === 'constraint' ? 1 : 0, JSON.stringify({ source: input.source }), now, id);
|
|
560
571
|
for (const { scopeId, isPrimary } of resolvedScopeIds) {
|
|
561
572
|
adapter
|
|
562
573
|
.prepare(`
|
|
@@ -609,22 +620,41 @@ async function saveMemoryInternal(input, options) {
|
|
|
609
620
|
rollbackError.memoryId = id;
|
|
610
621
|
throw rollbackError;
|
|
611
622
|
}
|
|
612
|
-
//
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
623
|
+
// Sync the adapter's status cache for every row whose status changed in the
|
|
624
|
+
// post-insert transaction: the saved row itself (status is written AFTER
|
|
625
|
+
// insertEmbedding populated the cache) and any rows just superseded. This makes
|
|
626
|
+
// the vectorSearch pre-filter correct within this session, not only after reload.
|
|
627
|
+
if (adapter.refreshDecisionStatusCache) {
|
|
628
|
+
const changedIds = [
|
|
629
|
+
id,
|
|
630
|
+
...evolution.edges.filter((e) => e.type === 'supersedes').map((e) => e.to_id),
|
|
631
|
+
];
|
|
632
|
+
const ridStmt = adapter.prepare('SELECT rowid FROM decisions WHERE id = ?');
|
|
633
|
+
for (const changedId of changedIds) {
|
|
634
|
+
const ridRow = ridStmt.get(changedId);
|
|
635
|
+
if (ridRow) {
|
|
636
|
+
adapter.refreshDecisionStatusCache(ridRow.rowid);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
625
639
|
}
|
|
626
|
-
|
|
627
|
-
//
|
|
640
|
+
if (options?.projectTruth !== false) {
|
|
641
|
+
// Project to memory_truth table (best-effort; failure should not break save)
|
|
642
|
+
try {
|
|
643
|
+
await (0, truth_store_js_1.projectMemoryTruth)({
|
|
644
|
+
memory_id: id,
|
|
645
|
+
topic: input.topic,
|
|
646
|
+
truth_status: targetStatus,
|
|
647
|
+
effective_summary: input.summary,
|
|
648
|
+
effective_details: input.details,
|
|
649
|
+
trust_score: input.confidence ?? 0.5,
|
|
650
|
+
scope_refs: input.scopes,
|
|
651
|
+
supporting_event_ids: [],
|
|
652
|
+
superseded_by: undefined,
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
catch {
|
|
656
|
+
// Truth projection is best-effort; do not fail the save
|
|
657
|
+
}
|
|
628
658
|
}
|
|
629
659
|
return {
|
|
630
660
|
success: true,
|
|
@@ -640,6 +670,156 @@ async function saveMemory(input) {
|
|
|
640
670
|
async function saveMemoryWithTrustedProvenance(input, options) {
|
|
641
671
|
return saveMemoryInternal((0, provenance_js_1.sanitizePublicSaveMemoryInput)(input), options);
|
|
642
672
|
}
|
|
673
|
+
async function promoteMemoryStatus(input) {
|
|
674
|
+
await (0, db_manager_js_1.initDB)();
|
|
675
|
+
const adapter = (0, db_manager_js_1.getAdapter)();
|
|
676
|
+
const memoryId = input.memoryId;
|
|
677
|
+
const now = input.nowMs ?? Date.now();
|
|
678
|
+
const targetStatus = input.status;
|
|
679
|
+
const row = adapter
|
|
680
|
+
.prepare(`
|
|
681
|
+
SELECT id, topic, decision, reasoning, confidence, kind, summary, supersedes
|
|
682
|
+
FROM decisions
|
|
683
|
+
WHERE id = ?
|
|
684
|
+
`)
|
|
685
|
+
.get(memoryId);
|
|
686
|
+
if (!row) {
|
|
687
|
+
throw new Error(`Cannot promote missing memory ${memoryId}`);
|
|
688
|
+
}
|
|
689
|
+
const topic = String(row.topic);
|
|
690
|
+
const summary = String(row.summary ?? row.decision ?? '');
|
|
691
|
+
const details = String(row.reasoning ?? row.decision ?? '');
|
|
692
|
+
const kind = (row.kind ?? 'fact') || 'fact';
|
|
693
|
+
const scopes = batchLoadScopes(adapter, [memoryId]).get(memoryId) ?? [];
|
|
694
|
+
let evolution = { edges: [] };
|
|
695
|
+
if (targetStatus === 'active') {
|
|
696
|
+
const primaryScope = scopes[0] ?? null;
|
|
697
|
+
let existingCandidates;
|
|
698
|
+
if (primaryScope) {
|
|
699
|
+
const scopeId = await (0, db_manager_js_1.ensureMemoryScope)(primaryScope.kind, primaryScope.id);
|
|
700
|
+
existingCandidates = adapter
|
|
701
|
+
.prepare(`
|
|
702
|
+
SELECT d.id, d.topic, d.summary, d.kind
|
|
703
|
+
FROM decisions d
|
|
704
|
+
JOIN memory_scope_bindings msb ON msb.memory_id = d.id
|
|
705
|
+
WHERE d.topic = ? AND msb.scope_id = ? AND d.id <> ?
|
|
706
|
+
AND (d.status = 'active' OR d.status IS NULL)
|
|
707
|
+
AND d.superseded_by IS NULL
|
|
708
|
+
ORDER BY d.created_at DESC
|
|
709
|
+
LIMIT 5
|
|
710
|
+
`)
|
|
711
|
+
.all(topic, scopeId, memoryId);
|
|
712
|
+
}
|
|
713
|
+
else {
|
|
714
|
+
existingCandidates = adapter
|
|
715
|
+
.prepare(`
|
|
716
|
+
SELECT id, topic, summary, kind
|
|
717
|
+
FROM decisions
|
|
718
|
+
WHERE topic = ? AND id <> ?
|
|
719
|
+
AND (status = 'active' OR status IS NULL)
|
|
720
|
+
AND superseded_by IS NULL
|
|
721
|
+
ORDER BY created_at DESC
|
|
722
|
+
LIMIT 5
|
|
723
|
+
`)
|
|
724
|
+
.all(topic, memoryId);
|
|
725
|
+
}
|
|
726
|
+
if (existingCandidates.length === 0) {
|
|
727
|
+
try {
|
|
728
|
+
const queryText = `${topic} ${summary}`;
|
|
729
|
+
const embedding = await (0, embeddings_js_1.generateEmbedding)(queryText, 'query');
|
|
730
|
+
// Same exclusion as saveMemoryInternal's fallback: superseded history must
|
|
731
|
+
// not crowd out the prior ACTIVE decision from the 3 candidate slots.
|
|
732
|
+
const semanticResults = await (0, db_manager_js_1.vectorSearch)(embedding, 3, 0.82, undefined, Array.from(EXCLUDED_STATUSES));
|
|
733
|
+
let scopeFiltered = semanticResults;
|
|
734
|
+
if (primaryScope) {
|
|
735
|
+
const semIds = semanticResults.map((result) => String(result.id));
|
|
736
|
+
const semScopeMap = batchLoadScopes(adapter, semIds);
|
|
737
|
+
const scopeKey = `${primaryScope.kind}:${primaryScope.id}`;
|
|
738
|
+
scopeFiltered = semanticResults.filter((result) => {
|
|
739
|
+
const resultScopes = semScopeMap.get(String(result.id)) ?? [];
|
|
740
|
+
return (resultScopes.length === 0 ||
|
|
741
|
+
resultScopes.some((scope) => `${scope.kind}:${scope.id}` === scopeKey));
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
existingCandidates = scopeFiltered
|
|
745
|
+
.filter((result) => String(result.id) !== memoryId)
|
|
746
|
+
.filter((result) => {
|
|
747
|
+
const status = String(result.status || '');
|
|
748
|
+
return !status || status === 'active' || status === '';
|
|
749
|
+
})
|
|
750
|
+
.map((result) => ({
|
|
751
|
+
id: String(result.id),
|
|
752
|
+
topic: String(result.topic || ''),
|
|
753
|
+
summary: String(result.decision || ''),
|
|
754
|
+
kind: 'fact',
|
|
755
|
+
_semanticMatch: true,
|
|
756
|
+
}));
|
|
757
|
+
}
|
|
758
|
+
catch {
|
|
759
|
+
// Semantic search unavailable — proceed with exact-match candidates only.
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
evolution = (0, evolution_engine_js_1.resolveMemoryEvolution)({
|
|
763
|
+
incoming: { topic, summary, kind },
|
|
764
|
+
existing: existingCandidates.map((candidate) => ({
|
|
765
|
+
...candidate,
|
|
766
|
+
kind: (candidate.kind || 'fact'),
|
|
767
|
+
})),
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
const existingSupersedesTarget = typeof row.supersedes === 'string' && row.supersedes.length > 0 ? row.supersedes : null;
|
|
771
|
+
const supersedesTarget = evolution.edges.find((edge) => edge.type === 'supersedes')?.to_id ??
|
|
772
|
+
(targetStatus === 'active' ? existingSupersedesTarget : null);
|
|
773
|
+
adapter.transaction(() => {
|
|
774
|
+
const updated = adapter
|
|
775
|
+
.prepare('UPDATE decisions SET status = ?, supersedes = ?, updated_at = ? WHERE id = ?')
|
|
776
|
+
.run(targetStatus, supersedesTarget, now, memoryId);
|
|
777
|
+
if (updated.changes !== 1) {
|
|
778
|
+
throw new Error(`Cannot promote missing memory ${memoryId}`);
|
|
779
|
+
}
|
|
780
|
+
adapter
|
|
781
|
+
.prepare(`
|
|
782
|
+
INSERT OR REPLACE INTO memory_truth (
|
|
783
|
+
memory_id, topic, truth_status, effective_summary, effective_details, trust_score,
|
|
784
|
+
scope_refs, supporting_event_ids, superseded_by, contradicted_by, created_at, updated_at
|
|
785
|
+
)
|
|
786
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, COALESCE((SELECT supporting_event_ids FROM memory_truth WHERE memory_id = ?), '[]'), NULL, NULL, COALESCE((SELECT created_at FROM memory_truth WHERE memory_id = ?), ?), ?)
|
|
787
|
+
`)
|
|
788
|
+
.run(memoryId, topic, targetStatus, summary, details, row.confidence ?? 0.5, JSON.stringify(scopes), memoryId, memoryId, now, now);
|
|
789
|
+
for (const edge of evolution.edges) {
|
|
790
|
+
if (edge.type === 'supersedes') {
|
|
791
|
+
adapter
|
|
792
|
+
.prepare(`UPDATE decisions SET superseded_by = ?, status = 'superseded', updated_at = ? WHERE id = ?`)
|
|
793
|
+
.run(memoryId, now, edge.to_id);
|
|
794
|
+
adapter
|
|
795
|
+
.prepare(`UPDATE memory_truth SET truth_status = 'superseded', superseded_by = ?, updated_at = ? WHERE memory_id = ?`)
|
|
796
|
+
.run(memoryId, now, edge.to_id);
|
|
797
|
+
}
|
|
798
|
+
adapter
|
|
799
|
+
.prepare(`
|
|
800
|
+
INSERT OR REPLACE INTO decision_edges (from_id, to_id, relationship, reason, weight, created_at)
|
|
801
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
802
|
+
`)
|
|
803
|
+
.run(memoryId, edge.to_id, edge.type, edge.reason ?? null, 1.0, now);
|
|
804
|
+
}
|
|
805
|
+
});
|
|
806
|
+
// Sync the adapter's status cache for every row whose status changed above.
|
|
807
|
+
// Without this, a row promoted OUT of an excluded status (e.g. stale->active)
|
|
808
|
+
// stays invisible to the vectorSearch pre-filter until the next full reload.
|
|
809
|
+
if (adapter.refreshDecisionStatusCache) {
|
|
810
|
+
const changedIds = [
|
|
811
|
+
memoryId,
|
|
812
|
+
...evolution.edges.filter((e) => e.type === 'supersedes').map((e) => e.to_id),
|
|
813
|
+
];
|
|
814
|
+
const ridStmt = adapter.prepare('SELECT rowid FROM decisions WHERE id = ?');
|
|
815
|
+
for (const id of changedIds) {
|
|
816
|
+
const ridRow = ridStmt.get(id);
|
|
817
|
+
if (ridRow) {
|
|
818
|
+
adapter.refreshDecisionStatusCache(ridRow.rowid);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
643
823
|
async function buildProfile(scopes) {
|
|
644
824
|
const records = await loadScopedMemories(scopes);
|
|
645
825
|
return (0, profile_builder_js_1.classifyProfileEntries)(records);
|
|
@@ -704,11 +884,14 @@ async function recallMemory(query, options = {}) {
|
|
|
704
884
|
let primaryQueryEmbedding = null;
|
|
705
885
|
try {
|
|
706
886
|
for (const sq of subQueries) {
|
|
707
|
-
const queryEmbedding = await (0, embeddings_js_1.generateEmbedding)(sq);
|
|
887
|
+
const queryEmbedding = await (0, embeddings_js_1.generateEmbedding)(sq, 'query');
|
|
708
888
|
if (sq === query && primaryQueryEmbedding === null) {
|
|
709
889
|
primaryQueryEmbedding = queryEmbedding;
|
|
710
890
|
}
|
|
711
|
-
const vectorResults = await (0, db_manager_js_1.vectorSearch)(queryEmbedding, vectorLimit, searchOptions.threshold, searchOptions.topicPrefix
|
|
891
|
+
const vectorResults = await (0, db_manager_js_1.vectorSearch)(queryEmbedding, vectorLimit, searchOptions.threshold, searchOptions.topicPrefix,
|
|
892
|
+
// Keep superseded history out of the candidate top-K at search time; the
|
|
893
|
+
// post-filter below stays the authority (and includeHistory restores it).
|
|
894
|
+
options.includeHistory ? undefined : Array.from(EXCLUDED_STATUSES));
|
|
712
895
|
let filtered = vectorResults;
|
|
713
896
|
let vectorScopeMap = new Map();
|
|
714
897
|
if (options.scopes && options.scopes.length > 0) {
|
|
@@ -1318,6 +1501,17 @@ async function recallMemory(query, options = {}) {
|
|
|
1318
1501
|
];
|
|
1319
1502
|
});
|
|
1320
1503
|
fusedHits = fusedHits.filter((hit) => hit.source_type !== 'decision' || acceptedPrimaryIds.has(hit.source_id));
|
|
1504
|
+
// Honor options.limit on the final memories (matched is RRF-rank-sorted, canonical
|
|
1505
|
+
// dual-write appends last). Without this cap the full fusion set (hundreds of records)
|
|
1506
|
+
// flowed into bundle.memories AND the per-record enrichment SQL loops below.
|
|
1507
|
+
if (matched.length > requestedLimit) {
|
|
1508
|
+
matched = matched.slice(0, requestedLimit);
|
|
1509
|
+
// Keep bundle.fused_hits consistent with the capped memories: drop decision hits
|
|
1510
|
+
// whose record no longer appears in `matched` (wiki hits are added below and are
|
|
1511
|
+
// not keyed to memories).
|
|
1512
|
+
const cappedIds = new Set(matched.map((record) => record.id));
|
|
1513
|
+
fusedHits = fusedHits.filter((hit) => hit.source_type !== 'decision' || cappedIds.has(hit.source_id));
|
|
1514
|
+
}
|
|
1321
1515
|
const acceptedWikiFusedHits = Array.from(wikiScores.values())
|
|
1322
1516
|
.sort((a, b) => b.score - a.score)
|
|
1323
1517
|
.flatMap((entry) => {
|
|
@@ -5,6 +5,7 @@ export interface TrustedProvenanceCapability {
|
|
|
5
5
|
export interface TrustedMemoryWriteOptions {
|
|
6
6
|
provenance: MemoryWriteProvenance;
|
|
7
7
|
capability: TrustedProvenanceCapability;
|
|
8
|
+
projectTruth?: boolean;
|
|
8
9
|
}
|
|
9
10
|
export interface NormalizedMemoryProvenance {
|
|
10
11
|
actor: MemoryEventRecord['actor'];
|
|
@@ -92,6 +92,7 @@ function appendProvenanceSourceRefs(options, refs) {
|
|
|
92
92
|
assertTrustedProvenanceCapability(options.capability);
|
|
93
93
|
return {
|
|
94
94
|
capability: options.capability,
|
|
95
|
+
projectTruth: options.projectTruth,
|
|
95
96
|
provenance: {
|
|
96
97
|
...options.provenance,
|
|
97
98
|
source_refs: [...(options.provenance.source_refs ?? []), ...refs],
|