@jungjaehoon/mama-core 1.4.2 → 1.6.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 (181) hide show
  1. package/README.md +48 -10
  2. package/db/migrations/026-create-canonical-entity-tables.sql +99 -0
  3. package/db/migrations/027-create-entity-review-support.sql +44 -0
  4. package/db/migrations/028-create-entity-audit-tables.sql +45 -0
  5. package/db/migrations/029-extend-entity-observations-observation-type.sql +60 -0
  6. package/db/migrations/030-case-first-memory-substrate.sql +901 -0
  7. package/db/migrations/031-wiki-page-index-provenance-columns.sql +2 -0
  8. package/db/migrations/032-add-memory-provenance-columns.sql +21 -0
  9. package/db/migrations/033-create-model-runs-and-tool-traces.sql +53 -0
  10. package/db/migrations/034-add-connector-event-scope-columns.sql +14 -0
  11. package/db/migrations/035-create-twin-edges.sql +63 -0
  12. package/db/migrations/036-create-agent-situation-packets.sql +61 -0
  13. package/dist/agent-graph/alias-write.d.ts +3 -0
  14. package/dist/agent-graph/alias-write.js +231 -0
  15. package/dist/agent-graph/entity-resolve.d.ts +3 -0
  16. package/dist/agent-graph/entity-resolve.js +275 -0
  17. package/dist/agent-graph/errors.d.ts +4 -0
  18. package/dist/agent-graph/errors.js +11 -0
  19. package/dist/agent-graph/graph-query.d.ts +5 -0
  20. package/dist/agent-graph/graph-query.js +359 -0
  21. package/dist/agent-graph/index.d.ts +6 -0
  22. package/dist/agent-graph/index.js +22 -0
  23. package/dist/agent-graph/types.d.ts +162 -0
  24. package/dist/agent-graph/types.js +3 -0
  25. package/dist/agent-situation/builder.d.ts +8 -0
  26. package/dist/agent-situation/builder.js +239 -0
  27. package/dist/agent-situation/cache-key.d.ts +4 -0
  28. package/dist/agent-situation/cache-key.js +64 -0
  29. package/dist/agent-situation/index.d.ts +7 -0
  30. package/dist/agent-situation/index.js +28 -0
  31. package/dist/agent-situation/packet-store.d.ts +27 -0
  32. package/dist/agent-situation/packet-store.js +228 -0
  33. package/dist/agent-situation/ranking-policy.d.ts +18 -0
  34. package/dist/agent-situation/ranking-policy.js +75 -0
  35. package/dist/agent-situation/source-readers.d.ts +52 -0
  36. package/dist/agent-situation/source-readers.js +300 -0
  37. package/dist/agent-situation/types.d.ts +195 -0
  38. package/dist/agent-situation/types.js +3 -0
  39. package/dist/canonicalize.d.ts +38 -0
  40. package/dist/canonicalize.js +106 -0
  41. package/dist/cases/case-links.d.ts +116 -0
  42. package/dist/cases/case-links.js +794 -0
  43. package/dist/cases/composition-overrides.d.ts +90 -0
  44. package/dist/cases/composition-overrides.js +724 -0
  45. package/dist/cases/corrections.d.ts +156 -0
  46. package/dist/cases/corrections.js +1045 -0
  47. package/dist/cases/freshness.d.ts +57 -0
  48. package/dist/cases/freshness.js +283 -0
  49. package/dist/cases/live-state.d.ts +48 -0
  50. package/dist/cases/live-state.js +256 -0
  51. package/dist/cases/membership-explain.d.ts +56 -0
  52. package/dist/cases/membership-explain.js +140 -0
  53. package/dist/cases/membership-matcher.d.ts +23 -0
  54. package/dist/cases/membership-matcher.js +193 -0
  55. package/dist/cases/merge-split.d.ts +61 -0
  56. package/dist/cases/merge-split.js +368 -0
  57. package/dist/cases/role-inference.d.ts +10 -0
  58. package/dist/cases/role-inference.js +85 -0
  59. package/dist/cases/search-rollup.d.ts +27 -0
  60. package/dist/cases/search-rollup.js +309 -0
  61. package/dist/cases/sqlite-transaction.d.ts +10 -0
  62. package/dist/cases/sqlite-transaction.js +62 -0
  63. package/dist/cases/store.d.ts +74 -0
  64. package/dist/cases/store.js +816 -0
  65. package/dist/cases/target-ref.d.ts +25 -0
  66. package/dist/cases/target-ref.js +22 -0
  67. package/dist/cases/timeline-range.d.ts +6 -0
  68. package/dist/cases/timeline-range.js +322 -0
  69. package/dist/cases/tombstone-sweeper.d.ts +13 -0
  70. package/dist/cases/tombstone-sweeper.js +128 -0
  71. package/dist/cases/types.d.ts +244 -0
  72. package/dist/cases/types.js +47 -0
  73. package/dist/cases/wiki-page-index.d.ts +50 -0
  74. package/dist/cases/wiki-page-index.js +350 -0
  75. package/dist/connectors/event-index.d.ts +45 -0
  76. package/dist/connectors/event-index.js +381 -0
  77. package/dist/connectors/raw-query.d.ts +18 -0
  78. package/dist/connectors/raw-query.js +305 -0
  79. package/dist/connectors/types.d.ts +109 -0
  80. package/dist/connectors/types.js +3 -0
  81. package/dist/db-adapter/node-sqlite-adapter.d.ts +6 -0
  82. package/dist/db-adapter/node-sqlite-adapter.js +131 -0
  83. package/dist/db-manager.d.ts +12 -1
  84. package/dist/db-manager.js +60 -5
  85. package/dist/decision-formatter.d.ts +2 -0
  86. package/dist/decision-formatter.js +11 -8
  87. package/dist/edges/ref-validation.d.ts +8 -0
  88. package/dist/edges/ref-validation.js +262 -0
  89. package/dist/edges/store.d.ts +10 -0
  90. package/dist/edges/store.js +343 -0
  91. package/dist/edges/types.d.ts +86 -0
  92. package/dist/edges/types.js +18 -0
  93. package/dist/embedding-server/mobile/daemon.d.ts +1 -0
  94. package/dist/embedding-server/mobile/daemon.js +11 -1
  95. package/dist/embeddings.js +13 -0
  96. package/dist/entities/audit-metrics.d.ts +73 -0
  97. package/dist/entities/audit-metrics.js +128 -0
  98. package/dist/entities/candidate-generator.d.ts +9 -0
  99. package/dist/entities/candidate-generator.js +436 -0
  100. package/dist/entities/entity-impact.d.ts +41 -0
  101. package/dist/entities/entity-impact.js +140 -0
  102. package/dist/entities/entity-linked-decision-counts.d.ts +2 -0
  103. package/dist/entities/entity-linked-decision-counts.js +29 -0
  104. package/dist/entities/entity-list.d.ts +23 -0
  105. package/dist/entities/entity-list.js +99 -0
  106. package/dist/entities/entity-orphan-list.d.ts +26 -0
  107. package/dist/entities/entity-orphan-list.js +91 -0
  108. package/dist/entities/entity-search.d.ts +21 -0
  109. package/dist/entities/entity-search.js +150 -0
  110. package/dist/entities/errors.d.ts +65 -0
  111. package/dist/entities/errors.js +124 -0
  112. package/dist/entities/exact-merge-backfill.d.ts +11 -0
  113. package/dist/entities/exact-merge-backfill.js +161 -0
  114. package/dist/entities/lineage-backfill.d.ts +12 -0
  115. package/dist/entities/lineage-backfill.js +101 -0
  116. package/dist/entities/lineage-store.d.ts +51 -0
  117. package/dist/entities/lineage-store.js +308 -0
  118. package/dist/entities/normalization.d.ts +14 -0
  119. package/dist/entities/normalization.js +63 -0
  120. package/dist/entities/policy-store.d.ts +17 -0
  121. package/dist/entities/policy-store.js +263 -0
  122. package/dist/entities/policy-types.d.ts +72 -0
  123. package/dist/entities/policy-types.js +59 -0
  124. package/dist/entities/projection.d.ts +8 -0
  125. package/dist/entities/projection.js +82 -0
  126. package/dist/entities/provenance-query.d.ts +33 -0
  127. package/dist/entities/provenance-query.js +167 -0
  128. package/dist/entities/read-identity.d.ts +31 -0
  129. package/dist/entities/read-identity.js +264 -0
  130. package/dist/entities/recall-bridge.d.ts +5 -0
  131. package/dist/entities/recall-bridge.js +171 -0
  132. package/dist/entities/resolution-engine.d.ts +15 -0
  133. package/dist/entities/resolution-engine.js +69 -0
  134. package/dist/entities/rollback-preview.d.ts +39 -0
  135. package/dist/entities/rollback-preview.js +235 -0
  136. package/dist/entities/source-locator.d.ts +3 -0
  137. package/dist/entities/source-locator.js +32 -0
  138. package/dist/entities/store.d.ts +75 -0
  139. package/dist/entities/store.js +465 -0
  140. package/dist/entities/types.d.ts +162 -0
  141. package/dist/entities/types.js +30 -0
  142. package/dist/index.d.ts +62 -3
  143. package/dist/index.js +116 -26
  144. package/dist/mama-api.d.ts +53 -35
  145. package/dist/mama-api.js +437 -78
  146. package/dist/memory/api.d.ts +26 -38
  147. package/dist/memory/api.js +772 -68
  148. package/dist/memory/event-store.d.ts +3 -0
  149. package/dist/memory/event-store.js +21 -2
  150. package/dist/memory/provenance-audit.d.ts +22 -0
  151. package/dist/memory/provenance-audit.js +112 -0
  152. package/dist/memory/provenance-query.d.ts +11 -0
  153. package/dist/memory/provenance-query.js +193 -0
  154. package/dist/memory/provenance.d.ts +29 -0
  155. package/dist/memory/provenance.js +136 -0
  156. package/dist/memory/scope-backfill.d.ts +17 -0
  157. package/dist/memory/scope-backfill.js +77 -0
  158. package/dist/memory/types.d.ts +97 -16
  159. package/dist/model-runs/store.d.ts +13 -0
  160. package/dist/model-runs/store.js +457 -0
  161. package/dist/model-runs/tool-trace-store.d.ts +4 -0
  162. package/dist/model-runs/tool-trace-store.js +114 -0
  163. package/dist/model-runs/types.d.ts +69 -0
  164. package/dist/model-runs/types.js +5 -0
  165. package/dist/runtime/trusted-provenance.d.ts +3 -0
  166. package/dist/runtime/trusted-provenance.js +6 -0
  167. package/dist/search/feedback-store.d.ts +59 -0
  168. package/dist/search/feedback-store.js +211 -0
  169. package/dist/search/question-type.d.ts +5 -0
  170. package/dist/search/question-type.js +41 -0
  171. package/dist/search/ranker-features.d.ts +23 -0
  172. package/dist/search/ranker-features.js +104 -0
  173. package/dist/search/ranker-rescore.d.ts +31 -0
  174. package/dist/search/ranker-rescore.js +226 -0
  175. package/dist/search/ranker-trainer.d.ts +94 -0
  176. package/dist/search/ranker-trainer.js +860 -0
  177. package/dist/search/search-quality.d.ts +36 -0
  178. package/dist/search/search-quality.js +29 -0
  179. package/dist/test-utils.d.ts +14 -0
  180. package/dist/test-utils.js +52 -0
  181. package/package.json +28 -3
@@ -0,0 +1,309 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rollUpSearchHits = rollUpSearchHits;
4
+ const store_js_1 = require("./store.js");
5
+ function sourceKey(sourceType, sourceId) {
6
+ return `${sourceType}:${sourceId}`;
7
+ }
8
+ function objectRecord(value) {
9
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
10
+ return value;
11
+ }
12
+ return null;
13
+ }
14
+ function stringOrNull(value) {
15
+ if (value === null || value === undefined) {
16
+ return null;
17
+ }
18
+ return String(value);
19
+ }
20
+ function searchHitDiagnostics(value) {
21
+ const object = objectRecord(value);
22
+ if (!object) {
23
+ return undefined;
24
+ }
25
+ if (typeof object.retrieval_source !== 'string' ||
26
+ !(object.vector_similarity === null || typeof object.vector_similarity === 'number') ||
27
+ typeof object.is_vector_only !== 'boolean' ||
28
+ typeof object.lexical_support !== 'boolean' ||
29
+ typeof object.entity_support !== 'boolean' ||
30
+ typeof object.scope_support !== 'boolean' ||
31
+ (object.graph_source !== 'primary' &&
32
+ object.graph_source !== 'expanded' &&
33
+ object.graph_source !== null) ||
34
+ !Array.isArray(object.confirmation_signals) ||
35
+ !Array.isArray(object.metadata_signals) ||
36
+ typeof object.candidate_threshold_used !== 'number') {
37
+ return undefined;
38
+ }
39
+ // Type assertion is safe: every required field of SearchHitDiagnostics has been
40
+ // validated above (including the array-typed signals consumed by diagnosticsRank).
41
+ return value;
42
+ }
43
+ function leafDiagnostics(leaf) {
44
+ const record = objectRecord(leaf.record);
45
+ return leaf.retrieval_diagnostics ?? searchHitDiagnostics(record?.retrieval_diagnostics);
46
+ }
47
+ function diagnosticsRank(diagnostics) {
48
+ let rank = 0;
49
+ rank += diagnostics.confirmation_signals.length * 100;
50
+ if (diagnostics.lexical_support) {
51
+ rank += 20;
52
+ }
53
+ if (diagnostics.entity_support) {
54
+ rank += 20;
55
+ }
56
+ if (diagnostics.graph_source === 'primary') {
57
+ rank += 10;
58
+ }
59
+ if (!diagnostics.is_vector_only) {
60
+ rank += 1;
61
+ }
62
+ return rank;
63
+ }
64
+ function betterDiagnostics(candidate, current) {
65
+ if (!current) {
66
+ return true;
67
+ }
68
+ return diagnosticsRank(candidate) > diagnosticsRank(current);
69
+ }
70
+ function diagnosticsRecord(diagnosticsByLeaf) {
71
+ if (diagnosticsByLeaf.size === 0) {
72
+ return undefined;
73
+ }
74
+ return Object.fromEntries(Array.from(diagnosticsByLeaf.entries()).sort(([left], [right]) => left.localeCompare(right)));
75
+ }
76
+ function attachLeafDiagnostics(group, leaf) {
77
+ const diagnostics = leafDiagnostics(leaf);
78
+ if (!diagnostics) {
79
+ return;
80
+ }
81
+ group.contributing_leaf_diagnostics.set(leaf.source_id, diagnostics);
82
+ if (betterDiagnostics(diagnostics, group.retrieval_diagnostics)) {
83
+ group.retrieval_diagnostics = diagnostics;
84
+ }
85
+ }
86
+ function placeholders(values) {
87
+ if (values.length === 0) {
88
+ throw new Error('Cannot build SQL IN clause for an empty value list.');
89
+ }
90
+ return values.map(() => '?').join(', ');
91
+ }
92
+ function withCaseId(record, caseId) {
93
+ const object = objectRecord(record);
94
+ if (!object) {
95
+ return { case_id: caseId };
96
+ }
97
+ return { ...object, case_id: caseId };
98
+ }
99
+ function loadCaseRecord(adapter, caseId) {
100
+ const row = adapter.prepare('SELECT * FROM case_truth WHERE case_id = ?').get(caseId);
101
+ return row ?? { case_id: caseId };
102
+ }
103
+ function canonicalCaseId(adapter, caseId) {
104
+ return (0, store_js_1.resolveCanonicalCaseChain)(adapter, caseId).terminal_case_id;
105
+ }
106
+ function listActiveMembershipCaseIds(adapter, sourceType, sourceId) {
107
+ const rows = adapter
108
+ .prepare(`
109
+ SELECT case_id
110
+ FROM case_memberships
111
+ WHERE source_type = ?
112
+ AND source_id = ?
113
+ AND status = 'active'
114
+ `)
115
+ .all(sourceType, sourceId);
116
+ return rows.map((row) => row.case_id);
117
+ }
118
+ function uniqueStrings(values) {
119
+ return Array.from(new Set(values.filter((value) => Boolean(value))));
120
+ }
121
+ function listConnectorEventMembershipCaseIds(adapter, leaf) {
122
+ const record = objectRecord(leaf.record) ?? {};
123
+ const sourceConnector = stringOrNull(record.source_connector);
124
+ const sourceLocator = stringOrNull(record.source_locator);
125
+ const artifactLocator = stringOrNull(record.artifact_locator);
126
+ const candidates = uniqueStrings([sourceLocator, artifactLocator]);
127
+ const caseIds = new Set();
128
+ if (sourceConnector && sourceLocator) {
129
+ const observationRows = adapter
130
+ .prepare(`
131
+ SELECT cm.case_id
132
+ FROM entity_observations eo
133
+ JOIN case_memberships cm
134
+ ON cm.source_type = 'observation'
135
+ AND cm.source_id = eo.id
136
+ AND cm.status = 'active'
137
+ WHERE eo.source_connector = ?
138
+ AND eo.source_locator = ?
139
+ `)
140
+ .all(sourceConnector, sourceLocator);
141
+ for (const row of observationRows) {
142
+ caseIds.add(row.case_id);
143
+ }
144
+ }
145
+ if (candidates.length > 0) {
146
+ const artifactRows = adapter
147
+ .prepare(`
148
+ SELECT case_id
149
+ FROM case_memberships
150
+ WHERE source_type = 'artifact'
151
+ AND status = 'active'
152
+ AND source_id IN (${placeholders(candidates)})
153
+ `)
154
+ .all(...candidates);
155
+ for (const row of artifactRows) {
156
+ caseIds.add(row.case_id);
157
+ }
158
+ }
159
+ return Array.from(caseIds).sort((left, right) => left.localeCompare(right));
160
+ }
161
+ function membershipCaseIdsForLeaf(adapter, leaf) {
162
+ if (leaf.source_type === 'connector_event') {
163
+ return listConnectorEventMembershipCaseIds(adapter, leaf);
164
+ }
165
+ return listActiveMembershipCaseIds(adapter, leaf.source_type, leaf.source_id);
166
+ }
167
+ function upsertOrphan(orphans, leaf) {
168
+ const key = sourceKey(leaf.source_type, leaf.source_id);
169
+ const existing = orphans.get(key);
170
+ if (existing && existing.score >= leaf.fused_rank_score) {
171
+ return;
172
+ }
173
+ const diagnostics = leafDiagnostics(leaf);
174
+ const contributingLeafDiagnostics = diagnostics ? { [leaf.source_id]: diagnostics } : undefined;
175
+ orphans.set(key, {
176
+ source_type: leaf.source_type === 'connector_event' ? 'standalone_connector_hit' : leaf.source_type,
177
+ source_id: leaf.source_id,
178
+ case_id: null,
179
+ score: leaf.fused_rank_score,
180
+ ...(diagnostics ? { retrieval_diagnostics: diagnostics } : {}),
181
+ ...(contributingLeafDiagnostics
182
+ ? { contributing_leaf_diagnostics: contributingLeafDiagnostics }
183
+ : {}),
184
+ record: leaf.record,
185
+ });
186
+ }
187
+ function upsertDirectCase(directCases, adapter, leaf, caseId) {
188
+ const survivorCaseId = canonicalCaseId(adapter, caseId);
189
+ const existing = directCases.get(survivorCaseId);
190
+ if (!existing || leaf.fused_rank_score > existing.score) {
191
+ directCases.set(survivorCaseId, {
192
+ case_id: survivorCaseId,
193
+ score: leaf.fused_rank_score,
194
+ max_leaf_score: leaf.fused_rank_score,
195
+ contributing_leaves: new Set([leaf.source_id]),
196
+ contributing_leaf_diagnostics: new Map(),
197
+ dedupe_keys: new Set([leaf.source_id]),
198
+ record: withCaseId(leaf.record, survivorCaseId),
199
+ });
200
+ }
201
+ const group = directCases.get(survivorCaseId);
202
+ if (group) {
203
+ attachLeafDiagnostics(group, leaf);
204
+ }
205
+ return survivorCaseId;
206
+ }
207
+ function upsertCaseGroup(groups, adapter, leaf, survivorCaseId) {
208
+ const dedupeKey = `${survivorCaseId}\0${leaf.source_type}\0${leaf.source_id}`;
209
+ let group = groups.get(survivorCaseId);
210
+ if (!group) {
211
+ group = {
212
+ case_id: survivorCaseId,
213
+ score: 0,
214
+ max_leaf_score: 0,
215
+ contributing_leaves: new Set(),
216
+ contributing_leaf_diagnostics: new Map(),
217
+ dedupe_keys: new Set(),
218
+ record: loadCaseRecord(adapter, survivorCaseId),
219
+ };
220
+ groups.set(survivorCaseId, group);
221
+ }
222
+ if (group.dedupe_keys.has(dedupeKey)) {
223
+ return;
224
+ }
225
+ group.dedupe_keys.add(dedupeKey);
226
+ group.contributing_leaves.add(leaf.source_id);
227
+ attachLeafDiagnostics(group, leaf);
228
+ group.score += leaf.fused_rank_score;
229
+ group.max_leaf_score = Math.max(group.max_leaf_score, leaf.fused_rank_score);
230
+ }
231
+ function groupToResult(group) {
232
+ const baseRecord = objectRecord(withCaseId(group.record, group.case_id)) ?? {
233
+ case_id: group.case_id,
234
+ };
235
+ const record = { ...baseRecord, max_leaf_score: group.max_leaf_score };
236
+ const contributingLeafDiagnostics = diagnosticsRecord(group.contributing_leaf_diagnostics);
237
+ return {
238
+ source_type: 'case',
239
+ source_id: group.case_id,
240
+ case_id: group.case_id,
241
+ score: group.score,
242
+ contributing_leaves: Array.from(group.contributing_leaves).sort((left, right) => left.localeCompare(right)),
243
+ ...(contributingLeafDiagnostics
244
+ ? { contributing_leaf_diagnostics: contributingLeafDiagnostics }
245
+ : {}),
246
+ ...(group.retrieval_diagnostics ? { retrieval_diagnostics: group.retrieval_diagnostics } : {}),
247
+ record,
248
+ };
249
+ }
250
+ function maxLeafScore(result) {
251
+ const object = objectRecord(result.record);
252
+ const value = object?.max_leaf_score;
253
+ return typeof value === 'number' && Number.isFinite(value) ? value : result.score;
254
+ }
255
+ function compareResults(left, right) {
256
+ if (right.score !== left.score) {
257
+ return right.score - left.score;
258
+ }
259
+ const leafDelta = maxLeafScore(right) - maxLeafScore(left);
260
+ if (leafDelta !== 0) {
261
+ return leafDelta;
262
+ }
263
+ if (left.source_type === 'case' && right.source_type !== 'case') {
264
+ return -1;
265
+ }
266
+ if (left.source_type !== 'case' && right.source_type === 'case') {
267
+ return 1;
268
+ }
269
+ return left.source_id.localeCompare(right.source_id);
270
+ }
271
+ function rollUpSearchHits(input) {
272
+ const directCases = new Map();
273
+ const directCaseIds = new Set();
274
+ const groupedCases = new Map();
275
+ const orphans = new Map();
276
+ for (const leaf of input.fusedHits) {
277
+ if (leaf.source_type === 'wiki_page' && leaf.page_type === 'case') {
278
+ if (leaf.case_id) {
279
+ const survivorCaseId = upsertDirectCase(directCases, input.adapter, leaf, leaf.case_id);
280
+ directCaseIds.add(survivorCaseId);
281
+ groupedCases.delete(survivorCaseId);
282
+ }
283
+ else {
284
+ upsertOrphan(orphans, leaf);
285
+ }
286
+ continue;
287
+ }
288
+ const membershipCaseIds = membershipCaseIdsForLeaf(input.adapter, leaf);
289
+ if (membershipCaseIds.length === 0) {
290
+ upsertOrphan(orphans, leaf);
291
+ continue;
292
+ }
293
+ for (const membershipCaseId of membershipCaseIds) {
294
+ const survivorCaseId = canonicalCaseId(input.adapter, membershipCaseId);
295
+ if (directCaseIds.has(survivorCaseId)) {
296
+ continue;
297
+ }
298
+ upsertCaseGroup(groupedCases, input.adapter, leaf, survivorCaseId);
299
+ }
300
+ }
301
+ return [
302
+ ...Array.from(directCases.values()).map(groupToResult),
303
+ ...Array.from(groupedCases.values())
304
+ .filter((group) => !directCaseIds.has(group.case_id))
305
+ .map(groupToResult),
306
+ ...Array.from(orphans.values()),
307
+ ].sort(compareResults);
308
+ }
309
+ //# sourceMappingURL=search-rollup.js.map
@@ -0,0 +1,10 @@
1
+ export interface ImmediateTransactionAdapter {
2
+ exec?: (sql: string) => void;
3
+ transaction: <T>(fn: () => T) => T;
4
+ }
5
+ export interface ImmediateTransactionOptions {
6
+ maxBusyRetries?: number;
7
+ busyRetryDelayMs?: number;
8
+ }
9
+ export declare function runImmediateTransaction<T>(adapter: ImmediateTransactionAdapter, fn: () => T, options?: ImmediateTransactionOptions): T;
10
+ //# sourceMappingURL=sqlite-transaction.d.ts.map
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runImmediateTransaction = runImmediateTransaction;
4
+ function isThenable(value) {
5
+ return (((typeof value === 'object' && value !== null) || typeof value === 'function') &&
6
+ typeof value.then === 'function');
7
+ }
8
+ function isBusyError(error) {
9
+ if (!(error instanceof Error)) {
10
+ return false;
11
+ }
12
+ const code = error.code;
13
+ return code === 'SQLITE_BUSY' || /SQLITE_BUSY|database is locked/i.test(error.message);
14
+ }
15
+ function requireSynchronousResult(result) {
16
+ if (isThenable(result)) {
17
+ throw new Error('runImmediateTransaction() callbacks must be synchronous');
18
+ }
19
+ return result;
20
+ }
21
+ function runImmediateTransaction(adapter, fn, options) {
22
+ const maxBusyRetries = options?.maxBusyRetries ?? 3;
23
+ const busyRetryDelayMs = Math.max(0, options?.busyRetryDelayMs ?? 20);
24
+ if (!adapter.exec) {
25
+ // Fake test adapters without exec can only provide deferred transaction
26
+ // semantics; production SQLite adapters should expose exec for BEGIN IMMEDIATE.
27
+ return adapter.transaction(() => requireSynchronousResult(fn()));
28
+ }
29
+ let attempt = 0;
30
+ for (;;) {
31
+ try {
32
+ adapter.exec('BEGIN IMMEDIATE');
33
+ try {
34
+ const result = requireSynchronousResult(fn());
35
+ adapter.exec('COMMIT');
36
+ return result;
37
+ }
38
+ catch (error) {
39
+ try {
40
+ adapter.exec('ROLLBACK');
41
+ }
42
+ catch {
43
+ // Preserve the original transaction failure when rollback also fails.
44
+ }
45
+ throw error;
46
+ }
47
+ }
48
+ catch (error) {
49
+ if (isBusyError(error) && attempt < maxBusyRetries) {
50
+ attempt += 1;
51
+ if (busyRetryDelayMs > 0) {
52
+ // Preserve the retry budget contract without blocking the event loop
53
+ // inside shared core code. Callers that need delayed retries should
54
+ // wrap this synchronous helper at a higher async boundary.
55
+ }
56
+ continue;
57
+ }
58
+ throw error;
59
+ }
60
+ }
61
+ }
62
+ //# sourceMappingURL=sqlite-transaction.js.map
@@ -0,0 +1,74 @@
1
+ import { type DatabaseAdapter } from '../db-manager.js';
2
+ import type { CanonicalCaseResolution, CaseAssembly, CaseAssemblyMembership, CaseBlocker, CaseConfidence, CaseCorrectionRecord, CaseMembershipRecord, CaseMembershipSourceType, CasePrimaryActor, CaseProposalKind, CaseProposalQueueRecord, CaseTruthRecord } from './types.js';
3
+ type CaseStoreAdapter = Pick<DatabaseAdapter, 'prepare' | 'transaction'>;
4
+ export interface UpsertCaseTruthSlowFieldsInput {
5
+ case_id: string;
6
+ current_wiki_path?: string | null;
7
+ title: string;
8
+ status_reason?: string | null;
9
+ primary_actors?: CasePrimaryActor[] | string | null;
10
+ blockers?: CaseBlocker[] | string | null;
11
+ confidence?: CaseConfidence | null;
12
+ scope_refs?: Array<{
13
+ kind: string;
14
+ id: string;
15
+ }> | string | null;
16
+ wiki_path_history?: Array<{
17
+ path: string;
18
+ valid_from: string;
19
+ valid_to: string | null;
20
+ }> | string | null;
21
+ compiled_at: string;
22
+ }
23
+ export interface UpsertExplicitCaseMembershipsInput {
24
+ case_id: string;
25
+ rows: Array<{
26
+ source_type: CaseMembershipSourceType;
27
+ source_id: string;
28
+ role?: string | null;
29
+ confidence?: number | null;
30
+ reason?: string | null;
31
+ }>;
32
+ }
33
+ export interface EnqueueCaseProposalInput {
34
+ project: string;
35
+ proposal_kind: CaseProposalKind;
36
+ proposed_payload: string;
37
+ stable_fingerprint_input: unknown;
38
+ conflicting_case_id?: string | null;
39
+ }
40
+ export interface AssembleCaseOptions {
41
+ since?: string;
42
+ limit?: number;
43
+ }
44
+ export declare function resolveCanonicalCaseChain(adapter: CaseStoreAdapter, caseId: string, maxDepth?: number): CanonicalCaseResolution;
45
+ export declare function expandCaseChainForAssembly(adapter: Pick<DatabaseAdapter, 'prepare'>, terminalCaseId: string, resolvedChain: string[]): string[];
46
+ export declare function upsertCaseTruthSlowFields(input: UpsertCaseTruthSlowFieldsInput): CaseTruthRecord;
47
+ export declare function upsertCaseTruthSlowFields(adapter: CaseStoreAdapter, input: UpsertCaseTruthSlowFieldsInput): CaseTruthRecord;
48
+ export declare function upsertExplicitCaseMemberships(input: UpsertExplicitCaseMembershipsInput): CaseMembershipRecord[];
49
+ export declare function upsertExplicitCaseMemberships(adapter: CaseStoreAdapter, input: UpsertExplicitCaseMembershipsInput): CaseMembershipRecord[];
50
+ export declare function listActiveMembershipsForCaseChain(adapter: CaseStoreAdapter, caseIds: string[], limit?: number): CaseAssemblyMembership[];
51
+ export interface ListActiveCorrectionsForCaseResult {
52
+ terminal_case_id: string;
53
+ resolved_via_case_id: string | null;
54
+ chain: string[];
55
+ corrections: CaseCorrectionRecord[];
56
+ }
57
+ /**
58
+ * Resolves the canonical merge chain for caseId (both up to terminal and
59
+ * down to merged losers) and returns every active, unreverted correction
60
+ * across the entire chain. HITL surfaces must use this rather than a
61
+ * direct `WHERE case_id = ?` query so post-merge corrections on loser
62
+ * cases remain visible and actionable from the survivor's view.
63
+ */
64
+ export declare function listActiveCorrectionsForCase(adapter: CaseStoreAdapter, caseId: string): ListActiveCorrectionsForCaseResult;
65
+ export declare function listActiveCorrectionsForCaseChain(adapter: CaseStoreAdapter, caseIds: string[]): CaseCorrectionRecord[];
66
+ export declare function enqueueCaseProposal(adapter: CaseStoreAdapter, input: EnqueueCaseProposalInput): {
67
+ proposal_id: string;
68
+ inserted: boolean;
69
+ };
70
+ export declare function listUnresolvedCaseProposals(adapter: CaseStoreAdapter, project: string): CaseProposalQueueRecord[];
71
+ export declare function assembleCase(caseId: string, options?: AssembleCaseOptions): CaseAssembly;
72
+ export declare function assembleCase(adapter: CaseStoreAdapter, caseId: string, options?: AssembleCaseOptions): CaseAssembly;
73
+ export {};
74
+ //# sourceMappingURL=store.d.ts.map