@jungjaehoon/mama-core 2.3.0 → 2.4.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.
@@ -0,0 +1,14 @@
1
+ -- Migration 067: give connector_event_index a first-class source_entity_id so an agent can read an
2
+ -- entity's change history (the revisions save() preserves) through the same grant-bounded core index
3
+ -- it already searches. Previously sourceEntityId lived only inside metadata_json, with no by-entity
4
+ -- query. Additive and backward-compatible: existing rows are backfilled from metadata_json.
5
+ ALTER TABLE connector_event_index ADD COLUMN source_entity_id TEXT;
6
+
7
+ UPDATE connector_event_index
8
+ SET source_entity_id = json_extract(metadata_json, '$.sourceEntityId')
9
+ WHERE source_entity_id IS NULL
10
+ AND metadata_json IS NOT NULL
11
+ AND json_extract(metadata_json, '$.sourceEntityId') IS NOT NULL;
12
+
13
+ CREATE INDEX IF NOT EXISTS idx_connector_event_source_entity
14
+ ON connector_event_index(source_connector, source_entity_id);
@@ -0,0 +1,13 @@
1
+ -- TG-03/04/05: additive diagnostic evidence, scoped by host metadata.
2
+ -- The migration runner reconciles existing MetricsStore scope columns idempotently.
3
+ -- NULL scope preserves legacy records without granting discovery access.
4
+ ALTER TABLE tool_traces ADD COLUMN diagnostic_json TEXT;
5
+ ALTER TABLE tool_traces ADD COLUMN evidence_json TEXT;
6
+ ALTER TABLE tool_traces ADD COLUMN catalog_revision TEXT;
7
+ ALTER TABLE tool_traces ADD COLUMN owner_scope TEXT;
8
+ ALTER TABLE tool_traces ADD COLUMN project_id TEXT;
9
+ ALTER TABLE tool_traces ADD COLUMN channel_id TEXT;
10
+ CREATE INDEX IF NOT EXISTS idx_tool_traces_scope_recency
11
+ ON tool_traces(owner_scope, project_id, created_at DESC, trace_id DESC);
12
+ CREATE INDEX IF NOT EXISTS idx_tool_traces_channel_recency
13
+ ON tool_traces(owner_scope, project_id, channel_id, created_at DESC, trace_id DESC);
@@ -135,15 +135,16 @@ function upsertConnectorEventIndex(adapter, input) {
135
135
  adapter
136
136
  .prepare(`
137
137
  INSERT INTO connector_event_index (
138
- event_index_id, source_connector, source_type, source_id, source_locator,
139
- channel, author, title, content, event_datetime, event_date, source_timestamp_ms,
140
- source_cursor, tenant_id, project_id, memory_scope_kind, memory_scope_id,
141
- metadata_json, artifact_locator, artifact_title, content_hash, indexed_at, updated_at,
142
- expires_at
138
+ event_index_id, source_connector, source_type, source_id, source_entity_id,
139
+ source_locator, channel, author, title, content, event_datetime, event_date,
140
+ source_timestamp_ms, source_cursor, tenant_id, project_id, memory_scope_kind,
141
+ memory_scope_id, metadata_json, artifact_locator, artifact_title, content_hash,
142
+ indexed_at, updated_at, expires_at
143
143
  )
144
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
144
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
145
145
  ON CONFLICT(source_connector, source_id) DO UPDATE SET
146
146
  source_type = excluded.source_type,
147
+ source_entity_id = excluded.source_entity_id,
147
148
  source_locator = excluded.source_locator,
148
149
  channel = excluded.channel,
149
150
  author = excluded.author,
@@ -182,7 +183,7 @@ function upsertConnectorEventIndex(adapter, input) {
182
183
  ELSE connector_event_index.operator_observation_seq
183
184
  END
184
185
  `)
185
- .run(eventIndexId, input.source_connector, input.source_type, input.source_id, input.source_locator ?? null, input.channel ?? null, input.author ?? null, input.title ?? null, input.content, eventDatetime, eventDate, sourceTimestampMs, input.source_cursor ?? null, input.tenant_id ?? null, input.project_id ?? null, input.memory_scope_kind ?? null, input.memory_scope_id ?? null, metadataJson, input.artifact_locator ?? null, input.artifact_title ?? null, contentHash, input.indexed_at ?? timestamp, timestamp, input.expires_at ?? null);
186
+ .run(eventIndexId, input.source_connector, input.source_type, input.source_id, input.source_entity_id ?? input.source_id, input.source_locator ?? null, input.channel ?? null, input.author ?? null, input.title ?? null, input.content, eventDatetime, eventDate, sourceTimestampMs, input.source_cursor ?? null, input.tenant_id ?? null, input.project_id ?? null, input.memory_scope_kind ?? null, input.memory_scope_id ?? null, metadataJson, input.artifact_locator ?? null, input.artifact_title ?? null, contentHash, input.indexed_at ?? timestamp, timestamp, input.expires_at ?? null);
186
187
  const saved = getConnectorEventIndexRecord(adapter, input.source_connector, input.source_id);
187
188
  if (!saved) {
188
189
  throw new Error(`Failed to read connector_event_index row after upsert: ${input.source_connector}/${input.source_id}`);
@@ -1,5 +1,5 @@
1
1
  import type { DatabaseAdapter } from '../db-manager.js';
2
- import type { RawSearchHit, RawSearchInput, RawSearchResult, RawSearchScopeFilter } from './types.js';
2
+ import type { RawSearchHit, RawDocument, RawSearchInput, RawSearchResult, RawSearchScopeFilter } from './types.js';
3
3
  type RawQueryAdapter = Pick<DatabaseAdapter, 'prepare'>;
4
4
  interface RawWindowInput {
5
5
  connectors?: string[];
@@ -9,10 +9,32 @@ interface RawWindowInput {
9
9
  }
10
10
  export declare function searchRaw(adapter: RawQueryAdapter, input: RawSearchInput): RawSearchResult;
11
11
  export declare function searchAllRaw(adapter: RawQueryAdapter, input: RawSearchInput): RawSearchResult;
12
- export declare function getRawById(adapter: RawQueryAdapter, rawId: string, visibility: Pick<RawSearchInput, 'connectors' | 'scopes'>): RawSearchHit | null;
12
+ export declare function getRawById(adapter: RawQueryAdapter, rawId: string, visibility: Pick<RawSearchInput, 'connectors' | 'scopes'>): RawDocument | null;
13
13
  export declare function getRawWindow(adapter: RawQueryAdapter, rawId: string, input: RawWindowInput): {
14
14
  target: RawSearchHit;
15
15
  items: RawSearchHit[];
16
16
  } | null;
17
+ export interface RawHistoryInput {
18
+ /** The entity whose revisions to read. Provide this OR `rawId`. */
19
+ entityId?: string;
20
+ /**
21
+ * A raw event id (event_index_id) to anchor on: its entity is resolved under the SAME
22
+ * visibility, then that entity's revisions are returned. An anchor the caller may not see
23
+ * resolves to nothing, so a rawId cannot widen what the caller can read.
24
+ */
25
+ rawId?: string;
26
+ connectors?: string[];
27
+ scopes?: RawSearchScopeFilter[];
28
+ fromMs?: number;
29
+ toMs?: number;
30
+ limit?: number;
31
+ cursor?: string;
32
+ }
33
+ /**
34
+ * Chronological change history of one upstream entity (rows sharing source_entity_id), bounded by
35
+ * the SAME connector/scope visibility as search - a citation must not out-read reading. Oldest first,
36
+ * cursor-paged. Rows with a NULL source_entity_id (no revision grouping) never match an entity query.
37
+ */
38
+ export declare function getRawHistory(adapter: RawQueryAdapter, input: RawHistoryInput): RawSearchResult;
17
39
  export type { RawSearchHit, RawSearchInput, RawSearchResult, RawSearchScopeFilter, } from './types.js';
18
40
  //# sourceMappingURL=raw-query.d.ts.map
@@ -4,6 +4,7 @@ exports.searchRaw = searchRaw;
4
4
  exports.searchAllRaw = searchAllRaw;
5
5
  exports.getRawById = getRawById;
6
6
  exports.getRawWindow = getRawWindow;
7
+ exports.getRawHistory = getRawHistory;
7
8
  const types_js_1 = require("../memory/types.js");
8
9
  const event_index_js_1 = require("./event-index.js");
9
10
  const DEFAULT_RAW_LIMIT = 25;
@@ -220,7 +221,7 @@ function getRawById(adapter, rawId, visibility) {
220
221
  LIMIT 1
221
222
  `)
222
223
  .get(...params);
223
- return row ? toRawHit(row) : null;
224
+ return row ? { ...toRawHit(row), content: String(row.content) } : null;
224
225
  }
225
226
  function getRawWindow(adapter, rawId, input) {
226
227
  const targetParams = [rawId];
@@ -302,4 +303,110 @@ function afterWindowRows(adapter, target, input, limit) {
302
303
  `)
303
304
  .all(...params, limit);
304
305
  }
306
+ // The entity a raw event belongs to, plus its connector. source_entity_id is unique only WITHIN a
307
+ // connector, so history must carry the connector too or two connectors sharing an id would merge.
308
+ function resolveEntityAnchor(adapter, rawId, visibility) {
309
+ const clauses = ['e.event_index_id = ?'];
310
+ const params = [rawId];
311
+ appendFilters(clauses, params, { query: '*', ...visibility });
312
+ const row = adapter
313
+ .prepare(`SELECT source_entity_id, source_connector FROM connector_event_index e WHERE ${clauses.join(' AND ')} LIMIT 1`)
314
+ .get(...params);
315
+ return row &&
316
+ typeof row.source_entity_id === 'string' &&
317
+ row.source_entity_id.length > 0 &&
318
+ typeof row.source_connector === 'string' &&
319
+ row.source_connector.length > 0
320
+ ? { entityId: row.source_entity_id, connector: row.source_connector }
321
+ : null;
322
+ }
323
+ function singleConnector(connectors) {
324
+ const normalized = normalizeConnectors(connectors);
325
+ return normalized.length === 1 ? normalized[0] : null;
326
+ }
327
+ function encodeHistoryCursor(row) {
328
+ return Buffer.from(JSON.stringify({ timestampMs: row.source_timestamp_ms, rawId: row.event_index_id }), 'utf8').toString('base64url');
329
+ }
330
+ function decodeHistoryCursor(cursor) {
331
+ if (!cursor) {
332
+ return null;
333
+ }
334
+ try {
335
+ const parsed = JSON.parse(Buffer.from(cursor, 'base64url').toString('utf8'));
336
+ if (typeof parsed.timestampMs !== 'number' ||
337
+ !Number.isFinite(parsed.timestampMs) ||
338
+ typeof parsed.rawId !== 'string' ||
339
+ parsed.rawId.length === 0) {
340
+ throw new Error('invalid shape');
341
+ }
342
+ return { timestampMs: parsed.timestampMs, rawId: parsed.rawId };
343
+ }
344
+ catch (err) {
345
+ const message = err instanceof Error ? err.message : String(err);
346
+ throw new Error(`Invalid raw history cursor: ${message}`);
347
+ }
348
+ }
349
+ /**
350
+ * Chronological change history of one upstream entity (rows sharing source_entity_id), bounded by
351
+ * the SAME connector/scope visibility as search - a citation must not out-read reading. Oldest first,
352
+ * cursor-paged. Rows with a NULL source_entity_id (no revision grouping) never match an entity query.
353
+ */
354
+ function getRawHistory(adapter, input) {
355
+ // Revision identity is connector-scoped. The rawId path takes the anchor's own connector; the
356
+ // entityId path requires the caller to name exactly one connector, since an entity id alone is
357
+ // ambiguous across connectors.
358
+ const trimmedEntityId = input.entityId?.trim() ?? '';
359
+ const anchor = trimmedEntityId.length > 0
360
+ ? (() => {
361
+ const connector = singleConnector(input.connectors);
362
+ return connector ? { entityId: trimmedEntityId, connector } : null;
363
+ })()
364
+ : input.rawId && input.rawId.trim().length > 0
365
+ ? resolveEntityAnchor(adapter, input.rawId.trim(), {
366
+ connectors: input.connectors,
367
+ scopes: input.scopes,
368
+ })
369
+ : null;
370
+ if (!anchor) {
371
+ return { hits: [], next_cursor: null };
372
+ }
373
+ const limit = normalizeLimit(input.limit);
374
+ if (limit === 0) {
375
+ return { hits: [], next_cursor: null };
376
+ }
377
+ const clauses = ['e.source_entity_id = ?'];
378
+ const params = [anchor.entityId];
379
+ appendFilters(clauses, params, {
380
+ query: '*',
381
+ connectors: [anchor.connector],
382
+ scopes: input.scopes,
383
+ fromMs: input.fromMs,
384
+ toMs: input.toMs,
385
+ });
386
+ const cursor = decodeHistoryCursor(input.cursor);
387
+ if (cursor) {
388
+ clauses.push('(e.source_timestamp_ms > ? OR (e.source_timestamp_ms = ? AND e.event_index_id > ?))');
389
+ params.push(cursor.timestampMs, cursor.timestampMs, cursor.rawId);
390
+ }
391
+ const rows = adapter
392
+ .prepare(`
393
+ SELECT e.*, 0 AS rank
394
+ FROM connector_event_index e
395
+ WHERE ${clauses.join(' AND ')}
396
+ ORDER BY e.source_timestamp_ms ASC, e.event_index_id ASC
397
+ LIMIT ?
398
+ `)
399
+ .all(...params, limit + 1);
400
+ const pageRows = rows.slice(0, limit);
401
+ const nextRow = rows.length > limit ? pageRows[pageRows.length - 1] : undefined;
402
+ return {
403
+ hits: pageRows.map(toRawHit),
404
+ next_cursor: nextRow
405
+ ? encodeHistoryCursor({
406
+ source_timestamp_ms: Number(nextRow.source_timestamp_ms),
407
+ event_index_id: String(nextRow.event_index_id),
408
+ })
409
+ : null,
410
+ };
411
+ }
305
412
  //# sourceMappingURL=raw-query.js.map
@@ -32,6 +32,8 @@ export interface UpsertConnectorEventIndexInput {
32
32
  source_connector: string;
33
33
  source_type: string;
34
34
  source_id: string;
35
+ /** Stable upstream entity across revisions; enables grant-bounded change-history reads. */
36
+ source_entity_id?: string | null;
35
37
  source_locator?: string | null;
36
38
  channel?: string | null;
37
39
  author?: string | null;
@@ -103,6 +105,10 @@ export interface RawSearchHit {
103
105
  source_ref: string | null;
104
106
  metadata: Record<string, unknown>;
105
107
  }
108
+ /** Explicit detail access includes the complete source; search results remain bounded previews. */
109
+ export interface RawDocument extends RawSearchHit {
110
+ content: string;
111
+ }
106
112
  export interface RawSearchResult {
107
113
  hits: RawSearchHit[];
108
114
  next_cursor: string | null;
@@ -66,10 +66,14 @@ export declare class NodeSQLiteAdapter extends DatabaseAdapter {
66
66
  private hasOperatorMemoryCommitIntentClaimInvariant;
67
67
  private assertMigration040BaseComplete;
68
68
  private assertMigration041Complete;
69
+ private needsToolTraceDiagnosticsRepair068;
70
+ private recoverToolTraceDiagnosticsMigration068;
69
71
  private recoverMemoryProvenanceMigration032;
70
72
  private assertMigration032Complete;
71
73
  private recoverConnectorEventScopeMigration034;
72
74
  private assertMigration034Complete;
75
+ private recoverConnectorEventSourceEntityIdMigration067;
76
+ private assertMigration067Complete;
73
77
  private recoverConnectorEventOperatorSeqMigration039;
74
78
  private assertMigration039Complete;
75
79
  private recoverConnectorEventSequencesMigration062;
@@ -376,6 +376,18 @@ class NodeSQLiteAdapter extends base_adapter_js_1.DatabaseAdapter {
376
376
  if (version <= currentVersion) {
377
377
  continue;
378
378
  }
379
+ // Runtime MetricsStore may already own project_id/channel_id. Reconcile
380
+ // this additive migration atomically instead of accepting a duplicate skip.
381
+ if (version === 68) {
382
+ if (!this.tableExists('tool_traces')) {
383
+ // Legacy version ledgers can have skipped 033; the structural repair
384
+ // below creates its table before reconciling 068. Do not stamp it yet.
385
+ continue;
386
+ }
387
+ this.recoverToolTraceDiagnosticsMigration068();
388
+ (0, debug_logger_js_1.info)(`[node-sqlite-adapter] Migration ${file} reconciled successfully`);
389
+ continue;
390
+ }
379
391
  const migrationPath = path_1.default.join(migrationsDir, file);
380
392
  const migrationSQL = fs_1.default.readFileSync(migrationPath, 'utf8');
381
393
  (0, debug_logger_js_1.info)(`[node-sqlite-adapter] Applying migration: ${file}`);
@@ -409,6 +421,11 @@ class NodeSQLiteAdapter extends base_adapter_js_1.DatabaseAdapter {
409
421
  (0, debug_logger_js_1.info)(`[node-sqlite-adapter] Migration ${file} recovered successfully`);
410
422
  continue;
411
423
  }
424
+ if (message.includes('duplicate column') && version === 67) {
425
+ this.recoverConnectorEventSourceEntityIdMigration067();
426
+ (0, debug_logger_js_1.info)(`[node-sqlite-adapter] Migration ${file} recovered successfully`);
427
+ continue;
428
+ }
412
429
  if (message.includes('no such column') && version === 62) {
413
430
  (0, debug_logger_js_1.warn)(`[node-sqlite-adapter] Migration ${file} deferred until connector structure recovery (${message})`);
414
431
  continue;
@@ -418,6 +435,11 @@ class NodeSQLiteAdapter extends base_adapter_js_1.DatabaseAdapter {
418
435
  (0, debug_logger_js_1.warn)(`[node-sqlite-adapter] Migration ${file} deferred until connector structure recovery (${message})`);
419
436
  continue;
420
437
  }
438
+ if (version === 67 &&
439
+ (message.includes('no such column') || message.includes('no such table'))) {
440
+ (0, debug_logger_js_1.warn)(`[node-sqlite-adapter] Migration ${file} deferred until connector structure recovery (${message})`);
441
+ continue;
442
+ }
421
443
  if (message.includes('duplicate column')) {
422
444
  (0, debug_logger_js_1.warn)(`[node-sqlite-adapter] Migration ${file} skipped (duplicate column - already applied)`);
423
445
  this.prepare('INSERT OR IGNORE INTO schema_version (version) VALUES (?)').run(version);
@@ -469,6 +491,14 @@ class NodeSQLiteAdapter extends base_adapter_js_1.DatabaseAdapter {
469
491
  if (!this.tableExists('model_runs') || !this.tableExists('tool_traces')) {
470
492
  this.applyRepairMigration(migrationsDir, '033-create-model-runs-and-tool-traces.sql', 'model run provenance');
471
493
  }
494
+ // Also repairs databases already stamped 68 by the former generic duplicate
495
+ // skip, which rolled back the new columns before advancing schema_version.
496
+ // Only enter the recovery transaction when something is actually missing —
497
+ // a complete database must not take the write path on every runMigrations().
498
+ if (fs_1.default.existsSync(path_1.default.join(migrationsDir, '068-tool-trace-diagnostics.sql')) &&
499
+ this.needsToolTraceDiagnosticsRepair068()) {
500
+ this.recoverToolTraceDiagnosticsMigration068();
501
+ }
472
502
  if (this.tableExists('connector_event_index')) {
473
503
  const connectorColumns = this.tableColumns('connector_event_index');
474
504
  const hasMissingConnectorScopeColumn = [
@@ -515,6 +545,13 @@ class NodeSQLiteAdapter extends base_adapter_js_1.DatabaseAdapter {
515
545
  (0, debug_logger_js_1.info)('[node-sqlite-adapter] Repaired skipped legacy connector event refresh migration');
516
546
  }
517
547
  }
548
+ const hasMissingSourceEntityIdFeature = !this.tableColumns('connector_event_index').has('source_entity_id') ||
549
+ !this.indexExists('idx_connector_event_source_entity') ||
550
+ !this.schemaVersionExists(67);
551
+ if (hasMissingSourceEntityIdFeature) {
552
+ this.recoverConnectorEventSourceEntityIdMigration067();
553
+ (0, debug_logger_js_1.info)('[node-sqlite-adapter] Repaired skipped connector event source_entity_id migration');
554
+ }
518
555
  }
519
556
  if (!this.tableExists('twin_edges')) {
520
557
  this.applyRepairMigration(migrationsDir, '035-create-twin-edges.sql', 'twin edge ledger');
@@ -630,6 +667,65 @@ class NodeSQLiteAdapter extends base_adapter_js_1.DatabaseAdapter {
630
667
  throw new Error('Migration 041 recovery failed: incompatible operator_memory_commit_intents table definition missing claim invariant');
631
668
  }
632
669
  }
670
+ needsToolTraceDiagnosticsRepair068() {
671
+ if (!this.tableExists('tool_traces')) {
672
+ // The table itself is missing: let the recovery helper fail loudly.
673
+ return true;
674
+ }
675
+ const columns = this.tableColumns('tool_traces');
676
+ const hasMissingColumn = [
677
+ 'diagnostic_json',
678
+ 'evidence_json',
679
+ 'catalog_revision',
680
+ 'owner_scope',
681
+ 'project_id',
682
+ 'channel_id',
683
+ ].some((column) => !columns.has(column));
684
+ const hasMissingIndex = [
685
+ 'idx_tool_traces_scope_recency',
686
+ 'idx_tool_traces_channel_recency',
687
+ ].some((indexName) => !this.indexExists(indexName));
688
+ return hasMissingColumn || hasMissingIndex || !this.schemaVersionExists(68);
689
+ }
690
+ recoverToolTraceDiagnosticsMigration068() {
691
+ this.transaction(() => {
692
+ if (!this.tableExists('tool_traces')) {
693
+ throw new Error('Migration 068 recovery failed: missing table tool_traces');
694
+ }
695
+ const expectedColumns = [
696
+ 'diagnostic_json',
697
+ 'evidence_json',
698
+ 'catalog_revision',
699
+ 'owner_scope',
700
+ 'project_id',
701
+ 'channel_id',
702
+ ];
703
+ const columns = this.tableColumns('tool_traces');
704
+ for (const column of expectedColumns) {
705
+ if (!columns.has(column)) {
706
+ this.exec(`ALTER TABLE tool_traces ADD COLUMN ${column} TEXT`);
707
+ }
708
+ }
709
+ this.exec(`
710
+ CREATE INDEX IF NOT EXISTS idx_tool_traces_scope_recency
711
+ ON tool_traces(owner_scope, project_id, created_at DESC, trace_id DESC);
712
+ CREATE INDEX IF NOT EXISTS idx_tool_traces_channel_recency
713
+ ON tool_traces(owner_scope, project_id, channel_id, created_at DESC, trace_id DESC);
714
+ `);
715
+ const actualColumns = this.tableColumns('tool_traces');
716
+ for (const column of expectedColumns) {
717
+ if (!actualColumns.has(column)) {
718
+ throw new Error(`Migration 068 recovery failed: missing tool_traces.${column}`);
719
+ }
720
+ }
721
+ for (const index of ['idx_tool_traces_scope_recency', 'idx_tool_traces_channel_recency']) {
722
+ if (!this.indexExists(index)) {
723
+ throw new Error(`Migration 068 recovery failed: missing index ${index}`);
724
+ }
725
+ }
726
+ this.prepare('INSERT OR IGNORE INTO schema_version (version) VALUES (?)').run(68);
727
+ });
728
+ }
633
729
  recoverMemoryProvenanceMigration032() {
634
730
  this.transaction(() => {
635
731
  const expectedDecisionColumns = [
@@ -743,6 +839,40 @@ class NodeSQLiteAdapter extends base_adapter_js_1.DatabaseAdapter {
743
839
  }
744
840
  }
745
841
  }
842
+ recoverConnectorEventSourceEntityIdMigration067() {
843
+ this.transaction(() => {
844
+ const columns = this.tableColumns('connector_event_index');
845
+ if (!columns.has('source_entity_id')) {
846
+ this.exec('ALTER TABLE connector_event_index ADD COLUMN source_entity_id TEXT');
847
+ }
848
+ // Backfill only when metadata_json exists; a legacy/minimal table rebuilt by the repair path
849
+ // may not have it yet. New rows carry source_entity_id at write time regardless.
850
+ if (columns.has('metadata_json')) {
851
+ this.exec(`
852
+ UPDATE connector_event_index
853
+ SET source_entity_id = json_extract(metadata_json, '$.sourceEntityId')
854
+ WHERE source_entity_id IS NULL
855
+ AND metadata_json IS NOT NULL
856
+ AND json_extract(metadata_json, '$.sourceEntityId') IS NOT NULL
857
+ `);
858
+ }
859
+ this.exec(`
860
+ CREATE INDEX IF NOT EXISTS idx_connector_event_source_entity
861
+ ON connector_event_index(source_connector, source_entity_id)
862
+ `);
863
+ this.assertMigration067Complete();
864
+ this.prepare('INSERT OR IGNORE INTO schema_version (version, description) VALUES (?, ?)').run(67, 'Add connector_event_index source_entity_id');
865
+ });
866
+ }
867
+ assertMigration067Complete() {
868
+ const columns = this.tableColumns('connector_event_index');
869
+ if (!columns.has('source_entity_id')) {
870
+ throw new Error('Migration 067 recovery failed: missing connector_event_index.source_entity_id');
871
+ }
872
+ if (!this.indexExists('idx_connector_event_source_entity')) {
873
+ throw new Error('Migration 067 recovery failed: missing index idx_connector_event_source_entity');
874
+ }
875
+ }
746
876
  recoverConnectorEventOperatorSeqMigration039() {
747
877
  this.transaction(() => {
748
878
  const columns = this.tableColumns('connector_event_index');
package/dist/index.d.ts CHANGED
@@ -25,9 +25,9 @@ export { createTrustedProvenanceCapability, assertTrustedProvenanceCapability, t
25
25
  export { getMemoryProvenance, listMemoriesByEnvelopeHash, listMemoriesByGatewayCallId, listMemoriesByModelRunId, } from './memory/provenance-query.js';
26
26
  export { type BackfillResult, type ConnectorEventScopeBackfillInput, } from './memory/scope-backfill.js';
27
27
  export { getMemoryProvenanceAudit, listMemoryProvenanceAudit, type MemoryProvenanceAuditRecord, type MemoryProvenanceAuditListOptions, } from './memory/provenance-audit.js';
28
- export { MODEL_RUN_STATUSES, type ModelRunStatus, type BeginModelRunInput, type ModelRunRecord, type AppendToolTraceInput, type ToolTraceRecord, } from './model-runs/types.js';
28
+ export { MODEL_RUN_STATUSES, type ModelRunStatus, type BeginModelRunInput, type ModelRunRecord, type AppendToolTraceInput, type ToolTraceRecord, type ToolTraceScope, type ListToolTracesInput, type ToolTracePage, } from './model-runs/types.js';
29
29
  export { beginModelRun, beginModelRunInAdapter, commitModelRun, commitModelRunInAdapter, failModelRun, failModelRunInAdapter, getModelRun, getModelRunInAdapter, } from './model-runs/store.js';
30
- export { appendToolTrace, listToolTracesForRun } from './model-runs/tool-trace-store.js';
30
+ export { appendToolTrace, listToolTracesForRun, listToolTraces, readToolTrace, } from './model-runs/tool-trace-store.js';
31
31
  export { TWIN_EDGE_SOURCES, TWIN_EDGE_TYPES, TWIN_REF_KINDS, type InsertTwinEdgeInput, type ListVisibleTwinEdgesOptions, type TwinEdgeRecord, type TwinEdgeSource, type TwinEdgeType, type TwinRef, type TwinRefKind, type TwinScopeRef, } from './edges/types.js';
32
32
  export { getTwinEdge, listTwinEdgesForRefs, mapTwinEdgeRow } from './edges/store.js';
33
33
  export { listVisibleTwinEdgesForRefs } from './edges/ref-validation.js';
package/dist/index.js CHANGED
@@ -27,9 +27,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  exports.createAuditAck = exports.buildMemoryBootstrap = exports.evolveMemory = exports.ingestWithTrustedProvenance = exports.ingestMemory = exports.buildProfile = exports.recallMemory = exports.promoteMemoryStatus = exports.saveMemoryWithTrustedProvenance = 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.SQLiteAdapter = exports.DatabaseAdapter = exports.createAdapter = 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.EmbeddingCache = exports.EMBEDDING_PREFIX_SCHEME = exports.MODEL_NAME = exports.EMBEDDING_DIM = exports.embeddingCache = exports.cosineSimilarity = exports.generateEnhancedEmbedding = exports.generateEmbedding = void 0;
30
- exports.getConfigPath = exports.updateConfig = exports.getCacheDir = exports.getEmbeddingDim = exports.getModelName = exports.loadConfig = exports.CanonicalizeError = exports.targetRefHash = exports.canonicalizeJSON = exports.listVisibleTwinEdgesForRefs = exports.mapTwinEdgeRow = exports.listTwinEdgesForRefs = 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.listMemoriesByModelRunId = exports.listMemoriesByGatewayCallId = exports.listMemoriesByEnvelopeHash = exports.getMemoryProvenance = exports.assertTrustedProvenanceCapability = exports.createTrustedProvenanceCapability = exports.listRecentMemoryEvents = exports.listMemoryEventsForMemory = exports.insertMemoryEventInTransaction = exports.appendMemoryEvent = exports.listOpenAuditFindings = exports.createAuditFinding = exports.queryRelevantTruth = exports.parseExtractionResponse = exports.buildExtractionPrompt = exports.getChannelSummary = exports.upsertChannelSummary = exports.setExtractionFn = exports.ingestConversationWithTrustedProvenance = exports.ingestConversation = exports.recordMemoryAudit = void 0;
31
- exports.formatTrustContext = 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 = exports.NotFoundError = exports.MAMAError = exports.formatTimeAgo = exports.DebugLogger = exports.error = exports.warn = exports.info = exports.debug = exports.logSearching = exports.logLoading = exports.logError = exports.logFailed = exports.logComplete = exports.logProgress = exports.getTierDescription = exports.checkDatabase = exports.checkEmbeddings = exports.checkSQLite = exports.checkNodeVersion = exports.VALID_EDGE_TYPES = exports.updateConfidence = exports.createEdgesFromReasoning = exports.parseReasoningForRelationships = exports.detectRefinement = exports.calculateCombinedConfidence = exports.markSuperseded = exports.createSupersedesEdge = exports.createEdge = exports.getPreviousDecision = exports.generateDecisionId = exports.learnDecision = exports.formatTopNContext = exports.selectTopDecisions = exports.calculateRelevance = exports.DEFAULT_CONFIG = void 0;
32
- exports.notifyInsight = exports.FALLBACK_MODEL = exports.DEFAULT_MODEL = exports.listModels = exports.isAvailable = exports.generate = exports.analyzeIntent = exports.RECENT_WINDOW_MS = exports.PARTIAL_INDICATORS = exports.SUCCESS_INDICATORS = exports.FAILURE_INDICATORS = 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 = void 0;
30
+ exports.getCacheDir = exports.getEmbeddingDim = exports.getModelName = exports.loadConfig = exports.CanonicalizeError = exports.targetRefHash = exports.canonicalizeJSON = exports.listVisibleTwinEdgesForRefs = exports.mapTwinEdgeRow = exports.listTwinEdgesForRefs = exports.getTwinEdge = exports.TWIN_REF_KINDS = exports.TWIN_EDGE_TYPES = exports.TWIN_EDGE_SOURCES = exports.readToolTrace = exports.listToolTraces = 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.listMemoriesByModelRunId = exports.listMemoriesByGatewayCallId = exports.listMemoriesByEnvelopeHash = exports.getMemoryProvenance = exports.assertTrustedProvenanceCapability = exports.createTrustedProvenanceCapability = exports.listRecentMemoryEvents = exports.listMemoryEventsForMemory = exports.insertMemoryEventInTransaction = exports.appendMemoryEvent = exports.listOpenAuditFindings = exports.createAuditFinding = exports.queryRelevantTruth = exports.parseExtractionResponse = exports.buildExtractionPrompt = exports.getChannelSummary = exports.upsertChannelSummary = exports.setExtractionFn = exports.ingestConversationWithTrustedProvenance = exports.ingestConversation = exports.recordMemoryAudit = void 0;
31
+ 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 = exports.NotFoundError = exports.MAMAError = exports.formatTimeAgo = exports.DebugLogger = exports.error = exports.warn = exports.info = exports.debug = exports.logSearching = exports.logLoading = exports.logError = exports.logFailed = exports.logComplete = exports.logProgress = exports.getTierDescription = exports.checkDatabase = exports.checkEmbeddings = exports.checkSQLite = exports.checkNodeVersion = exports.VALID_EDGE_TYPES = exports.updateConfidence = exports.createEdgesFromReasoning = exports.parseReasoningForRelationships = exports.detectRefinement = exports.calculateCombinedConfidence = exports.markSuperseded = exports.createSupersedesEdge = exports.createEdge = exports.getPreviousDecision = exports.generateDecisionId = exports.learnDecision = exports.formatTopNContext = exports.selectTopDecisions = exports.calculateRelevance = exports.DEFAULT_CONFIG = exports.getConfigPath = exports.updateConfig = void 0;
32
+ exports.notifyInsight = exports.FALLBACK_MODEL = exports.DEFAULT_MODEL = exports.listModels = exports.isAvailable = exports.generate = exports.analyzeIntent = exports.RECENT_WINDOW_MS = exports.PARTIAL_INDICATORS = exports.SUCCESS_INDICATORS = exports.FAILURE_INDICATORS = 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.formatList = 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; } });
@@ -133,6 +133,8 @@ Object.defineProperty(exports, "getModelRunInAdapter", { enumerable: true, get:
133
133
  var tool_trace_store_js_1 = require("./model-runs/tool-trace-store.js");
134
134
  Object.defineProperty(exports, "appendToolTrace", { enumerable: true, get: function () { return tool_trace_store_js_1.appendToolTrace; } });
135
135
  Object.defineProperty(exports, "listToolTracesForRun", { enumerable: true, get: function () { return tool_trace_store_js_1.listToolTracesForRun; } });
136
+ Object.defineProperty(exports, "listToolTraces", { enumerable: true, get: function () { return tool_trace_store_js_1.listToolTraces; } });
137
+ Object.defineProperty(exports, "readToolTrace", { enumerable: true, get: function () { return tool_trace_store_js_1.readToolTrace; } });
136
138
  var types_js_3 = require("./edges/types.js");
137
139
  Object.defineProperty(exports, "TWIN_EDGE_SOURCES", { enumerable: true, get: function () { return types_js_3.TWIN_EDGE_SOURCES; } });
138
140
  Object.defineProperty(exports, "TWIN_EDGE_TYPES", { enumerable: true, get: function () { return types_js_3.TWIN_EDGE_TYPES; } });
@@ -25,7 +25,7 @@ import { listMemoryEventsForMemory, listRecentMemoryEvents } from './memory/even
25
25
  import type { TrustedMemoryWriteOptions } from './memory/provenance.js';
26
26
  import { getMemoryProvenance, listMemoriesByEnvelopeHash, listMemoriesByGatewayCallId, listMemoriesByModelRunId } from './memory/provenance-query.js';
27
27
  import { beginModelRun, beginModelRunInAdapter, commitModelRun, commitModelRunInAdapter, failModelRun, failModelRunInAdapter, getModelRun, getModelRunInAdapter } from './model-runs/store.js';
28
- import { appendToolTrace, listToolTracesForRun } from './model-runs/tool-trace-store.js';
28
+ import { appendToolTrace, listToolTracesForRun, listToolTraces, readToolTrace } from './model-runs/tool-trace-store.js';
29
29
  import { type SearchHitDiagnostics, type SearchQualityOptions } from './search/search-quality.js';
30
30
  /**
31
31
  * Parameters for mama.save()
@@ -1083,6 +1083,8 @@ declare const mama: {
1083
1083
  getModelRunInAdapter: typeof getModelRunInAdapter;
1084
1084
  appendToolTrace: typeof appendToolTrace;
1085
1085
  listToolTracesForRun: typeof listToolTracesForRun;
1086
+ listToolTraces: typeof listToolTraces;
1087
+ readToolTrace: typeof readToolTrace;
1086
1088
  saveCheckpoint: typeof saveCheckpoint;
1087
1089
  loadCheckpoint: typeof loadCheckpoint;
1088
1090
  recall: typeof recall;
@@ -1107,6 +1109,6 @@ declare const mama: {
1107
1109
  validateCleanupResult: typeof validateCleanupResult;
1108
1110
  expandWithGraph: typeof expandWithGraph;
1109
1111
  };
1110
- export { save, saveWithTrustedProvenance, suggest, annotateTopicCurrency, saveMemory, saveMemoryWithTrustedProvenance, recallMemory, listDecisions as list, listCheckpoints, updateOutcome, buildProfile, ingestMemory, ingestWithTrustedProvenance, ingestConversation, ingestConversationWithTrustedProvenance, evolveMemory, buildMemoryBootstrap, createAuditAck, recordMemoryAudit, upsertChannelSummary, getChannelSummary, listOpenAuditFindings, createAuditFinding, getMemoryProvenance, listMemoriesByEnvelopeHash, listMemoriesByGatewayCallId, listMemoriesByModelRunId, listMemoryEventsForMemory, listRecentMemoryEvents, beginModelRun, beginModelRunInAdapter, commitModelRun, commitModelRunInAdapter, failModelRun, failModelRunInAdapter, getModelRun, getModelRunInAdapter, appendToolTrace, listToolTracesForRun, saveCheckpoint, loadCheckpoint, recall, proposeLink, approveLink, rejectLink, getPendingLinks, deprecateAutoLinks, calculateCoverage, calculateQuality, generateQualityReport, logRestartAttempt, calculateRestartSuccessRate, calculateRestartLatency, getRestartMetrics, scanAutoLinks, createLinkBackup, generatePreCleanupReport, restoreLinkBackup, verifyBackupExists, deleteAutoLinks, validateCleanupResult, expandWithGraph, };
1112
+ export { save, saveWithTrustedProvenance, suggest, annotateTopicCurrency, saveMemory, saveMemoryWithTrustedProvenance, recallMemory, listDecisions as list, listCheckpoints, updateOutcome, buildProfile, ingestMemory, ingestWithTrustedProvenance, ingestConversation, ingestConversationWithTrustedProvenance, evolveMemory, buildMemoryBootstrap, createAuditAck, recordMemoryAudit, upsertChannelSummary, getChannelSummary, listOpenAuditFindings, createAuditFinding, getMemoryProvenance, listMemoriesByEnvelopeHash, listMemoriesByGatewayCallId, listMemoriesByModelRunId, listMemoryEventsForMemory, listRecentMemoryEvents, beginModelRun, beginModelRunInAdapter, commitModelRun, commitModelRunInAdapter, failModelRun, failModelRunInAdapter, getModelRun, getModelRunInAdapter, appendToolTrace, listToolTracesForRun, listToolTraces, readToolTrace, saveCheckpoint, loadCheckpoint, recall, proposeLink, approveLink, rejectLink, getPendingLinks, deprecateAutoLinks, calculateCoverage, calculateQuality, generateQualityReport, logRestartAttempt, calculateRestartSuccessRate, calculateRestartLatency, getRestartMetrics, scanAutoLinks, createLinkBackup, generatePreCleanupReport, restoreLinkBackup, verifyBackupExists, deleteAutoLinks, validateCleanupResult, expandWithGraph, };
1111
1113
  export default mama;
1112
1114
  //# sourceMappingURL=mama-api.d.ts.map
package/dist/mama-api.js CHANGED
@@ -22,7 +22,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
22
22
  return (mod && mod.__esModule) ? mod : { "default": mod };
23
23
  };
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.listToolTracesForRun = exports.appendToolTrace = exports.getModelRunInAdapter = exports.getModelRun = exports.failModelRunInAdapter = exports.failModelRun = exports.commitModelRunInAdapter = exports.commitModelRun = exports.beginModelRunInAdapter = exports.beginModelRun = exports.listRecentMemoryEvents = exports.listMemoryEventsForMemory = exports.listMemoriesByModelRunId = exports.listMemoriesByGatewayCallId = exports.listMemoriesByEnvelopeHash = exports.getMemoryProvenance = exports.createAuditFinding = exports.listOpenAuditFindings = exports.getChannelSummary = exports.upsertChannelSummary = exports.recordMemoryAudit = exports.createAuditAck = exports.buildMemoryBootstrap = exports.evolveMemory = exports.ingestConversationWithTrustedProvenance = exports.ingestConversation = exports.ingestWithTrustedProvenance = exports.ingestMemory = exports.buildProfile = exports.recallMemory = exports.saveMemoryWithTrustedProvenance = exports.saveMemory = void 0;
25
+ exports.readToolTrace = exports.listToolTraces = exports.listToolTracesForRun = exports.appendToolTrace = exports.getModelRunInAdapter = exports.getModelRun = exports.failModelRunInAdapter = exports.failModelRun = exports.commitModelRunInAdapter = exports.commitModelRun = exports.beginModelRunInAdapter = exports.beginModelRun = exports.listRecentMemoryEvents = exports.listMemoryEventsForMemory = exports.listMemoriesByModelRunId = exports.listMemoriesByGatewayCallId = exports.listMemoriesByEnvelopeHash = exports.getMemoryProvenance = exports.createAuditFinding = exports.listOpenAuditFindings = exports.getChannelSummary = exports.upsertChannelSummary = exports.recordMemoryAudit = exports.createAuditAck = exports.buildMemoryBootstrap = exports.evolveMemory = exports.ingestConversationWithTrustedProvenance = exports.ingestConversation = exports.ingestWithTrustedProvenance = exports.ingestMemory = exports.buildProfile = exports.recallMemory = exports.saveMemoryWithTrustedProvenance = exports.saveMemory = void 0;
26
26
  exports.save = save;
27
27
  exports.saveWithTrustedProvenance = saveWithTrustedProvenance;
28
28
  exports.suggest = suggest;
@@ -106,6 +106,8 @@ Object.defineProperty(exports, "getModelRunInAdapter", { enumerable: true, get:
106
106
  const tool_trace_store_js_1 = require("./model-runs/tool-trace-store.js");
107
107
  Object.defineProperty(exports, "appendToolTrace", { enumerable: true, get: function () { return tool_trace_store_js_1.appendToolTrace; } });
108
108
  Object.defineProperty(exports, "listToolTracesForRun", { enumerable: true, get: function () { return tool_trace_store_js_1.listToolTracesForRun; } });
109
+ Object.defineProperty(exports, "listToolTraces", { enumerable: true, get: function () { return tool_trace_store_js_1.listToolTraces; } });
110
+ Object.defineProperty(exports, "readToolTrace", { enumerable: true, get: function () { return tool_trace_store_js_1.readToolTrace; } });
109
111
  const search_rollup_js_1 = require("./cases/search-rollup.js");
110
112
  const ranker_rescore_js_1 = require("./search/ranker-rescore.js");
111
113
  const ranker_features_js_1 = require("./search/ranker-features.js");
@@ -2832,6 +2834,8 @@ const mama = {
2832
2834
  getModelRunInAdapter: store_js_1.getModelRunInAdapter,
2833
2835
  appendToolTrace: tool_trace_store_js_1.appendToolTrace,
2834
2836
  listToolTracesForRun: tool_trace_store_js_1.listToolTracesForRun,
2837
+ listToolTraces: tool_trace_store_js_1.listToolTraces,
2838
+ readToolTrace: tool_trace_store_js_1.readToolTrace,
2835
2839
  saveCheckpoint,
2836
2840
  loadCheckpoint,
2837
2841
  // Legacy functions (retained for internal use, not exposed via MCP)
@@ -1,4 +1,8 @@
1
- import type { AppendToolTraceInput, ToolTraceRecord } from './types.js';
1
+ import type { AppendToolTraceInput, ToolTraceRecord, ToolTraceScope, ListToolTracesInput, ToolTracePage } from './types.js';
2
2
  export declare function appendToolTrace(input: AppendToolTraceInput): Promise<ToolTraceRecord>;
3
3
  export declare function listToolTracesForRun(modelRunId: string): Promise<ToolTraceRecord[]>;
4
+ /** Access is exact host-derived scope; knowing a trace ID never grants access. */
5
+ export declare function readToolTrace(traceId: string, scope: ToolTraceScope): Promise<ToolTraceRecord | null>;
6
+ /** Bounded metadata discovery; detailed evidence is read explicitly by trace ID. */
7
+ export declare function listToolTraces(input: ListToolTracesInput): Promise<ToolTracePage>;
4
8
  //# sourceMappingURL=tool-trace-store.d.ts.map
@@ -5,6 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.appendToolTrace = appendToolTrace;
7
7
  exports.listToolTracesForRun = listToolTracesForRun;
8
+ exports.readToolTrace = readToolTrace;
9
+ exports.listToolTraces = listToolTraces;
8
10
  const node_crypto_1 = __importDefault(require("node:crypto"));
9
11
  const db_manager_js_1 = require("../db-manager.js");
10
12
  function traceId() {
@@ -47,8 +49,33 @@ function requiredNonNegativeInteger(value, field) {
47
49
  }
48
50
  return normalized;
49
51
  }
52
+ function nullableJsonObject(value, field) {
53
+ if (value === undefined || value === null) {
54
+ return null;
55
+ }
56
+ if (typeof value !== 'string') {
57
+ throw new Error(`tool_traces.${field} must be a JSON object string`);
58
+ }
59
+ let parsed;
60
+ try {
61
+ parsed = JSON.parse(value);
62
+ }
63
+ catch {
64
+ throw new Error(`tool_traces.${field} contains malformed JSON`);
65
+ }
66
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
67
+ throw new Error(`tool_traces.${field} must contain a JSON object`);
68
+ }
69
+ return value;
70
+ }
50
71
  function mapToolTraceRow(row) {
51
72
  return {
73
+ diagnostic_json: nullableJsonObject(row.diagnostic_json, 'diagnostic_json'),
74
+ evidence_json: nullableJsonObject(row.evidence_json, 'evidence_json'),
75
+ catalog_revision: nullableString(row.catalog_revision),
76
+ owner_scope: nullableString(row.owner_scope),
77
+ project_id: nullableString(row.project_id),
78
+ channel_id: nullableString(row.channel_id),
52
79
  trace_id: requiredString(row.trace_id, 'trace_id'),
53
80
  model_run_id: requiredString(row.model_run_id, 'model_run_id'),
54
81
  gateway_call_id: nullableString(row.gateway_call_id),
@@ -72,7 +99,7 @@ function selectToolTrace(adapter, id) {
72
99
  SELECT
73
100
  trace_id, model_run_id, gateway_call_id, tool_name, input_summary,
74
101
  output_summary, execution_status, duration_ms, envelope_hash, failure_code,
75
- created_at
102
+ created_at, diagnostic_json, evidence_json, catalog_revision, owner_scope, project_id, channel_id
76
103
  FROM tool_traces
77
104
  WHERE trace_id = ?
78
105
  `)
@@ -92,11 +119,11 @@ async function appendToolTrace(input) {
92
119
  INSERT INTO tool_traces (
93
120
  trace_id, model_run_id, gateway_call_id, tool_name, input_summary,
94
121
  output_summary, execution_status, duration_ms, envelope_hash, failure_code,
95
- created_at
122
+ created_at, diagnostic_json, evidence_json, catalog_revision, owner_scope, project_id, channel_id
96
123
  )
97
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
124
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
98
125
  `)
99
- .run(id, modelRunId, nullableString(input.gateway_call_id), toolName, nullableString(input.input_summary), nullableString(input.output_summary), nullableString(input.execution_status), normalizeDuration(input.duration_ms), nullableString(input.envelope_hash), nullableString(input.failure_code), normalizeTimestamp(input.created_at));
126
+ .run(id, modelRunId, nullableString(input.gateway_call_id), toolName, nullableString(input.input_summary), nullableString(input.output_summary), nullableString(input.execution_status), normalizeDuration(input.duration_ms), nullableString(input.envelope_hash), nullableString(input.failure_code), normalizeTimestamp(input.created_at), nullableJsonObject(input.diagnostic_json, 'diagnostic_json'), nullableJsonObject(input.evidence_json, 'evidence_json'), nullableString(input.catalog_revision), nullableString(input.owner_scope), nullableString(input.project_id), nullableString(input.channel_id));
100
127
  return selectToolTrace(adapter, id);
101
128
  }
102
129
  async function listToolTracesForRun(modelRunId) {
@@ -107,7 +134,7 @@ async function listToolTracesForRun(modelRunId) {
107
134
  SELECT
108
135
  trace_id, model_run_id, gateway_call_id, tool_name, input_summary,
109
136
  output_summary, execution_status, duration_ms, envelope_hash, failure_code,
110
- created_at
137
+ created_at, diagnostic_json, evidence_json, catalog_revision, owner_scope, project_id, channel_id
111
138
  FROM tool_traces
112
139
  WHERE model_run_id = ?
113
140
  ORDER BY created_at DESC, rowid DESC
@@ -115,4 +142,82 @@ async function listToolTracesForRun(modelRunId) {
115
142
  .all(id);
116
143
  return rows.map(mapToolTraceRow);
117
144
  }
145
+ function scopedWhere(scope) {
146
+ const clauses = ['owner_scope = ?', 'project_id = ?'];
147
+ const values = [
148
+ requiredString(scope.owner_scope, 'owner_scope'),
149
+ requiredString(scope.project_id, 'project_id'),
150
+ ];
151
+ if (scope.channel_id !== undefined) {
152
+ clauses.push('channel_id = ?');
153
+ values.push(requiredString(scope.channel_id, 'channel_id'));
154
+ }
155
+ return { clauses, values };
156
+ }
157
+ /** Access is exact host-derived scope; knowing a trace ID never grants access. */
158
+ async function readToolTrace(traceId, scope) {
159
+ const { clauses, values } = scopedWhere(scope);
160
+ clauses.push('trace_id = ?');
161
+ values.push(requiredString(traceId, 'trace_id'));
162
+ const adapter = await initializedAdapter();
163
+ const row = adapter
164
+ .prepare(`SELECT * FROM tool_traces WHERE ${clauses.join(' AND ')}`)
165
+ .get(...values);
166
+ return row ? mapToolTraceRow(row) : null;
167
+ }
168
+ /** Bounded metadata discovery; detailed evidence is read explicitly by trace ID. */
169
+ async function listToolTraces(input) {
170
+ const { clauses, values } = scopedWhere(input);
171
+ if (input.evidence_only !== undefined && typeof input.evidence_only !== 'boolean') {
172
+ throw new Error('tool_traces.evidence_only must be a boolean');
173
+ }
174
+ if (input.evidence_only === true) {
175
+ clauses.push('evidence_json IS NOT NULL');
176
+ }
177
+ const limit = input.limit ?? 20;
178
+ if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
179
+ throw new Error('tool_traces.limit must be an integer between 1 and 100');
180
+ }
181
+ for (const field of ['model_run_id', 'tool_name']) {
182
+ if (input[field] !== undefined) {
183
+ clauses.push(`${field} = ?`);
184
+ values.push(requiredString(input[field], field));
185
+ }
186
+ }
187
+ if (input.cursor !== undefined) {
188
+ let cursor;
189
+ try {
190
+ cursor = JSON.parse(Buffer.from(input.cursor, 'base64url').toString('utf8'));
191
+ }
192
+ catch {
193
+ throw new Error('tool_traces.cursor is malformed');
194
+ }
195
+ if (!Array.isArray(cursor) || cursor.length !== 2) {
196
+ throw new Error('tool_traces.cursor is malformed');
197
+ }
198
+ const [cursorCreatedAt, cursorTraceId] = cursor;
199
+ if (typeof cursorCreatedAt !== 'number' ||
200
+ !Number.isSafeInteger(cursorCreatedAt) ||
201
+ typeof cursorTraceId !== 'string' ||
202
+ cursorTraceId.length === 0) {
203
+ throw new Error('tool_traces.cursor is malformed');
204
+ }
205
+ clauses.push('(created_at < ? OR (created_at = ? AND trace_id < ?))');
206
+ values.push(cursorCreatedAt, cursorCreatedAt, cursorTraceId);
207
+ }
208
+ const adapter = await initializedAdapter();
209
+ const rows = adapter
210
+ .prepare(`SELECT trace_id, model_run_id, gateway_call_id, tool_name,
211
+ input_summary, output_summary, execution_status, duration_ms, envelope_hash, failure_code,
212
+ created_at, diagnostic_json, NULL AS evidence_json, catalog_revision, owner_scope, project_id, channel_id
213
+ FROM tool_traces WHERE ${clauses.join(' AND ')}
214
+ ORDER BY created_at DESC, trace_id DESC LIMIT ?`)
215
+ .all(...values, limit + 1);
216
+ const traces = rows.slice(0, limit).map(mapToolTraceRow);
217
+ const last = traces.at(-1);
218
+ const next_cursor = rows.length > limit && last
219
+ ? Buffer.from(JSON.stringify([last.created_at, last.trace_id])).toString('base64url')
220
+ : null;
221
+ return { traces, next_cursor };
222
+ }
118
223
  //# sourceMappingURL=tool-trace-store.js.map
@@ -43,6 +43,13 @@ export interface ModelRunRecord {
43
43
  completed_at: number | null;
44
44
  }
45
45
  export interface AppendToolTraceInput {
46
+ /** Host-derived access scope; legacy rows remain unscoped. */
47
+ owner_scope?: string | null;
48
+ project_id?: string | null;
49
+ channel_id?: string | null;
50
+ diagnostic_json?: string | null;
51
+ evidence_json?: string | null;
52
+ catalog_revision?: string | null;
46
53
  trace_id?: string;
47
54
  model_run_id: string;
48
55
  gateway_call_id?: string | null;
@@ -57,6 +64,13 @@ export interface AppendToolTraceInput {
57
64
  created_at?: number;
58
65
  }
59
66
  export interface ToolTraceRecord {
67
+ /** Host-derived access scope; legacy rows remain unscoped. */
68
+ owner_scope?: string | null;
69
+ project_id?: string | null;
70
+ channel_id?: string | null;
71
+ diagnostic_json?: string | null;
72
+ evidence_json?: string | null;
73
+ catalog_revision?: string | null;
60
74
  trace_id: string;
61
75
  model_run_id: string;
62
76
  gateway_call_id: string | null;
@@ -69,4 +83,21 @@ export interface ToolTraceRecord {
69
83
  failure_code: string | null;
70
84
  created_at: number;
71
85
  }
86
+ export interface ToolTraceScope {
87
+ owner_scope: string;
88
+ project_id: string;
89
+ channel_id?: string;
90
+ }
91
+ export interface ListToolTracesInput extends ToolTraceScope {
92
+ evidence_only?: boolean;
93
+ model_run_id?: string;
94
+ tool_name?: string;
95
+ cursor?: string;
96
+ limit?: number;
97
+ }
98
+ export interface ToolTracePage {
99
+ /** Metadata only: evidence_json is null. Read a scoped trace for details. */
100
+ traces: ToolTraceRecord[];
101
+ next_cursor: string | null;
102
+ }
72
103
  //# sourceMappingURL=types.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jungjaehoon/mama-core",
3
- "version": "2.3.0",
3
+ "version": "2.4.1",
4
4
  "description": "MAMA Core - Shared modules for Memory-Augmented MCP Assistant",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",