@jungjaehoon/mama-core 1.9.0 → 2.1.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 (139) hide show
  1. package/db/migrations/043-add-tool-trace-failure-code.sql +11 -0
  2. package/dist/agent-graph/alias-write.js +3 -0
  3. package/dist/agent-graph/entity-resolve.js +3 -7
  4. package/dist/agent-graph/graph-query.js +5 -4
  5. package/dist/agent-graph/types.d.ts +30 -0
  6. package/dist/cases/store.d.ts +0 -4
  7. package/dist/cases/store.js +0 -66
  8. package/dist/cases/wiki-page-index.d.ts +0 -13
  9. package/dist/cases/wiki-page-index.js +0 -153
  10. package/dist/connectors/event-index.d.ts +1 -22
  11. package/dist/connectors/event-index.js +0 -157
  12. package/dist/context-compile/channel-grant.d.ts +46 -0
  13. package/dist/context-compile/channel-grant.js +66 -0
  14. package/dist/context-compile/compiler.js +5 -2
  15. package/dist/context-compile/index.d.ts +1 -0
  16. package/dist/context-compile/index.js +1 -0
  17. package/dist/context-compile/packet-store.d.ts +0 -1
  18. package/dist/context-compile/packet-store.js +0 -16
  19. package/dist/context-compile/source-readers.d.ts +1 -0
  20. package/dist/context-compile/source-readers.js +156 -23
  21. package/dist/context-compile/types.d.ts +17 -0
  22. package/dist/context-compile/visibility.js +28 -4
  23. package/dist/db-adapter/node-sqlite-adapter.js +4 -0
  24. package/dist/db-manager.d.ts +0 -13
  25. package/dist/db-manager.js +0 -66
  26. package/dist/decision-formatter.d.ts +0 -11
  27. package/dist/decision-formatter.js +0 -122
  28. package/dist/decision-tracker.d.ts +0 -12
  29. package/dist/decision-tracker.js +0 -50
  30. package/dist/edges/ref-validation.d.ts +1 -2
  31. package/dist/edges/ref-validation.js +33 -13
  32. package/dist/edges/store.d.ts +1 -3
  33. package/dist/edges/store.js +0 -214
  34. package/dist/edges/types.d.ts +10 -0
  35. package/dist/embedding-client.d.ts +0 -17
  36. package/dist/embedding-client.js +0 -80
  37. package/dist/embedding-server/mobile/auth.d.ts +1 -10
  38. package/dist/embedding-server/mobile/auth.js +0 -15
  39. package/dist/embeddings.d.ts +0 -12
  40. package/dist/embeddings.js +0 -68
  41. package/dist/entities/audit-metrics.d.ts +0 -5
  42. package/dist/entities/audit-metrics.js +0 -4
  43. package/dist/entities/candidate-generator.d.ts +1 -2
  44. package/dist/entities/candidate-generator.js +0 -412
  45. package/dist/entities/entity-list.d.ts +0 -1
  46. package/dist/entities/entity-list.js +0 -96
  47. package/dist/entities/entity-orphan-list.d.ts +0 -9
  48. package/dist/entities/entity-orphan-list.js +0 -88
  49. package/dist/entities/entity-search.d.ts +0 -1
  50. package/dist/entities/entity-search.js +0 -147
  51. package/dist/entities/errors.d.ts +0 -10
  52. package/dist/entities/errors.js +1 -25
  53. package/dist/entities/lineage-store.d.ts +0 -23
  54. package/dist/entities/lineage-store.js +0 -104
  55. package/dist/entities/policy-store.d.ts +0 -9
  56. package/dist/entities/policy-store.js +0 -188
  57. package/dist/entities/provenance-query.d.ts +0 -1
  58. package/dist/entities/provenance-query.js +0 -164
  59. package/dist/entities/resolution-engine.d.ts +1 -8
  60. package/dist/entities/resolution-engine.js +0 -66
  61. package/dist/entities/rollback-preview.d.ts +0 -4
  62. package/dist/entities/rollback-preview.js +0 -232
  63. package/dist/entities/store.d.ts +1 -10
  64. package/dist/entities/store.js +0 -49
  65. package/dist/index.d.ts +15 -31
  66. package/dist/index.js +4 -46
  67. package/dist/mama-api.d.ts +19 -1
  68. package/dist/mama-api.js +97 -2
  69. package/dist/memory/api.d.ts +8 -0
  70. package/dist/memory/api.js +55 -2
  71. package/dist/memory/event-store.d.ts +0 -1
  72. package/dist/memory/event-store.js +0 -15
  73. package/dist/memory/finding-store.d.ts +0 -1
  74. package/dist/memory/finding-store.js +0 -12
  75. package/dist/memory/scope-backfill.d.ts +0 -3
  76. package/dist/memory/scope-backfill.js +0 -74
  77. package/dist/memory/scope-store.d.ts +0 -1
  78. package/dist/memory/scope-store.js +0 -4
  79. package/dist/memory/truth-store.d.ts +0 -3
  80. package/dist/memory/truth-store.js +0 -22
  81. package/dist/memory/types.d.ts +0 -1
  82. package/dist/memory/types.js +0 -13
  83. package/dist/memory-store.d.ts +0 -16
  84. package/dist/memory-store.js +0 -51
  85. package/dist/model-runs/tool-trace-store.js +9 -5
  86. package/dist/model-runs/types.d.ts +3 -0
  87. package/dist/ollama-client.d.ts +0 -9
  88. package/dist/ollama-client.js +0 -125
  89. package/dist/outcome-tracker.d.ts +0 -14
  90. package/dist/outcome-tracker.js +0 -44
  91. package/dist/progress-indicator.d.ts +0 -4
  92. package/dist/progress-indicator.js +0 -7
  93. package/dist/provenance/source-ref.d.ts +0 -9
  94. package/dist/provenance/source-ref.js +0 -145
  95. package/dist/query-intent.d.ts +0 -4
  96. package/dist/query-intent.js +0 -30
  97. package/dist/relevance-scorer.d.ts +0 -9
  98. package/dist/relevance-scorer.js +0 -81
  99. package/dist/search/feedback-store.d.ts +0 -1
  100. package/dist/search/feedback-store.js +0 -4
  101. package/dist/search/ranker-rescore.d.ts +0 -3
  102. package/dist/search/ranker-rescore.js +0 -27
  103. package/dist/search/ranker-trainer.d.ts +0 -8
  104. package/dist/search/ranker-trainer.js +0 -376
  105. package/dist/tier-validator.d.ts +0 -12
  106. package/dist/tier-validator.js +0 -56
  107. package/package.json +1 -10
  108. package/dist/cases/case-links.d.ts +0 -116
  109. package/dist/cases/case-links.js +0 -794
  110. package/dist/cases/composition-overrides.d.ts +0 -90
  111. package/dist/cases/composition-overrides.js +0 -724
  112. package/dist/cases/corrections.d.ts +0 -156
  113. package/dist/cases/corrections.js +0 -1045
  114. package/dist/cases/freshness.d.ts +0 -57
  115. package/dist/cases/freshness.js +0 -283
  116. package/dist/cases/live-state.d.ts +0 -48
  117. package/dist/cases/live-state.js +0 -256
  118. package/dist/cases/membership-explain.d.ts +0 -56
  119. package/dist/cases/membership-explain.js +0 -140
  120. package/dist/cases/membership-matcher.d.ts +0 -23
  121. package/dist/cases/membership-matcher.js +0 -193
  122. package/dist/cases/merge-split.d.ts +0 -61
  123. package/dist/cases/merge-split.js +0 -368
  124. package/dist/cases/role-inference.d.ts +0 -10
  125. package/dist/cases/role-inference.js +0 -85
  126. package/dist/cases/sqlite-transaction.d.ts +0 -10
  127. package/dist/cases/sqlite-transaction.js +0 -62
  128. package/dist/cases/target-ref.d.ts +0 -25
  129. package/dist/cases/target-ref.js +0 -22
  130. package/dist/cases/tombstone-sweeper.d.ts +0 -13
  131. package/dist/cases/tombstone-sweeper.js +0 -128
  132. package/dist/entities/entity-impact.d.ts +0 -41
  133. package/dist/entities/entity-impact.js +0 -140
  134. package/dist/entities/exact-merge-backfill.d.ts +0 -11
  135. package/dist/entities/exact-merge-backfill.js +0 -161
  136. package/dist/entities/lineage-backfill.d.ts +0 -12
  137. package/dist/entities/lineage-backfill.js +0 -101
  138. package/dist/memory-inject.d.ts +0 -24
  139. package/dist/memory-inject.js +0 -116
@@ -8,6 +8,7 @@ exports.normalizeSeedRefs = normalizeSeedRefs;
8
8
  const node_crypto_1 = require("node:crypto");
9
9
  const types_js_1 = require("../memory/types.js");
10
10
  const ref_js_1 = require("./ref.js");
11
+ const channel_grant_js_1 = require("./channel-grant.js");
11
12
  const SCOPE_ORDER = {
12
13
  project: 0,
13
14
  channel: 1,
@@ -110,13 +111,36 @@ function assertContextBoundaryAllowsInput(input) {
110
111
  throw new Error('Requested tenant is outside the context boundary');
111
112
  }
112
113
  const seedRefs = (0, ref_js_1.normalizeContextRefs)(input.seedRefs);
113
- assertSeedRefsAllowed(seedRefs, boundaryConnectors, hasConnectorBoundary);
114
- }
115
- function assertSeedRefsAllowed(seedRefs, boundaryConnectors, hasConnectorBoundary) {
114
+ assertSeedRefsAllowed(seedRefs, boundaryConnectors, hasConnectorBoundary, input.boundary.channels);
115
+ }
116
+ /**
117
+ * A seed ref must satisfy the same visibility rule as a row the reader would have returned.
118
+ *
119
+ * The connector check alone was never enough, and once raw visibility is decided by
120
+ * (connector, channel) it is plainly not: a caller could name the raw id of an event in an
121
+ * ungranted channel and have its identifiers - upstream source id and channel - come back
122
+ * in the packet's source_refs, which is then persisted. That is a citation path reaching
123
+ * past the reading path, the exact asymmetry this rebuild exists to remove.
124
+ *
125
+ * The channel is checked only when the boundary carries a grant. Without one there is no
126
+ * channel rule to enforce and the connector check stands, unchanged.
127
+ */
128
+ function assertSeedRefsAllowed(seedRefs, boundaryConnectors, hasConnectorBoundary, channelGrant) {
116
129
  for (const ref of seedRefs) {
117
- if (ref.kind === 'raw' && hasConnectorBoundary && !boundaryConnectors.has(ref.connector)) {
130
+ if (ref.kind !== 'raw')
131
+ continue;
132
+ if (hasConnectorBoundary && !boundaryConnectors.has(ref.connector)) {
118
133
  throw new Error(`Seed raw ref connector is outside the context boundary: ${ref.connector}`);
119
134
  }
135
+ if (!channelGrant)
136
+ continue;
137
+ // Calls the shared rule rather than re-deriving membership. An inline re-derivation
138
+ // here already disagreed with it on the empty-channel case, which is how a fourth copy
139
+ // of a decision starts: not by being written as a copy, but by being written as
140
+ // "obviously the same check".
141
+ if (!(0, channel_grant_js_1.isChannelGranted)(ref.connector, ref.channel_id, channelGrant)) {
142
+ throw new Error(`Seed raw ref channel is outside the context boundary: ${ref.connector}`);
143
+ }
120
144
  }
121
145
  }
122
146
  function isRecord(value) {
@@ -613,6 +613,10 @@ class NodeSQLiteAdapter extends base_adapter_js_1.DatabaseAdapter {
613
613
  }
614
614
  }
615
615
  this.exec(`
616
+ CREATE INDEX IF NOT EXISTS idx_decisions_topic
617
+ ON decisions(topic)
618
+ `);
619
+ this.exec(`
616
620
  CREATE INDEX IF NOT EXISTS idx_decisions_envelope_hash
617
621
  ON decisions(envelope_hash)
618
622
  `);
@@ -147,11 +147,6 @@ export declare function getAdapter(): DatabaseAdapter;
147
147
  */
148
148
  export declare function closeDB(): Promise<void>;
149
149
  export declare function ensureMemoryScope(kind: string, externalId: string): Promise<string>;
150
- export declare function bindMemoryToScope(memoryId: string, scopeId: string, isPrimary?: boolean): Promise<void>;
151
- export declare function listScopesForMemory(memoryId: string): Promise<Array<{
152
- kind: string;
153
- id: string;
154
- }>>;
155
150
  /**
156
151
  * Insert embedding into vector search table
157
152
  *
@@ -301,12 +296,4 @@ export declare function fts5Search(query: string, limit?: number): Promise<{
301
296
  id: string;
302
297
  rank: number;
303
298
  }[]>;
304
- /**
305
- * Re-generate all embeddings using the currently configured model.
306
- * Call this after changing the embedding model in config.json.
307
- *
308
- * @param onProgress - Optional callback with (completed, total) counts
309
- * @returns Number of embeddings regenerated
310
- */
311
- export declare function reindexEmbeddings(onProgress?: (completed: number, total: number) => void): Promise<number>;
312
299
  //# sourceMappingURL=db-manager.d.ts.map
@@ -29,8 +29,6 @@ exports.getDB = getDB;
29
29
  exports.getAdapter = getAdapter;
30
30
  exports.closeDB = closeDB;
31
31
  exports.ensureMemoryScope = ensureMemoryScope;
32
- exports.bindMemoryToScope = bindMemoryToScope;
33
- exports.listScopesForMemory = listScopesForMemory;
34
32
  exports.insertEmbedding = insertEmbedding;
35
33
  exports.vectorSearch = vectorSearch;
36
34
  exports.insertDecisionWithEmbedding = insertDecisionWithEmbedding;
@@ -43,7 +41,6 @@ exports.getDbPath = getDbPath;
43
41
  exports.resetDBState = resetDBState;
44
42
  exports.isTestMode = isTestMode;
45
43
  exports.fts5Search = fts5Search;
46
- exports.reindexEmbeddings = reindexEmbeddings;
47
44
  const path_1 = __importDefault(require("path"));
48
45
  const os_1 = __importDefault(require("os"));
49
46
  const debug_logger_js_1 = require("./debug-logger.js");
@@ -262,27 +259,6 @@ async function ensureMemoryScope(kind, externalId) {
262
259
  .run(id, kind, externalId);
263
260
  return id;
264
261
  }
265
- async function bindMemoryToScope(memoryId, scopeId, isPrimary = false) {
266
- const adapter = getAdapter();
267
- adapter
268
- .prepare(`
269
- INSERT OR REPLACE INTO memory_scope_bindings (memory_id, scope_id, is_primary)
270
- VALUES (?, ?, ?)
271
- `)
272
- .run(memoryId, scopeId, isPrimary ? 1 : 0);
273
- }
274
- async function listScopesForMemory(memoryId) {
275
- const adapter = getAdapter();
276
- return adapter
277
- .prepare(`
278
- SELECT ms.kind, ms.external_id AS id
279
- FROM memory_scope_bindings msb
280
- JOIN memory_scopes ms ON ms.id = msb.scope_id
281
- WHERE msb.memory_id = ?
282
- ORDER BY msb.is_primary DESC, ms.created_at ASC
283
- `)
284
- .all(memoryId);
285
- }
286
262
  /**
287
263
  * Insert embedding into vector search table
288
264
  *
@@ -811,46 +787,4 @@ async function fts5Search(query, limit = 10) {
811
787
  `);
812
788
  return stmt.all(query, limit);
813
789
  }
814
- /**
815
- * Re-generate all embeddings using the currently configured model.
816
- * Call this after changing the embedding model in config.json.
817
- *
818
- * @param onProgress - Optional callback with (completed, total) counts
819
- * @returns Number of embeddings regenerated
820
- */
821
- async function reindexEmbeddings(onProgress) {
822
- const adapter = getAdapter();
823
- const { generateEnhancedEmbedding } = await import('./embeddings.js');
824
- const decisions = adapter
825
- .prepare('SELECT rowid, topic, decision, reasoning, outcome, confidence FROM decisions')
826
- .all();
827
- if (typeof adapter.insertEmbedding !== 'function') {
828
- throw new Error('Embedding insertion is not available on this adapter');
829
- }
830
- const total = decisions.length;
831
- let completed = 0;
832
- for (const row of decisions) {
833
- try {
834
- // e5 passage role (default) - stored vector
835
- const embedding = await generateEnhancedEmbedding({
836
- topic: row.topic,
837
- decision: row.decision,
838
- reasoning: row.reasoning || undefined,
839
- outcome: row.outcome || undefined,
840
- confidence: row.confidence ?? undefined,
841
- });
842
- adapter.insertEmbedding(row.rowid, embedding);
843
- completed++;
844
- if (onProgress && completed % 50 === 0) {
845
- onProgress(completed, total);
846
- }
847
- }
848
- catch (e) {
849
- (0, debug_logger_js_1.error)(`[db-manager] Failed to reindex rowid ${row.rowid}: ${e}`);
850
- }
851
- }
852
- onProgress?.(completed, total);
853
- (0, debug_logger_js_1.info)(`[db-manager] Reindexed ${completed}/${total} embeddings`);
854
- return completed;
855
- }
856
790
  //# sourceMappingURL=db-manager.js.map
@@ -140,13 +140,6 @@ export declare function ensureTokenBudget(text: string, maxTokens: number): stri
140
140
  * Heuristic: ~1 token per 4 characters
141
141
  */
142
142
  export declare function estimateTokens(text: string): number;
143
- /**
144
- * Format context in Claude-friendly Instant Answer format
145
- *
146
- * Story 014.7.10: Claude-Friendly Context Formatting
147
- * AC #1: Instant Answer format with trust components
148
- */
149
- export declare function formatInstantAnswer(decision: DecisionForFormat | null, options?: FormatOptions): string | null;
150
143
  /**
151
144
  * Extract quick answer from decision
152
145
  */
@@ -161,10 +154,6 @@ export declare function extractCodeExample(decision: DecisionForFormat): string
161
154
  * Story 014.7.10 AC #2: Trust Context display
162
155
  */
163
156
  export declare function formatTrustContext(trustCtx: TrustContext | null | undefined): string | null;
164
- /**
165
- * Format decision as curiosity-inducing teaser
166
- */
167
- export declare function formatTeaser(decision: DecisionForFormat | null): string | null;
168
157
  /**
169
158
  * Format mama.recall() results in readable format
170
159
  */
@@ -17,11 +17,9 @@ exports.formatContext = formatContext;
17
17
  exports.formatLegacyContext = formatLegacyContext;
18
18
  exports.ensureTokenBudget = ensureTokenBudget;
19
19
  exports.estimateTokens = estimateTokens;
20
- exports.formatInstantAnswer = formatInstantAnswer;
21
20
  exports.extractQuickAnswer = extractQuickAnswer;
22
21
  exports.extractCodeExample = extractCodeExample;
23
22
  exports.formatTrustContext = formatTrustContext;
24
- exports.formatTeaser = formatTeaser;
25
23
  exports.formatRecall = formatRecall;
26
24
  exports.formatList = formatList;
27
25
  const relevance_scorer_js_1 = require("./relevance-scorer.js");
@@ -362,42 +360,6 @@ function getOutcomeEmoji(outcome) {
362
360
  };
363
361
  return outcome ? emojiMap[outcome] || '' : '';
364
362
  }
365
- /**
366
- * Format context in Claude-friendly Instant Answer format
367
- *
368
- * Story 014.7.10: Claude-Friendly Context Formatting
369
- * AC #1: Instant Answer format with trust components
370
- */
371
- function formatInstantAnswer(decision, options = {}) {
372
- const { maxTokens = 500 } = options;
373
- if (!decision) {
374
- return null;
375
- }
376
- // Extract quick answer (first line of decision)
377
- const quickAnswer = extractQuickAnswer(decision);
378
- if (!quickAnswer) {
379
- return null;
380
- }
381
- // Extract code example (from reasoning)
382
- const codeExample = extractCodeExample(decision);
383
- // Format trust context
384
- const trustSection = formatTrustContext(typeof decision.trust_context === 'string'
385
- ? parseTrustContext(decision.trust_context)
386
- : decision.trust_context);
387
- // Build output
388
- let output = `⚡ INSTANT ANSWER\n\n${quickAnswer}`;
389
- if (codeExample) {
390
- output += `\n\n${codeExample}`;
391
- }
392
- if (trustSection) {
393
- output += `\n\n${trustSection}`;
394
- }
395
- // Token budget check
396
- if (estimateTokens(output) > maxTokens) {
397
- output = truncateToFit(output, maxTokens);
398
- }
399
- return output;
400
- }
401
363
  /**
402
364
  * Extract quick answer from decision
403
365
  */
@@ -500,50 +462,6 @@ function formatTrustContext(trustCtx) {
500
462
  lines.push('━'.repeat(40));
501
463
  return lines.join('\n');
502
464
  }
503
- /**
504
- * Truncate output to fit token budget
505
- */
506
- function truncateToFit(output, maxTokens) {
507
- const sections = output.split('\n\n');
508
- const quickAnswer = sections[0];
509
- let result = quickAnswer;
510
- let remainingTokens = maxTokens - estimateTokens(result);
511
- // Try to add code example
512
- const codeIndex = sections.findIndex((s) => s.startsWith('```'));
513
- if (codeIndex > 0) {
514
- const codeSection = sections[codeIndex];
515
- const codeTokens = estimateTokens(codeSection);
516
- if (codeTokens <= remainingTokens) {
517
- result += '\n\n' + codeSection;
518
- remainingTokens -= codeTokens;
519
- }
520
- }
521
- // Try to add trust section (trimmed if needed)
522
- const trustIndex = sections.findIndex((s) => s.startsWith('━'));
523
- if (trustIndex > 0 && remainingTokens > 50) {
524
- const trustSection = sections[trustIndex];
525
- const trustTokens = estimateTokens(trustSection);
526
- if (trustTokens <= remainingTokens) {
527
- result += '\n\n' + trustSection;
528
- }
529
- else {
530
- const trustLines = trustSection.split('\n');
531
- let trimmed = trustLines[0] + '\n' + trustLines[1] + '\n';
532
- for (let i = 2; i < trustLines.length - 1; i++) {
533
- const line = trustLines[i] + '\n';
534
- if (estimateTokens(trimmed + line) <= remainingTokens - 10) {
535
- trimmed += line;
536
- }
537
- else {
538
- break;
539
- }
540
- }
541
- trimmed += trustLines[trustLines.length - 1];
542
- result += '\n\n' + trimmed;
543
- }
544
- }
545
- return result;
546
- }
547
465
  /**
548
466
  * Format multiple decisions as Google-style search results
549
467
  */
@@ -578,46 +496,6 @@ function formatTeaserList(decisions, topN = 3) {
578
496
  }
579
497
  return output;
580
498
  }
581
- /**
582
- * Format decision as curiosity-inducing teaser
583
- */
584
- function formatTeaser(decision) {
585
- if (!decision) {
586
- return null;
587
- }
588
- const timeAgo = calculateDuration(getDecisionTimestamp(decision));
589
- // Extract preview (first 60 chars)
590
- const preview = decision.decision.length > 60 ? decision.decision.substring(0, 60) + '...' : decision.decision;
591
- // Extract files from trust_context or show generic
592
- let files = 'Multiple files';
593
- const trustCtx = typeof decision.trust_context === 'string'
594
- ? parseTrustContext(decision.trust_context)
595
- : decision.trust_context;
596
- if (trustCtx?.source?.file) {
597
- const fileStr = trustCtx.source.file;
598
- const fileList = fileStr.split(',').map((f) => f.trim());
599
- if (fileList.length === 1) {
600
- files = fileList[0];
601
- }
602
- else if (fileList.length === 2) {
603
- files = fileList.join(', ');
604
- }
605
- else {
606
- files = `${fileList[0]}, ${fileList[1]} (+${fileList.length - 2})`;
607
- }
608
- }
609
- const teaser = `
610
- 💡 MAMA has related info
611
-
612
- 📚 Topic: ${decision.topic}
613
- 📖 Preview: "${preview}"
614
- 📁 Files: ${files}
615
- ⏰ Updated: ${timeAgo}
616
-
617
- 🔍 Read more: mama.recall('${decision.topic}')
618
- `.trim();
619
- return teaser;
620
- }
621
499
  /**
622
500
  * Format mama.recall() results in readable format
623
501
  */
@@ -163,18 +163,6 @@ export declare function createEdgesFromReasoning(fromId: string, reasoning: stri
163
163
  created: number;
164
164
  failed: number;
165
165
  }>;
166
- /**
167
- * Get supersedes chain depth for a topic
168
- *
169
- * Story 2.2: Calculate how many times a topic has been superseded
170
- *
171
- * @param topic - Decision topic
172
- * @returns Chain depth and decision IDs
173
- */
174
- export declare function getSupersededChainDepth(topic: string): Promise<{
175
- depth: number;
176
- chain: string[];
177
- }>;
178
166
  /**
179
167
  * Learn Decision Function (Main API)
180
168
  *
@@ -25,7 +25,6 @@ exports.calculateCombinedConfidence = calculateCombinedConfidence;
25
25
  exports.detectRefinement = detectRefinement;
26
26
  exports.parseReasoningForRelationships = parseReasoningForRelationships;
27
27
  exports.createEdgesFromReasoning = createEdgesFromReasoning;
28
- exports.getSupersededChainDepth = getSupersededChainDepth;
29
28
  exports.learnDecision = learnDecision;
30
29
  exports.updateConfidence = updateConfidence;
31
30
  const debug_logger_js_1 = require("./debug-logger.js");
@@ -305,55 +304,6 @@ async function createEdgesFromReasoning(fromId, reasoning) {
305
304
  }
306
305
  return { created, failed };
307
306
  }
308
- /**
309
- * Get supersedes chain depth for a topic
310
- *
311
- * Story 2.2: Calculate how many times a topic has been superseded
312
- *
313
- * @param topic - Decision topic
314
- * @returns Chain depth and decision IDs
315
- */
316
- async function getSupersededChainDepth(topic) {
317
- const adapter = (0, memory_store_js_1.getAdapter)();
318
- const chain = [];
319
- try {
320
- // Start from the latest decision (superseded_by IS NULL)
321
- let stmt = adapter.prepare(`
322
- SELECT id, supersedes FROM decisions
323
- WHERE topic = ? AND superseded_by IS NULL
324
- ORDER BY created_at DESC
325
- LIMIT 1
326
- `);
327
- let current = stmt.get(topic);
328
- if (!current) {
329
- return { depth: 0, chain: [] };
330
- }
331
- chain.push(current.id);
332
- // Walk back through supersedes chain with cycle detection
333
- const visited = new Set([current.id]);
334
- const MAX_CHAIN_DEPTH = 1000; // Safety limit
335
- while (current && current.supersedes && chain.length < MAX_CHAIN_DEPTH) {
336
- // Cycle detection: stop if we've seen this ID before
337
- if (visited.has(current.supersedes)) {
338
- break;
339
- }
340
- stmt = adapter.prepare('SELECT id, supersedes FROM decisions WHERE id = ?');
341
- current = stmt.get(current.supersedes);
342
- if (current) {
343
- visited.add(current.id);
344
- chain.push(current.id);
345
- }
346
- }
347
- return {
348
- depth: chain.length - 1, // depth = number of supersedes edges
349
- chain: chain.reverse(), // oldest to newest
350
- };
351
- }
352
- catch (error) {
353
- const message = error instanceof Error ? error.message : String(error);
354
- throw new Error(`Failed to get supersedes chain: ${message}`);
355
- }
356
- }
357
307
  // ════════════════════════════════════════════════════════════════════════════
358
308
  // NOTE: Auto-link functions REMOVED in v1.2.0
359
309
  //
@@ -1,8 +1,7 @@
1
1
  import type { DatabaseAdapter } from '../db-manager.js';
2
- import type { ListVisibleTwinEdgesOptions, TwinEdgeRecord, TwinRef, TwinScopeRef, TwinVisibility } from './types.js';
2
+ import type { ListVisibleTwinEdgesOptions, TwinEdgeRecord, TwinRef, TwinVisibility } from './types.js';
3
3
  type TwinRefVisibilityAdapter = Pick<DatabaseAdapter, 'prepare'>;
4
4
  export declare function assertTwinRefsVisible(adapter: TwinRefVisibilityAdapter, refs: readonly TwinRef[], visibility?: TwinVisibility): void;
5
- export declare function assertTwinRefsVisibleToScopes(adapter: TwinRefVisibilityAdapter, refs: readonly TwinRef[], scopes?: TwinScopeRef[]): void;
6
5
  export declare function listVisibleTwinEdgesForRefs(adapter: TwinRefVisibilityAdapter, refs: readonly TwinRef[], options?: ListVisibleTwinEdgesOptions): TwinEdgeRecord[];
7
6
  export {};
8
7
  //# sourceMappingURL=ref-validation.d.ts.map
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.assertTwinRefsVisible = assertTwinRefsVisible;
4
- exports.assertTwinRefsVisibleToScopes = assertTwinRefsVisibleToScopes;
5
4
  exports.listVisibleTwinEdgesForRefs = listVisibleTwinEdgesForRefs;
6
5
  const store_js_1 = require("./store.js");
6
+ const channel_grant_js_1 = require("../context-compile/channel-grant.js");
7
7
  const TABLE_COLUMN_PRAGMAS = {
8
8
  decisions: 'PRAGMA table_info(decisions)',
9
9
  case_truth: 'PRAGMA table_info(case_truth)',
@@ -21,9 +21,6 @@ function hasScopes(scopes) {
21
21
  function hasProjectRefs(projectRefs) {
22
22
  return Array.isArray(projectRefs) && projectRefs.length > 0;
23
23
  }
24
- function visibilityFromScopes(scopes) {
25
- return { scopes: scopes ? [...scopes] : undefined };
26
- }
27
24
  function asOfMs(visibility) {
28
25
  return typeof visibility.asOfMs === 'number' ? visibility.asOfMs : null;
29
26
  }
@@ -177,6 +174,30 @@ function isCaseVisible(adapter, id, visibility) {
177
174
  }
178
175
  return false;
179
176
  }
177
+ /**
178
+ * The time bound, shared by both branches so a grant cannot skip it.
179
+ *
180
+ * DELIBERATELY STRICTER THAN THE READER, and a review flagged the difference as a
181
+ * divergence between two delegates of one rule. It is not. A row with no usable timestamp
182
+ * is refused here even when no bound is set, while the reader - whose clause is on
183
+ * `COALESCE(event_datetime, source_timestamp_ms)` - leaves such a row alone when there is
184
+ * no bound to apply.
185
+ *
186
+ * The direction is what makes it correct: this gate guards refs reached INDIRECTLY, through
187
+ * a seed the caller named or an edge the graph walked. Refusing what cannot be shown to sit
188
+ * inside any window keeps the indirect path no wider than the direct one, which is the
189
+ * invariant the whole design rests on. Relaxing it to match the reader would have made
190
+ * citation reach further than reading for exactly the rows whose position in time is
191
+ * unknown. `fails closed for raw endpoints with blank timestamps` states the intent.
192
+ */
193
+ function isRawWithinTime(row, visibility) {
194
+ const rawTs = row.event_datetime ?? row.source_timestamp_ms;
195
+ if (rawTs === null || rawTs === undefined || (typeof rawTs === 'string' && rawTs.trim() === '')) {
196
+ return false;
197
+ }
198
+ const eventTsMs = parseTimestamp(rawTs);
199
+ return eventTsMs !== null && eventTsMs >= 0 && isWithinVisibilityTime(eventTsMs, visibility);
200
+ }
180
201
  function isRawVisible(adapter, id, visibility) {
181
202
  const row = adapter
182
203
  .prepare('SELECT * FROM connector_event_index WHERE event_index_id = ? LIMIT 1')
@@ -188,6 +209,13 @@ function isRawVisible(adapter, id, visibility) {
188
209
  !visibility.connectors.includes(String(row.source_connector))) {
189
210
  return false;
190
211
  }
212
+ // The grant decides, and it decides the same way here as in the reader - one rule, taken
213
+ // from one place. The scope/project/tenant clauses below are the pre-grant rule and are
214
+ // skipped when a grant is present, exactly as the reader skips them, because applying
215
+ // both would make a cited ref satisfy a stricter test than a read one.
216
+ if (visibility.channels) {
217
+ return ((0, channel_grant_js_1.isChannelGranted)(String(row.source_connector), typeof row.channel === 'string' ? row.channel : null, visibility.channels) && isRawWithinTime(row, visibility));
218
+ }
191
219
  if (hasProjectRefs(visibility.projectRefs) &&
192
220
  !visibility.projectRefs.some((project) => project.id === row.project_id)) {
193
221
  return false;
@@ -197,12 +225,7 @@ function isRawVisible(adapter, id, visibility) {
197
225
  row.tenant_id !== visibility.tenantId) {
198
226
  return false;
199
227
  }
200
- const rawTs = row.event_datetime ?? row.source_timestamp_ms;
201
- if (rawTs === null || rawTs === undefined || (typeof rawTs === 'string' && rawTs.trim() === '')) {
202
- return false;
203
- }
204
- const eventTsMs = parseTimestamp(rawTs);
205
- if (eventTsMs === null || eventTsMs < 0 || !isWithinVisibilityTime(eventTsMs, visibility)) {
228
+ if (!isRawWithinTime(row, visibility)) {
206
229
  return false;
207
230
  }
208
231
  if (!hasScopes(visibility.scopes)) {
@@ -269,9 +292,6 @@ function assertTwinRefsVisible(adapter, refs, visibility = {}) {
269
292
  }
270
293
  }
271
294
  }
272
- function assertTwinRefsVisibleToScopes(adapter, refs, scopes) {
273
- assertTwinRefsVisible(adapter, refs, visibilityFromScopes(scopes));
274
- }
275
295
  function listVisibleTwinEdgesForRefs(adapter, refs, options = {}) {
276
296
  const edgeCache = new Map();
277
297
  const edgeTypes = normalizeEdgeTypes(options.edgeTypes);
@@ -1,10 +1,8 @@
1
1
  import type { DatabaseAdapter } from '../db-manager.js';
2
- import { type InsertTwinEdgeInput, type TwinEdgeRecord, type TwinRef } from './types.js';
2
+ import { type TwinEdgeRecord, type TwinRef } from './types.js';
3
3
  type TwinEdgeReadAdapter = Pick<DatabaseAdapter, 'prepare'>;
4
- type TwinEdgeAdapter = Pick<DatabaseAdapter, 'prepare' | 'transaction'>;
5
4
  export declare function mapTwinEdgeRow(row: Record<string, unknown>): TwinEdgeRecord;
6
5
  export declare function getTwinEdge(adapter: TwinEdgeReadAdapter, edgeId: string): TwinEdgeRecord | null;
7
6
  export declare function listTwinEdgesForRefs(adapter: TwinEdgeReadAdapter, refs: readonly TwinRef[]): TwinEdgeRecord[];
8
- export declare function insertTwinEdge(adapter: TwinEdgeAdapter, input: InsertTwinEdgeInput): TwinEdgeRecord;
9
7
  export {};
10
8
  //# sourceMappingURL=store.d.ts.map