@jungjaehoon/mama-core 1.6.0 → 1.7.0
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/037-create-context-packets.sql +32 -0
- package/dist/cases/wiki-page-index.js +6 -2
- package/dist/context-compile/boundary-defaults.d.ts +12 -0
- package/dist/context-compile/boundary-defaults.js +105 -0
- package/dist/context-compile/compiler-policy.d.ts +39 -0
- package/dist/context-compile/compiler-policy.js +147 -0
- package/dist/context-compile/compiler.d.ts +18 -0
- package/dist/context-compile/compiler.js +401 -0
- package/dist/context-compile/index.d.ts +9 -0
- package/dist/context-compile/index.js +25 -0
- package/dist/context-compile/packet-store.d.ts +9 -0
- package/dist/context-compile/packet-store.js +255 -0
- package/dist/context-compile/ref.d.ts +7 -0
- package/dist/context-compile/ref.js +102 -0
- package/dist/context-compile/source-readers.d.ts +69 -0
- package/dist/context-compile/source-readers.js +707 -0
- package/dist/context-compile/types.d.ts +138 -0
- package/dist/context-compile/types.js +11 -0
- package/dist/context-compile/visibility.d.ts +21 -0
- package/dist/context-compile/visibility.js +200 -0
- package/dist/db-adapter/node-sqlite-adapter.d.ts +3 -0
- package/dist/db-adapter/node-sqlite-adapter.js +76 -0
- package/dist/edges/ref-validation.js +55 -16
- package/dist/edges/types.d.ts +2 -0
- package/dist/embeddings.d.ts +3 -0
- package/dist/embeddings.js +37 -10
- package/dist/entities/store.d.ts +4 -1
- package/dist/entities/store.js +206 -78
- package/dist/entities/types.d.ts +5 -1
- package/dist/entities/types.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/memory/provenance.js +1 -0
- package/dist/memory/types.d.ts +1 -0
- package/package.json +3 -2
|
@@ -0,0 +1,707 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sourceRefsFromCandidates = sourceRefsFromCandidates;
|
|
4
|
+
exports.readMemoryCandidates = readMemoryCandidates;
|
|
5
|
+
exports.readRawCandidates = readRawCandidates;
|
|
6
|
+
exports.readGraphCandidates = readGraphCandidates;
|
|
7
|
+
const ref_validation_js_1 = require("../edges/ref-validation.js");
|
|
8
|
+
const api_js_1 = require("../memory/api.js");
|
|
9
|
+
const ref_js_1 = require("./ref.js");
|
|
10
|
+
const visibility_js_1 = require("./visibility.js");
|
|
11
|
+
const boundary_defaults_js_1 = require("./boundary-defaults.js");
|
|
12
|
+
function hiddenAggregate() {
|
|
13
|
+
return { total: 0, by_kind: {}, by_reason: {} };
|
|
14
|
+
}
|
|
15
|
+
function incrementHidden(aggregate, ref, reason) {
|
|
16
|
+
aggregate.total += 1;
|
|
17
|
+
aggregate.by_kind[ref.kind] = (aggregate.by_kind[ref.kind] ?? 0) + 1;
|
|
18
|
+
aggregate.by_reason[reason] = (aggregate.by_reason[reason] ?? 0) + 1;
|
|
19
|
+
}
|
|
20
|
+
function tableExists(adapter, table) {
|
|
21
|
+
const row = adapter
|
|
22
|
+
.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name = ?")
|
|
23
|
+
.get(table);
|
|
24
|
+
return Boolean(row?.name);
|
|
25
|
+
}
|
|
26
|
+
function placeholders(values) {
|
|
27
|
+
return values.map(() => '?').join(', ');
|
|
28
|
+
}
|
|
29
|
+
function normalizeLimit(limit) {
|
|
30
|
+
return Math.max(0, Math.min(100, Math.floor(limit ?? 10)));
|
|
31
|
+
}
|
|
32
|
+
function parseTimestampMs(value) {
|
|
33
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
34
|
+
return Math.floor(value);
|
|
35
|
+
}
|
|
36
|
+
if (typeof value !== 'string' || value.trim().length === 0) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const numeric = Number(value);
|
|
40
|
+
if (Number.isFinite(numeric)) {
|
|
41
|
+
return Math.floor(numeric);
|
|
42
|
+
}
|
|
43
|
+
const parsed = Date.parse(value);
|
|
44
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
45
|
+
}
|
|
46
|
+
function asOfMs(input) {
|
|
47
|
+
if (input.as_of === null || input.as_of === undefined) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
const parsed = parseTimestampMs(input.as_of);
|
|
51
|
+
if (parsed === null) {
|
|
52
|
+
throw new Error(`Invalid context source as_of: ${String(input.as_of)}`);
|
|
53
|
+
}
|
|
54
|
+
return parsed;
|
|
55
|
+
}
|
|
56
|
+
function timeFilterRequired(input) {
|
|
57
|
+
return Boolean(input.range?.start_ms !== undefined ||
|
|
58
|
+
input.range?.end_ms !== undefined ||
|
|
59
|
+
(input.as_of !== undefined && input.as_of !== null));
|
|
60
|
+
}
|
|
61
|
+
function rangeBoundaryMs(value, field) {
|
|
62
|
+
if (value === undefined) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
66
|
+
return Math.floor(value);
|
|
67
|
+
}
|
|
68
|
+
throw new Error(`Invalid context source ${field}: ${String(value)}`);
|
|
69
|
+
}
|
|
70
|
+
function normalizeContextSourceRange(range) {
|
|
71
|
+
if (!range) {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
const startMs = rangeBoundaryMs(range.start_ms, 'range.start_ms');
|
|
75
|
+
const endMs = rangeBoundaryMs(range.end_ms, 'range.end_ms');
|
|
76
|
+
if (startMs !== null && endMs !== null && startMs > endMs) {
|
|
77
|
+
throw new RangeError(`Invalid context source range: start_ms ${startMs} > end_ms ${endMs}`);
|
|
78
|
+
}
|
|
79
|
+
if (startMs === null && endMs === null) {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
...(startMs !== null ? { start_ms: startMs } : {}),
|
|
84
|
+
...(endMs !== null ? { end_ms: endMs } : {}),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function normalizeContextSourceAsOf(asOf) {
|
|
88
|
+
if (asOf === null || asOf === undefined) {
|
|
89
|
+
return asOf;
|
|
90
|
+
}
|
|
91
|
+
const parsed = parseTimestampMs(asOf);
|
|
92
|
+
if (parsed === null) {
|
|
93
|
+
throw new Error(`Invalid context source as_of: ${String(asOf)}`);
|
|
94
|
+
}
|
|
95
|
+
return parsed;
|
|
96
|
+
}
|
|
97
|
+
function normalizeTimeFilters(input) {
|
|
98
|
+
return {
|
|
99
|
+
...input,
|
|
100
|
+
range: normalizeContextSourceRange(input.range),
|
|
101
|
+
as_of: normalizeContextSourceAsOf(input.as_of),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function maxVisibleTimeMs(input) {
|
|
105
|
+
const ends = [rangeBoundaryMs(input.range?.end_ms, 'range.end_ms'), asOfMs(input)].filter((value) => typeof value === 'number' && Number.isFinite(value));
|
|
106
|
+
return ends.length > 0 ? Math.min(...ends) : null;
|
|
107
|
+
}
|
|
108
|
+
function minVisibleTimeMs(input) {
|
|
109
|
+
return rangeBoundaryMs(input.range?.start_ms, 'range.start_ms');
|
|
110
|
+
}
|
|
111
|
+
function isWithinTimeBoundary(timestampMs, input) {
|
|
112
|
+
const min = minVisibleTimeMs(input);
|
|
113
|
+
const max = maxVisibleTimeMs(input);
|
|
114
|
+
return (min === null || timestampMs >= min) && (max === null || timestampMs <= max);
|
|
115
|
+
}
|
|
116
|
+
function hasExplicitEmptyProjectWindow(input) {
|
|
117
|
+
return (Array.isArray(input.project_refs) &&
|
|
118
|
+
input.project_refs.length === 0 &&
|
|
119
|
+
(input.boundary?.project_refs?.length ?? 0) > 0);
|
|
120
|
+
}
|
|
121
|
+
function applyBoundaryDefaults(input) {
|
|
122
|
+
return (0, boundary_defaults_js_1.applyContextBoundaryReadDefaults)(input, input.boundary);
|
|
123
|
+
}
|
|
124
|
+
function memoryTimestampMs(memory) {
|
|
125
|
+
return (parseTimestampMs(memory.event_datetime) ??
|
|
126
|
+
parseTimestampMs(memory.updated_at) ??
|
|
127
|
+
parseTimestampMs(memory.created_at));
|
|
128
|
+
}
|
|
129
|
+
function diagnosticsFromUnknown(value) {
|
|
130
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
const record = value;
|
|
134
|
+
if (typeof record.retrieval_source !== 'string') {
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
retrieval_source: record.retrieval_source,
|
|
139
|
+
vector_similarity: typeof record.vector_similarity === 'number' ? record.vector_similarity : null,
|
|
140
|
+
lexical_support: record.lexical_support === true,
|
|
141
|
+
entity_support: record.entity_support === true,
|
|
142
|
+
scope_support: record.scope_support === true,
|
|
143
|
+
graph_source: record.graph_source === 'primary' || record.graph_source === 'expanded'
|
|
144
|
+
? record.graph_source
|
|
145
|
+
: null,
|
|
146
|
+
is_vector_only: record.is_vector_only === true,
|
|
147
|
+
confirmation_signals: Array.isArray(record.confirmation_signals)
|
|
148
|
+
? record.confirmation_signals.filter((signal) => typeof signal === 'string')
|
|
149
|
+
: [],
|
|
150
|
+
metadata_signals: Array.isArray(record.metadata_signals)
|
|
151
|
+
? record.metadata_signals.filter((signal) => typeof signal === 'string')
|
|
152
|
+
: [],
|
|
153
|
+
candidate_threshold_used: typeof record.candidate_threshold_used === 'number' ? record.candidate_threshold_used : 0,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function supportFromDiagnostics(diagnostics) {
|
|
157
|
+
return {
|
|
158
|
+
retrieval_source: diagnostics?.retrieval_source,
|
|
159
|
+
lexical_support: diagnostics?.lexical_support,
|
|
160
|
+
entity_support: diagnostics?.entity_support,
|
|
161
|
+
scope_support: diagnostics?.scope_support,
|
|
162
|
+
graph_source: diagnostics?.graph_source,
|
|
163
|
+
graph_expanded: diagnostics?.graph_source === 'expanded',
|
|
164
|
+
is_vector_only: diagnostics?.is_vector_only,
|
|
165
|
+
vector_similarity: diagnostics?.vector_similarity,
|
|
166
|
+
confirmation_signals: diagnostics?.confirmation_signals ?? [],
|
|
167
|
+
metadata_signals: diagnostics?.metadata_signals ?? [],
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function emptySupport(retrievalSource) {
|
|
171
|
+
return {
|
|
172
|
+
retrieval_source: retrievalSource,
|
|
173
|
+
confirmation_signals: [],
|
|
174
|
+
metadata_signals: [],
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
const EXCLUDED_MEMORY_STATUSES = new Set(['superseded', 'quarantined', 'contradicted', 'stale']);
|
|
178
|
+
function loadScopesForMemoryIds(adapter, memoryIds) {
|
|
179
|
+
const scopeMap = new Map();
|
|
180
|
+
if (memoryIds.length === 0 || !tableExists(adapter, 'memory_scope_bindings')) {
|
|
181
|
+
return scopeMap;
|
|
182
|
+
}
|
|
183
|
+
const rows = adapter
|
|
184
|
+
.prepare(`
|
|
185
|
+
SELECT msb.memory_id, ms.kind, ms.external_id
|
|
186
|
+
FROM memory_scope_bindings msb
|
|
187
|
+
JOIN memory_scopes ms ON ms.id = msb.scope_id
|
|
188
|
+
WHERE msb.memory_id IN (${placeholders(memoryIds)})
|
|
189
|
+
ORDER BY msb.is_primary DESC
|
|
190
|
+
`)
|
|
191
|
+
.all(...memoryIds);
|
|
192
|
+
for (const row of rows) {
|
|
193
|
+
const existing = scopeMap.get(row.memory_id) ?? [];
|
|
194
|
+
existing.push({ kind: row.kind, id: row.external_id });
|
|
195
|
+
scopeMap.set(row.memory_id, existing);
|
|
196
|
+
}
|
|
197
|
+
return scopeMap;
|
|
198
|
+
}
|
|
199
|
+
function sourceFromTrustContext(value) {
|
|
200
|
+
if (typeof value === 'string') {
|
|
201
|
+
try {
|
|
202
|
+
const parsed = JSON.parse(value);
|
|
203
|
+
if (parsed.source) {
|
|
204
|
+
return parsed.source;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
// malformed trust_context falls back to local adapter source
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return { package: 'mama-core', source_type: 'context_compile_adapter' };
|
|
212
|
+
}
|
|
213
|
+
function memoryRecordFromRow(row, scopes) {
|
|
214
|
+
return {
|
|
215
|
+
id: String(row.id),
|
|
216
|
+
topic: String(row.topic ?? ''),
|
|
217
|
+
kind: (row.kind ?? 'decision'),
|
|
218
|
+
summary: String(row.summary ?? row.decision ?? ''),
|
|
219
|
+
details: String(row.reasoning ?? row.decision ?? ''),
|
|
220
|
+
confidence: typeof row.confidence === 'number' ? row.confidence : 0.5,
|
|
221
|
+
status: (typeof row.status === 'string' ? row.status : 'active'),
|
|
222
|
+
scopes,
|
|
223
|
+
source: sourceFromTrustContext(row.trust_context),
|
|
224
|
+
created_at: row.created_at ?? 0,
|
|
225
|
+
updated_at: row.updated_at ??
|
|
226
|
+
row.created_at ??
|
|
227
|
+
0,
|
|
228
|
+
event_date: typeof row.event_date === 'string' ? row.event_date : null,
|
|
229
|
+
event_datetime: typeof row.event_datetime === 'number' && Number.isFinite(row.event_datetime)
|
|
230
|
+
? row.event_datetime
|
|
231
|
+
: null,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
function memoryLexicalScore(memory, query) {
|
|
235
|
+
const tokens = (0, api_js_1.getLexicalQueryTokens)(query);
|
|
236
|
+
if (tokens.length === 0) {
|
|
237
|
+
return 1;
|
|
238
|
+
}
|
|
239
|
+
const haystack = [memory.topic, memory.summary, memory.details].join(' ').toLowerCase();
|
|
240
|
+
const phraseBoost = haystack.includes(query.toLowerCase()) ? 2 : 0;
|
|
241
|
+
return tokens.reduce((score, token) => (haystack.includes(token) ? score + 1 : score), phraseBoost);
|
|
242
|
+
}
|
|
243
|
+
function memoryLexicalWhereClause(tokens) {
|
|
244
|
+
if (tokens.length === 0) {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
const fields = [
|
|
248
|
+
"LOWER(COALESCE(d.topic, ''))",
|
|
249
|
+
"LOWER(COALESCE(d.summary, ''))",
|
|
250
|
+
"LOWER(COALESCE(d.decision, ''))",
|
|
251
|
+
"LOWER(COALESCE(d.reasoning, ''))",
|
|
252
|
+
];
|
|
253
|
+
return `(${tokens
|
|
254
|
+
.map(() => `(${fields.map((field) => `instr(${field}, ?) > 0`).join(' OR ')})`)
|
|
255
|
+
.join(' OR ')})`;
|
|
256
|
+
}
|
|
257
|
+
function pushMemoryLexicalParams(params, tokens) {
|
|
258
|
+
for (const token of tokens) {
|
|
259
|
+
const normalized = token.toLowerCase();
|
|
260
|
+
params.push(normalized, normalized, normalized, normalized);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
function memoryTimestampSql() {
|
|
264
|
+
return 'COALESCE(d.event_datetime, d.updated_at, d.created_at)';
|
|
265
|
+
}
|
|
266
|
+
const OPERATIONAL_MEMORY_LABELS = [
|
|
267
|
+
'dashboard_briefing',
|
|
268
|
+
'dashboard-briefing',
|
|
269
|
+
'wiki_compilation',
|
|
270
|
+
'wiki-compilation',
|
|
271
|
+
'system-audit',
|
|
272
|
+
'system_audit',
|
|
273
|
+
];
|
|
274
|
+
function isOperationalMemoryTopic(topic) {
|
|
275
|
+
const normalized = topic.trim().toLowerCase();
|
|
276
|
+
return (OPERATIONAL_MEMORY_LABELS.some((label) => normalized === label || normalized.startsWith(`${label}_`)) ||
|
|
277
|
+
normalized.startsWith('audit_') ||
|
|
278
|
+
normalized.startsWith('audit-'));
|
|
279
|
+
}
|
|
280
|
+
function taskRequestsOperationalMemory(task) {
|
|
281
|
+
const normalized = task.toLowerCase();
|
|
282
|
+
return (OPERATIONAL_MEMORY_LABELS.some((label) => normalized.includes(label)) ||
|
|
283
|
+
normalized.includes('audit log') ||
|
|
284
|
+
normalized.includes('audit-log') ||
|
|
285
|
+
normalized.includes('raw audit'));
|
|
286
|
+
}
|
|
287
|
+
function adapterScopedRecallMemory(adapter, input) {
|
|
288
|
+
if (!tableExists(adapter, 'decisions')) {
|
|
289
|
+
throw new Error('Context compile memory reader requires a migrated memory database with a decisions table.');
|
|
290
|
+
}
|
|
291
|
+
const scopes = input.scopes ?? [];
|
|
292
|
+
const params = [];
|
|
293
|
+
const joins = [];
|
|
294
|
+
const where = [
|
|
295
|
+
`(d.status IS NULL OR d.status NOT IN (${[...EXCLUDED_MEMORY_STATUSES].map(() => '?').join(', ')}))`,
|
|
296
|
+
];
|
|
297
|
+
const lexicalTokens = (0, api_js_1.getLexicalQueryTokens)(input.task);
|
|
298
|
+
params.push(...EXCLUDED_MEMORY_STATUSES);
|
|
299
|
+
if (scopes.length > 0) {
|
|
300
|
+
const includesLegacyGlobalSystem = scopes.some((scope) => scope.kind === 'global' && scope.id === 'system');
|
|
301
|
+
joins.push(includesLegacyGlobalSystem
|
|
302
|
+
? 'LEFT JOIN memory_scope_bindings msb ON msb.memory_id = d.id'
|
|
303
|
+
: 'JOIN memory_scope_bindings msb ON msb.memory_id = d.id');
|
|
304
|
+
joins.push(includesLegacyGlobalSystem
|
|
305
|
+
? 'LEFT JOIN memory_scopes ms ON ms.id = msb.scope_id'
|
|
306
|
+
: 'JOIN memory_scopes ms ON ms.id = msb.scope_id');
|
|
307
|
+
// Records written before the 'global'/'system' alignment shipped with id
|
|
308
|
+
// 'global'. Match both ids when the request includes the global-system
|
|
309
|
+
// sentinel so legacy records remain visible.
|
|
310
|
+
const scopeClauses = [];
|
|
311
|
+
const scopeParams = [];
|
|
312
|
+
for (const scope of scopes) {
|
|
313
|
+
if (scope.kind === 'global' && scope.id === 'system') {
|
|
314
|
+
scopeClauses.push('(ms.kind = ? AND ms.external_id IN (?, ?))');
|
|
315
|
+
scopeParams.push('global', 'system', 'global');
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
scopeClauses.push('(ms.kind = ? AND ms.external_id = ?)');
|
|
319
|
+
scopeParams.push(scope.kind, scope.id);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
const scopeMatch = scopeClauses.join(' OR ');
|
|
323
|
+
where.push(includesLegacyGlobalSystem
|
|
324
|
+
? `((${scopeMatch}) OR NOT EXISTS (SELECT 1 FROM memory_scope_bindings legacy_msb WHERE legacy_msb.memory_id = d.id))`
|
|
325
|
+
: `(${scopeMatch})`);
|
|
326
|
+
params.push(...scopeParams);
|
|
327
|
+
}
|
|
328
|
+
const lexicalWhere = memoryLexicalWhereClause(lexicalTokens);
|
|
329
|
+
if (lexicalWhere) {
|
|
330
|
+
where.push(lexicalWhere);
|
|
331
|
+
pushMemoryLexicalParams(params, lexicalTokens);
|
|
332
|
+
}
|
|
333
|
+
const min = minVisibleTimeMs(input);
|
|
334
|
+
const max = maxVisibleTimeMs(input);
|
|
335
|
+
const timestampSql = memoryTimestampSql();
|
|
336
|
+
if (min !== null) {
|
|
337
|
+
where.push(`${timestampSql} >= ?`);
|
|
338
|
+
params.push(min);
|
|
339
|
+
}
|
|
340
|
+
if (max !== null) {
|
|
341
|
+
where.push(`${timestampSql} <= ?`);
|
|
342
|
+
params.push(max);
|
|
343
|
+
}
|
|
344
|
+
const rows = adapter
|
|
345
|
+
.prepare(`
|
|
346
|
+
SELECT DISTINCT d.id, d.topic, d.decision, d.reasoning, d.confidence, d.created_at,
|
|
347
|
+
d.updated_at, d.trust_context, d.kind, d.status, d.summary, d.event_date,
|
|
348
|
+
d.event_datetime
|
|
349
|
+
FROM decisions d
|
|
350
|
+
${joins.join('\n ')}
|
|
351
|
+
WHERE ${where.join('\n AND ')}
|
|
352
|
+
ORDER BY ${timestampSql} DESC, d.created_at DESC
|
|
353
|
+
LIMIT ?
|
|
354
|
+
`)
|
|
355
|
+
.all(...params, Math.max(normalizeLimit(input.limit) * 5, normalizeLimit(input.limit)));
|
|
356
|
+
const scopeMap = loadScopesForMemoryIds(adapter, rows.map((row) => String(row.id)));
|
|
357
|
+
const memories = rows
|
|
358
|
+
.map((row) => memoryRecordFromRow(row, scopeMap.get(String(row.id)) ?? []))
|
|
359
|
+
.map((memory) => ({ memory, score: memoryLexicalScore(memory, input.task) }))
|
|
360
|
+
.filter((candidate) => candidate.score > 0)
|
|
361
|
+
.sort((left, right) => {
|
|
362
|
+
if (right.score !== left.score) {
|
|
363
|
+
return right.score - left.score;
|
|
364
|
+
}
|
|
365
|
+
return (memoryTimestampMs(right.memory) ?? 0) - (memoryTimestampMs(left.memory) ?? 0);
|
|
366
|
+
})
|
|
367
|
+
.slice(0, normalizeLimit(input.limit))
|
|
368
|
+
.map(({ memory, score }) => ({
|
|
369
|
+
...memory,
|
|
370
|
+
confidence: Math.max(memory.confidence, Math.min(0.95, 0.45 + score * 0.05)),
|
|
371
|
+
retrieval_diagnostics: {
|
|
372
|
+
retrieval_source: 'context_compile_adapter',
|
|
373
|
+
vector_similarity: null,
|
|
374
|
+
lexical_support: true,
|
|
375
|
+
entity_support: false,
|
|
376
|
+
scope_support: scopes.length > 0,
|
|
377
|
+
graph_source: null,
|
|
378
|
+
is_vector_only: false,
|
|
379
|
+
confirmation_signals: ['lexical'],
|
|
380
|
+
metadata_signals: ['adapter_scoped'],
|
|
381
|
+
candidate_threshold_used: 0,
|
|
382
|
+
},
|
|
383
|
+
}));
|
|
384
|
+
return {
|
|
385
|
+
profile: { static: [], dynamic: [], evidence: [] },
|
|
386
|
+
memories,
|
|
387
|
+
graph_context: { primary: memories, expanded: [], edges: [] },
|
|
388
|
+
search_meta: {
|
|
389
|
+
query: input.task,
|
|
390
|
+
scope_order: scopes.map((scope) => scope.kind),
|
|
391
|
+
retrieval_sources: ['context_compile_adapter'],
|
|
392
|
+
},
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
function resultFromCandidates(candidates, hidden) {
|
|
396
|
+
const visible = candidates.filter((candidate) => candidate.visible);
|
|
397
|
+
for (const candidate of candidates) {
|
|
398
|
+
if (!candidate.visible) {
|
|
399
|
+
incrementHidden(hidden, candidate.ref, candidate.hidden_reason ?? 'hidden');
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return {
|
|
403
|
+
candidates: visible,
|
|
404
|
+
hidden,
|
|
405
|
+
source_refs: sourceRefsFromCandidates(visible),
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
function sourceRefsFromCandidates(candidates) {
|
|
409
|
+
const refs = new Map();
|
|
410
|
+
for (const candidate of candidates) {
|
|
411
|
+
if (!candidate.visible) {
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
refs.set((0, ref_js_1.serializeContextRefForProvenance)(candidate.ref), candidate.ref);
|
|
415
|
+
}
|
|
416
|
+
return [...refs.values()];
|
|
417
|
+
}
|
|
418
|
+
async function readMemoryCandidates(input, deps = {}) {
|
|
419
|
+
const effectiveInput = normalizeTimeFilters(applyBoundaryDefaults(input));
|
|
420
|
+
if (effectiveInput.boundary) {
|
|
421
|
+
(0, visibility_js_1.assertContextBoundaryAllowsInput)({
|
|
422
|
+
boundary: effectiveInput.boundary,
|
|
423
|
+
requestedScopes: effectiveInput.scopes,
|
|
424
|
+
requestedConnectors: effectiveInput.connectors,
|
|
425
|
+
requestedProjectRefs: effectiveInput.project_refs,
|
|
426
|
+
requestedTenantId: effectiveInput.tenant_id,
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
if (Array.isArray(effectiveInput.scopes) && effectiveInput.scopes.length === 0) {
|
|
430
|
+
return resultFromCandidates([], hiddenAggregate());
|
|
431
|
+
}
|
|
432
|
+
const recallOptions = {
|
|
433
|
+
scopes: effectiveInput.scopes,
|
|
434
|
+
limit: normalizeLimit(effectiveInput.limit),
|
|
435
|
+
diagnostics: true,
|
|
436
|
+
strictness: effectiveInput.strictness,
|
|
437
|
+
threshold: effectiveInput.threshold,
|
|
438
|
+
...(effectiveInput.range ? { range: effectiveInput.range } : {}),
|
|
439
|
+
...(effectiveInput.as_of !== undefined ? { as_of: effectiveInput.as_of } : {}),
|
|
440
|
+
};
|
|
441
|
+
const bundle = deps.recallMemory
|
|
442
|
+
? await deps.recallMemory(effectiveInput.task, recallOptions)
|
|
443
|
+
: deps.adapter
|
|
444
|
+
? adapterScopedRecallMemory(deps.adapter, effectiveInput)
|
|
445
|
+
: await (0, api_js_1.recallMemory)(effectiveInput.task, recallOptions);
|
|
446
|
+
const candidates = [];
|
|
447
|
+
const hidden = hiddenAggregate();
|
|
448
|
+
const requiresTime = timeFilterRequired(effectiveInput);
|
|
449
|
+
const allowOperationalMemory = taskRequestsOperationalMemory(effectiveInput.task);
|
|
450
|
+
for (const memory of bundle.memories) {
|
|
451
|
+
const ref = { kind: 'memory', id: memory.id };
|
|
452
|
+
if (!allowOperationalMemory && isOperationalMemoryTopic(memory.topic)) {
|
|
453
|
+
candidates.push(hiddenCandidate(ref, 'memory', 'operational_summary'));
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
const timestampMs = memoryTimestampMs(memory);
|
|
457
|
+
if (timestampMs === null && requiresTime) {
|
|
458
|
+
candidates.push(hiddenCandidate(ref, 'memory', 'timestamp_missing'));
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
if (timestampMs !== null && !isWithinTimeBoundary(timestampMs, effectiveInput)) {
|
|
462
|
+
candidates.push(hiddenCandidate(ref, 'memory', 'time_boundary'));
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
const diagnostics = diagnosticsFromUnknown(memory.retrieval_diagnostics);
|
|
466
|
+
candidates.push({
|
|
467
|
+
ref,
|
|
468
|
+
title: memory.topic || memory.summary || memory.id,
|
|
469
|
+
excerpt: memory.summary || memory.details || '',
|
|
470
|
+
score: typeof memory.confidence === 'number' ? memory.confidence : 0,
|
|
471
|
+
timestamp_ms: timestampMs,
|
|
472
|
+
source: 'memory',
|
|
473
|
+
visible: true,
|
|
474
|
+
support: supportFromDiagnostics(diagnostics),
|
|
475
|
+
...(diagnostics ? { retrieval_diagnostics: diagnostics } : {}),
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
return resultFromCandidates(candidates, hidden);
|
|
479
|
+
}
|
|
480
|
+
function hiddenCandidate(ref, source, hiddenReason) {
|
|
481
|
+
return {
|
|
482
|
+
ref,
|
|
483
|
+
title: '',
|
|
484
|
+
excerpt: '',
|
|
485
|
+
score: 0,
|
|
486
|
+
timestamp_ms: null,
|
|
487
|
+
source,
|
|
488
|
+
visible: false,
|
|
489
|
+
hidden_reason: hiddenReason,
|
|
490
|
+
support: emptySupport(),
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
function readRawCandidates(adapter, input) {
|
|
494
|
+
const effectiveInput = normalizeTimeFilters(applyBoundaryDefaults(input));
|
|
495
|
+
if (effectiveInput.boundary) {
|
|
496
|
+
(0, visibility_js_1.assertContextBoundaryAllowsInput)({
|
|
497
|
+
boundary: effectiveInput.boundary,
|
|
498
|
+
requestedScopes: effectiveInput.scopes,
|
|
499
|
+
requestedConnectors: effectiveInput.connectors,
|
|
500
|
+
requestedProjectRefs: effectiveInput.project_refs,
|
|
501
|
+
requestedTenantId: effectiveInput.tenant_id,
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
if (!tableExists(adapter, 'connector_event_index')) {
|
|
505
|
+
return resultFromCandidates([], hiddenAggregate());
|
|
506
|
+
}
|
|
507
|
+
const connectors = effectiveInput.connectors ?? [];
|
|
508
|
+
const scopes = effectiveInput.scopes ?? [];
|
|
509
|
+
const projectIds = (effectiveInput.project_refs ?? []).map((project) => project.id);
|
|
510
|
+
const hasGlobalSystemScope = scopes.some((scope) => scope.kind === 'global' && scope.id === 'system');
|
|
511
|
+
const hasScopedVisibility = scopes.some((scope) => !(scope.kind === 'global' && scope.id === 'system')) ||
|
|
512
|
+
projectIds.length > 0 ||
|
|
513
|
+
Boolean(effectiveInput.tenant_id);
|
|
514
|
+
const includesLegacyGlobalSystem = hasGlobalSystemScope && !hasScopedVisibility;
|
|
515
|
+
if (connectors.length === 0 ||
|
|
516
|
+
scopes.length === 0 ||
|
|
517
|
+
hasExplicitEmptyProjectWindow(effectiveInput)) {
|
|
518
|
+
return resultFromCandidates([], hiddenAggregate());
|
|
519
|
+
}
|
|
520
|
+
const clauses = [`source_connector IN (${placeholders(connectors)})`];
|
|
521
|
+
const params = [...connectors];
|
|
522
|
+
if (projectIds.length > 0) {
|
|
523
|
+
const projectMatches = [`project_id IN (${placeholders(projectIds)})`];
|
|
524
|
+
if (includesLegacyGlobalSystem) {
|
|
525
|
+
projectMatches.push('project_id IS NULL');
|
|
526
|
+
}
|
|
527
|
+
clauses.push(`(${projectMatches.join(' OR ')})`);
|
|
528
|
+
params.push(...projectIds);
|
|
529
|
+
}
|
|
530
|
+
if (effectiveInput.tenant_id) {
|
|
531
|
+
clauses.push(includesLegacyGlobalSystem ? '(tenant_id = ? OR tenant_id IS NULL)' : 'tenant_id = ?');
|
|
532
|
+
params.push(effectiveInput.tenant_id);
|
|
533
|
+
}
|
|
534
|
+
// Mirror the legacy global-id handling from readMemoryCandidates: rows
|
|
535
|
+
// saved before the alignment used memory_scope_id='global'.
|
|
536
|
+
const scopeMatches = [];
|
|
537
|
+
const scopeParams = [];
|
|
538
|
+
for (const scope of scopes) {
|
|
539
|
+
if (scope.kind === 'global' && scope.id === 'system') {
|
|
540
|
+
scopeMatches.push('(memory_scope_kind = ? AND memory_scope_id IN (?, ?))');
|
|
541
|
+
scopeParams.push('global', 'system', 'global');
|
|
542
|
+
}
|
|
543
|
+
else {
|
|
544
|
+
scopeMatches.push('(memory_scope_kind = ? AND memory_scope_id = ?)');
|
|
545
|
+
scopeParams.push(scope.kind, scope.id);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
if (includesLegacyGlobalSystem) {
|
|
549
|
+
scopeMatches.push('(memory_scope_kind IS NULL AND memory_scope_id IS NULL)');
|
|
550
|
+
}
|
|
551
|
+
clauses.push(`(${scopeMatches.join(' OR ')})`);
|
|
552
|
+
params.push(...scopeParams);
|
|
553
|
+
const min = minVisibleTimeMs(effectiveInput);
|
|
554
|
+
const max = maxVisibleTimeMs(effectiveInput);
|
|
555
|
+
if (min !== null) {
|
|
556
|
+
clauses.push('COALESCE(event_datetime, source_timestamp_ms) >= ?');
|
|
557
|
+
params.push(min);
|
|
558
|
+
}
|
|
559
|
+
if (max !== null) {
|
|
560
|
+
clauses.push('COALESCE(event_datetime, source_timestamp_ms) <= ?');
|
|
561
|
+
params.push(max);
|
|
562
|
+
}
|
|
563
|
+
params.push(normalizeLimit(effectiveInput.limit));
|
|
564
|
+
const rows = adapter
|
|
565
|
+
.prepare(`
|
|
566
|
+
SELECT event_index_id, source_connector, source_id, channel, title, content,
|
|
567
|
+
event_datetime, source_timestamp_ms
|
|
568
|
+
FROM connector_event_index
|
|
569
|
+
WHERE ${clauses.join('\n AND ')}
|
|
570
|
+
ORDER BY COALESCE(event_datetime, source_timestamp_ms) DESC, event_index_id ASC
|
|
571
|
+
LIMIT ?
|
|
572
|
+
`)
|
|
573
|
+
.all(...params);
|
|
574
|
+
const candidates = rows.map((row) => {
|
|
575
|
+
const sourceId = typeof row.source_id === 'string' && row.source_id.trim().length > 0
|
|
576
|
+
? row.source_id
|
|
577
|
+
: undefined;
|
|
578
|
+
const ref = {
|
|
579
|
+
kind: 'raw',
|
|
580
|
+
connector: String(row.source_connector),
|
|
581
|
+
raw_id: String(row.event_index_id),
|
|
582
|
+
...(sourceId ? { source_id: sourceId } : {}),
|
|
583
|
+
channel_id: typeof row.channel === 'string' ? row.channel : null,
|
|
584
|
+
};
|
|
585
|
+
return {
|
|
586
|
+
ref,
|
|
587
|
+
title: String(row.title ?? 'Raw event'),
|
|
588
|
+
excerpt: String(row.content ?? '').slice(0, 500),
|
|
589
|
+
score: 0.7,
|
|
590
|
+
timestamp_ms: parseTimestampMs(row.event_datetime ?? row.source_timestamp_ms),
|
|
591
|
+
source: 'raw',
|
|
592
|
+
visible: true,
|
|
593
|
+
support: emptySupport('connector_event_index'),
|
|
594
|
+
};
|
|
595
|
+
});
|
|
596
|
+
return resultFromCandidates(candidates, hiddenAggregate());
|
|
597
|
+
}
|
|
598
|
+
function readGraphCandidates(adapter, input, visibleRefs, deps = {}) {
|
|
599
|
+
const effectiveInput = normalizeTimeFilters(applyBoundaryDefaults(input));
|
|
600
|
+
if (effectiveInput.boundary) {
|
|
601
|
+
(0, visibility_js_1.assertContextBoundaryAllowsInput)({
|
|
602
|
+
boundary: effectiveInput.boundary,
|
|
603
|
+
requestedScopes: effectiveInput.scopes,
|
|
604
|
+
requestedConnectors: effectiveInput.connectors,
|
|
605
|
+
requestedProjectRefs: effectiveInput.project_refs,
|
|
606
|
+
requestedTenantId: effectiveInput.tenant_id,
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
if (Array.isArray(effectiveInput.scopes) && effectiveInput.scopes.length === 0) {
|
|
610
|
+
return resultFromCandidates([], hiddenAggregate());
|
|
611
|
+
}
|
|
612
|
+
if (Array.isArray(effectiveInput.connectors) && effectiveInput.connectors.length === 0) {
|
|
613
|
+
// Fail closed when caller explicitly narrowed connectors to none, so an
|
|
614
|
+
// injectable listEdges cannot leak edges back into the result.
|
|
615
|
+
return resultFromCandidates([], hiddenAggregate());
|
|
616
|
+
}
|
|
617
|
+
if (hasExplicitEmptyProjectWindow(effectiveInput)) {
|
|
618
|
+
return resultFromCandidates([], hiddenAggregate());
|
|
619
|
+
}
|
|
620
|
+
if (visibleRefs.length === 0) {
|
|
621
|
+
return resultFromCandidates([], hiddenAggregate());
|
|
622
|
+
}
|
|
623
|
+
const refs = visibleRefs.map((ref) => (0, ref_js_1.toTwinRef)(ref));
|
|
624
|
+
const visibleRefKeys = new Set(visibleRefs.map(ref_js_1.serializeContextRefForProvenance));
|
|
625
|
+
const listEdges = deps.listVisibleTwinEdgesForRefs ?? ref_validation_js_1.listVisibleTwinEdgesForRefs;
|
|
626
|
+
const min = minVisibleTimeMs(effectiveInput);
|
|
627
|
+
const max = maxVisibleTimeMs(effectiveInput);
|
|
628
|
+
const edges = listEdges(adapter, refs, {
|
|
629
|
+
scopes: effectiveInput.scopes,
|
|
630
|
+
connectors: effectiveInput.connectors,
|
|
631
|
+
projectRefs: effectiveInput.project_refs,
|
|
632
|
+
tenantId: effectiveInput.tenant_id,
|
|
633
|
+
startMs: min,
|
|
634
|
+
asOfMs: max,
|
|
635
|
+
limit: normalizeLimit(effectiveInput.limit),
|
|
636
|
+
}).filter((edge) => (min === null || edge.created_at >= min) && (max === null || edge.created_at <= max));
|
|
637
|
+
const candidates = [];
|
|
638
|
+
const emitted = new Set();
|
|
639
|
+
for (const edge of edges) {
|
|
640
|
+
for (const ref of edgeNeighborRefs(adapter, edge)) {
|
|
641
|
+
const key = (0, ref_js_1.serializeContextRefForProvenance)(ref);
|
|
642
|
+
if (visibleRefKeys.has(key) || emitted.has(key)) {
|
|
643
|
+
continue;
|
|
644
|
+
}
|
|
645
|
+
emitted.add(key);
|
|
646
|
+
candidates.push({
|
|
647
|
+
ref,
|
|
648
|
+
title: edge.edge_type,
|
|
649
|
+
excerpt: edge.reason_text ?? '',
|
|
650
|
+
score: edge.confidence,
|
|
651
|
+
timestamp_ms: edge.created_at,
|
|
652
|
+
source: 'graph',
|
|
653
|
+
visible: true,
|
|
654
|
+
edge_id: edge.edge_id,
|
|
655
|
+
edge_type: edge.edge_type,
|
|
656
|
+
support: {
|
|
657
|
+
retrieval_source: 'twin_edges',
|
|
658
|
+
graph_source: 'expanded',
|
|
659
|
+
graph_expanded: true,
|
|
660
|
+
confirmation_signals: [],
|
|
661
|
+
metadata_signals: ['graph_edge'],
|
|
662
|
+
},
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
return resultFromCandidates(candidates, hiddenAggregate());
|
|
667
|
+
}
|
|
668
|
+
function edgeNeighborRefs(adapter, edge) {
|
|
669
|
+
return [edge.subject_ref, edge.object_ref].flatMap((ref) => {
|
|
670
|
+
const contextRef = contextRefFromTwinRef(adapter, ref);
|
|
671
|
+
return contextRef ? [contextRef] : [];
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
function contextRefFromTwinRef(adapter, ref) {
|
|
675
|
+
switch (ref.kind) {
|
|
676
|
+
case 'memory':
|
|
677
|
+
case 'entity':
|
|
678
|
+
case 'case':
|
|
679
|
+
return { kind: ref.kind, id: ref.id };
|
|
680
|
+
case 'raw': {
|
|
681
|
+
const row = adapter
|
|
682
|
+
.prepare(`
|
|
683
|
+
SELECT source_connector, source_id, channel
|
|
684
|
+
FROM connector_event_index
|
|
685
|
+
WHERE event_index_id = ?
|
|
686
|
+
LIMIT 1
|
|
687
|
+
`)
|
|
688
|
+
.get(ref.id);
|
|
689
|
+
if (!row || typeof row.source_connector !== 'string') {
|
|
690
|
+
return null;
|
|
691
|
+
}
|
|
692
|
+
const contextRef = {
|
|
693
|
+
kind: 'raw',
|
|
694
|
+
connector: row.source_connector,
|
|
695
|
+
raw_id: ref.id,
|
|
696
|
+
};
|
|
697
|
+
if (typeof row.source_id === 'string' && row.source_id.trim().length > 0) {
|
|
698
|
+
contextRef.source_id = row.source_id;
|
|
699
|
+
}
|
|
700
|
+
contextRef.channel_id = typeof row.channel === 'string' ? row.channel : null;
|
|
701
|
+
return contextRef;
|
|
702
|
+
}
|
|
703
|
+
default:
|
|
704
|
+
return null;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
//# sourceMappingURL=source-readers.js.map
|