@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.
Files changed (35) hide show
  1. package/db/migrations/037-create-context-packets.sql +32 -0
  2. package/dist/cases/wiki-page-index.js +6 -2
  3. package/dist/context-compile/boundary-defaults.d.ts +12 -0
  4. package/dist/context-compile/boundary-defaults.js +105 -0
  5. package/dist/context-compile/compiler-policy.d.ts +39 -0
  6. package/dist/context-compile/compiler-policy.js +147 -0
  7. package/dist/context-compile/compiler.d.ts +18 -0
  8. package/dist/context-compile/compiler.js +401 -0
  9. package/dist/context-compile/index.d.ts +9 -0
  10. package/dist/context-compile/index.js +25 -0
  11. package/dist/context-compile/packet-store.d.ts +9 -0
  12. package/dist/context-compile/packet-store.js +255 -0
  13. package/dist/context-compile/ref.d.ts +7 -0
  14. package/dist/context-compile/ref.js +102 -0
  15. package/dist/context-compile/source-readers.d.ts +69 -0
  16. package/dist/context-compile/source-readers.js +707 -0
  17. package/dist/context-compile/types.d.ts +138 -0
  18. package/dist/context-compile/types.js +11 -0
  19. package/dist/context-compile/visibility.d.ts +21 -0
  20. package/dist/context-compile/visibility.js +200 -0
  21. package/dist/db-adapter/node-sqlite-adapter.d.ts +3 -0
  22. package/dist/db-adapter/node-sqlite-adapter.js +76 -0
  23. package/dist/edges/ref-validation.js +55 -16
  24. package/dist/edges/types.d.ts +2 -0
  25. package/dist/embeddings.d.ts +3 -0
  26. package/dist/embeddings.js +37 -10
  27. package/dist/entities/store.d.ts +4 -1
  28. package/dist/entities/store.js +206 -78
  29. package/dist/entities/types.d.ts +5 -1
  30. package/dist/entities/types.js +1 -1
  31. package/dist/index.d.ts +1 -0
  32. package/dist/index.js +1 -0
  33. package/dist/memory/provenance.js +1 -0
  34. package/dist/memory/types.d.ts +1 -0
  35. 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