@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.
- package/README.md +48 -10
- package/db/migrations/026-create-canonical-entity-tables.sql +99 -0
- package/db/migrations/027-create-entity-review-support.sql +44 -0
- package/db/migrations/028-create-entity-audit-tables.sql +45 -0
- package/db/migrations/029-extend-entity-observations-observation-type.sql +60 -0
- package/db/migrations/030-case-first-memory-substrate.sql +901 -0
- package/db/migrations/031-wiki-page-index-provenance-columns.sql +2 -0
- package/db/migrations/032-add-memory-provenance-columns.sql +21 -0
- package/db/migrations/033-create-model-runs-and-tool-traces.sql +53 -0
- package/db/migrations/034-add-connector-event-scope-columns.sql +14 -0
- package/db/migrations/035-create-twin-edges.sql +63 -0
- package/db/migrations/036-create-agent-situation-packets.sql +61 -0
- package/dist/agent-graph/alias-write.d.ts +3 -0
- package/dist/agent-graph/alias-write.js +231 -0
- package/dist/agent-graph/entity-resolve.d.ts +3 -0
- package/dist/agent-graph/entity-resolve.js +275 -0
- package/dist/agent-graph/errors.d.ts +4 -0
- package/dist/agent-graph/errors.js +11 -0
- package/dist/agent-graph/graph-query.d.ts +5 -0
- package/dist/agent-graph/graph-query.js +359 -0
- package/dist/agent-graph/index.d.ts +6 -0
- package/dist/agent-graph/index.js +22 -0
- package/dist/agent-graph/types.d.ts +162 -0
- package/dist/agent-graph/types.js +3 -0
- package/dist/agent-situation/builder.d.ts +8 -0
- package/dist/agent-situation/builder.js +239 -0
- package/dist/agent-situation/cache-key.d.ts +4 -0
- package/dist/agent-situation/cache-key.js +64 -0
- package/dist/agent-situation/index.d.ts +7 -0
- package/dist/agent-situation/index.js +28 -0
- package/dist/agent-situation/packet-store.d.ts +27 -0
- package/dist/agent-situation/packet-store.js +228 -0
- package/dist/agent-situation/ranking-policy.d.ts +18 -0
- package/dist/agent-situation/ranking-policy.js +75 -0
- package/dist/agent-situation/source-readers.d.ts +52 -0
- package/dist/agent-situation/source-readers.js +300 -0
- package/dist/agent-situation/types.d.ts +195 -0
- package/dist/agent-situation/types.js +3 -0
- package/dist/canonicalize.d.ts +38 -0
- package/dist/canonicalize.js +106 -0
- package/dist/cases/case-links.d.ts +116 -0
- package/dist/cases/case-links.js +794 -0
- package/dist/cases/composition-overrides.d.ts +90 -0
- package/dist/cases/composition-overrides.js +724 -0
- package/dist/cases/corrections.d.ts +156 -0
- package/dist/cases/corrections.js +1045 -0
- package/dist/cases/freshness.d.ts +57 -0
- package/dist/cases/freshness.js +283 -0
- package/dist/cases/live-state.d.ts +48 -0
- package/dist/cases/live-state.js +256 -0
- package/dist/cases/membership-explain.d.ts +56 -0
- package/dist/cases/membership-explain.js +140 -0
- package/dist/cases/membership-matcher.d.ts +23 -0
- package/dist/cases/membership-matcher.js +193 -0
- package/dist/cases/merge-split.d.ts +61 -0
- package/dist/cases/merge-split.js +368 -0
- package/dist/cases/role-inference.d.ts +10 -0
- package/dist/cases/role-inference.js +85 -0
- package/dist/cases/search-rollup.d.ts +27 -0
- package/dist/cases/search-rollup.js +309 -0
- package/dist/cases/sqlite-transaction.d.ts +10 -0
- package/dist/cases/sqlite-transaction.js +62 -0
- package/dist/cases/store.d.ts +74 -0
- package/dist/cases/store.js +816 -0
- package/dist/cases/target-ref.d.ts +25 -0
- package/dist/cases/target-ref.js +22 -0
- package/dist/cases/timeline-range.d.ts +6 -0
- package/dist/cases/timeline-range.js +322 -0
- package/dist/cases/tombstone-sweeper.d.ts +13 -0
- package/dist/cases/tombstone-sweeper.js +128 -0
- package/dist/cases/types.d.ts +244 -0
- package/dist/cases/types.js +47 -0
- package/dist/cases/wiki-page-index.d.ts +50 -0
- package/dist/cases/wiki-page-index.js +350 -0
- package/dist/connectors/event-index.d.ts +45 -0
- package/dist/connectors/event-index.js +381 -0
- package/dist/connectors/raw-query.d.ts +18 -0
- package/dist/connectors/raw-query.js +305 -0
- package/dist/connectors/types.d.ts +109 -0
- package/dist/connectors/types.js +3 -0
- package/dist/db-adapter/node-sqlite-adapter.d.ts +6 -0
- package/dist/db-adapter/node-sqlite-adapter.js +131 -0
- package/dist/db-manager.d.ts +12 -1
- package/dist/db-manager.js +60 -5
- package/dist/decision-formatter.d.ts +2 -0
- package/dist/decision-formatter.js +11 -8
- package/dist/edges/ref-validation.d.ts +8 -0
- package/dist/edges/ref-validation.js +262 -0
- package/dist/edges/store.d.ts +10 -0
- package/dist/edges/store.js +343 -0
- package/dist/edges/types.d.ts +86 -0
- package/dist/edges/types.js +18 -0
- package/dist/embedding-server/mobile/daemon.d.ts +1 -0
- package/dist/embedding-server/mobile/daemon.js +11 -1
- package/dist/embeddings.js +13 -0
- package/dist/entities/audit-metrics.d.ts +73 -0
- package/dist/entities/audit-metrics.js +128 -0
- package/dist/entities/candidate-generator.d.ts +9 -0
- package/dist/entities/candidate-generator.js +436 -0
- package/dist/entities/entity-impact.d.ts +41 -0
- package/dist/entities/entity-impact.js +140 -0
- package/dist/entities/entity-linked-decision-counts.d.ts +2 -0
- package/dist/entities/entity-linked-decision-counts.js +29 -0
- package/dist/entities/entity-list.d.ts +23 -0
- package/dist/entities/entity-list.js +99 -0
- package/dist/entities/entity-orphan-list.d.ts +26 -0
- package/dist/entities/entity-orphan-list.js +91 -0
- package/dist/entities/entity-search.d.ts +21 -0
- package/dist/entities/entity-search.js +150 -0
- package/dist/entities/errors.d.ts +65 -0
- package/dist/entities/errors.js +124 -0
- package/dist/entities/exact-merge-backfill.d.ts +11 -0
- package/dist/entities/exact-merge-backfill.js +161 -0
- package/dist/entities/lineage-backfill.d.ts +12 -0
- package/dist/entities/lineage-backfill.js +101 -0
- package/dist/entities/lineage-store.d.ts +51 -0
- package/dist/entities/lineage-store.js +308 -0
- package/dist/entities/normalization.d.ts +14 -0
- package/dist/entities/normalization.js +63 -0
- package/dist/entities/policy-store.d.ts +17 -0
- package/dist/entities/policy-store.js +263 -0
- package/dist/entities/policy-types.d.ts +72 -0
- package/dist/entities/policy-types.js +59 -0
- package/dist/entities/projection.d.ts +8 -0
- package/dist/entities/projection.js +82 -0
- package/dist/entities/provenance-query.d.ts +33 -0
- package/dist/entities/provenance-query.js +167 -0
- package/dist/entities/read-identity.d.ts +31 -0
- package/dist/entities/read-identity.js +264 -0
- package/dist/entities/recall-bridge.d.ts +5 -0
- package/dist/entities/recall-bridge.js +171 -0
- package/dist/entities/resolution-engine.d.ts +15 -0
- package/dist/entities/resolution-engine.js +69 -0
- package/dist/entities/rollback-preview.d.ts +39 -0
- package/dist/entities/rollback-preview.js +235 -0
- package/dist/entities/source-locator.d.ts +3 -0
- package/dist/entities/source-locator.js +32 -0
- package/dist/entities/store.d.ts +75 -0
- package/dist/entities/store.js +465 -0
- package/dist/entities/types.d.ts +162 -0
- package/dist/entities/types.js +30 -0
- package/dist/index.d.ts +62 -3
- package/dist/index.js +116 -26
- package/dist/mama-api.d.ts +53 -35
- package/dist/mama-api.js +437 -78
- package/dist/memory/api.d.ts +26 -38
- package/dist/memory/api.js +772 -68
- package/dist/memory/event-store.d.ts +3 -0
- package/dist/memory/event-store.js +21 -2
- package/dist/memory/provenance-audit.d.ts +22 -0
- package/dist/memory/provenance-audit.js +112 -0
- package/dist/memory/provenance-query.d.ts +11 -0
- package/dist/memory/provenance-query.js +193 -0
- package/dist/memory/provenance.d.ts +29 -0
- package/dist/memory/provenance.js +136 -0
- package/dist/memory/scope-backfill.d.ts +17 -0
- package/dist/memory/scope-backfill.js +77 -0
- package/dist/memory/types.d.ts +97 -16
- package/dist/model-runs/store.d.ts +13 -0
- package/dist/model-runs/store.js +457 -0
- package/dist/model-runs/tool-trace-store.d.ts +4 -0
- package/dist/model-runs/tool-trace-store.js +114 -0
- package/dist/model-runs/types.d.ts +69 -0
- package/dist/model-runs/types.js +5 -0
- package/dist/runtime/trusted-provenance.d.ts +3 -0
- package/dist/runtime/trusted-provenance.js +6 -0
- package/dist/search/feedback-store.d.ts +59 -0
- package/dist/search/feedback-store.js +211 -0
- package/dist/search/question-type.d.ts +5 -0
- package/dist/search/question-type.js +41 -0
- package/dist/search/ranker-features.d.ts +23 -0
- package/dist/search/ranker-features.js +104 -0
- package/dist/search/ranker-rescore.d.ts +31 -0
- package/dist/search/ranker-rescore.js +226 -0
- package/dist/search/ranker-trainer.d.ts +94 -0
- package/dist/search/ranker-trainer.js +860 -0
- package/dist/search/search-quality.d.ts +36 -0
- package/dist/search/search-quality.js +29 -0
- package/dist/test-utils.d.ts +14 -0
- package/dist/test-utils.js +52 -0
- 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
|