@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,368 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mergeCases = mergeCases;
|
|
4
|
+
exports.splitCase = splitCase;
|
|
5
|
+
const node_crypto_1 = require("node:crypto");
|
|
6
|
+
const canonicalize_js_1 = require("../canonicalize.js");
|
|
7
|
+
const corrections_js_1 = require("./corrections.js");
|
|
8
|
+
const sqlite_transaction_js_1 = require("./sqlite-transaction.js");
|
|
9
|
+
const TERMINAL_CASE_STATUSES = new Set(['merged', 'archived', 'split']);
|
|
10
|
+
const CHILD_UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
11
|
+
function normalizeNow(value) {
|
|
12
|
+
return value ?? new Date().toISOString();
|
|
13
|
+
}
|
|
14
|
+
function createdAtMs(now) {
|
|
15
|
+
const parsed = Date.parse(now);
|
|
16
|
+
return Number.isFinite(parsed) ? parsed : Date.now();
|
|
17
|
+
}
|
|
18
|
+
function nonEmpty(value) {
|
|
19
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
20
|
+
}
|
|
21
|
+
function confirmed(input) {
|
|
22
|
+
return (input.confirmed === true && nonEmpty(input.confirmed_by) && nonEmpty(input.confirmation_summary));
|
|
23
|
+
}
|
|
24
|
+
function readCase(adapter, caseId) {
|
|
25
|
+
const row = adapter.prepare('SELECT * FROM case_truth WHERE case_id = ?').get(caseId);
|
|
26
|
+
return row ?? null;
|
|
27
|
+
}
|
|
28
|
+
function resolveCanonicalChain(adapter, caseId) {
|
|
29
|
+
const visited = new Set();
|
|
30
|
+
const chain = [];
|
|
31
|
+
let current = caseId;
|
|
32
|
+
for (;;) {
|
|
33
|
+
const depth = chain.length + 1;
|
|
34
|
+
const cycleChain = [...chain, current];
|
|
35
|
+
if (visited.has(current) || depth > 64) {
|
|
36
|
+
return {
|
|
37
|
+
kind: 'cycle',
|
|
38
|
+
message: `Case merge chain cycle detected at depth ${depth} starting from case_id=${caseId}. Chain=${cycleChain.join(' -> ')}.`,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
visited.add(current);
|
|
42
|
+
chain.push(current);
|
|
43
|
+
const row = readCase(adapter, current);
|
|
44
|
+
if (!row) {
|
|
45
|
+
return { kind: 'precompile_gap', case_id: current };
|
|
46
|
+
}
|
|
47
|
+
if (!row.canonical_case_id) {
|
|
48
|
+
return { kind: 'resolved', terminal_case_id: current, chain };
|
|
49
|
+
}
|
|
50
|
+
current = row.canonical_case_id;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function insertMemoryEvent(input) {
|
|
54
|
+
const eventId = `me_${(0, node_crypto_1.randomUUID)()}`;
|
|
55
|
+
input.adapter
|
|
56
|
+
.prepare(`
|
|
57
|
+
INSERT INTO memory_events (
|
|
58
|
+
event_id, event_type, actor, source_turn_id, memory_id, topic,
|
|
59
|
+
scope_refs, evidence_refs, reason, created_at
|
|
60
|
+
)
|
|
61
|
+
VALUES (?, ?, 'user', NULL, NULL, ?, ?, ?, ?, ?)
|
|
62
|
+
`)
|
|
63
|
+
.run(eventId, input.event_type, input.topic, (0, canonicalize_js_1.canonicalizeJSON)(input.scope_refs), (0, canonicalize_js_1.canonicalizeJSON)(input.evidence_refs), (0, canonicalize_js_1.canonicalizeJSON)(input.reason), createdAtMs(input.now));
|
|
64
|
+
return eventId;
|
|
65
|
+
}
|
|
66
|
+
function hasCallerChildCaseIds(children) {
|
|
67
|
+
return children.some((child) => Object.prototype.hasOwnProperty.call(child, 'case_id'));
|
|
68
|
+
}
|
|
69
|
+
function trustedChildIdsAreValid(ids) {
|
|
70
|
+
return ids.every((id) => CHILD_UUID_PATTERN.test(id) && !id.startsWith('case_'));
|
|
71
|
+
}
|
|
72
|
+
function childIdsAreUnique(ids) {
|
|
73
|
+
return new Set(ids).size === ids.length;
|
|
74
|
+
}
|
|
75
|
+
function readParentMembership(adapter, parentCaseId, sourceType, sourceId) {
|
|
76
|
+
const row = adapter
|
|
77
|
+
.prepare(`
|
|
78
|
+
SELECT role, confidence, reason, status
|
|
79
|
+
FROM case_memberships
|
|
80
|
+
WHERE case_id = ?
|
|
81
|
+
AND source_type = ?
|
|
82
|
+
AND source_id = ?
|
|
83
|
+
`)
|
|
84
|
+
.get(parentCaseId, sourceType, sourceId);
|
|
85
|
+
return row ?? null;
|
|
86
|
+
}
|
|
87
|
+
function upsertChildMembership(input) {
|
|
88
|
+
input.adapter
|
|
89
|
+
.prepare(`
|
|
90
|
+
INSERT INTO case_memberships (
|
|
91
|
+
case_id, source_type, source_id, role, confidence, reason, status, added_by,
|
|
92
|
+
added_at, updated_at, user_locked
|
|
93
|
+
)
|
|
94
|
+
VALUES (?, ?, ?, ?, ?, ?, 'active', 'user-correction', ?, ?, 1)
|
|
95
|
+
ON CONFLICT(case_id, source_type, source_id) DO UPDATE SET
|
|
96
|
+
role = excluded.role,
|
|
97
|
+
confidence = excluded.confidence,
|
|
98
|
+
reason = excluded.reason,
|
|
99
|
+
status = 'active',
|
|
100
|
+
added_by = 'user-correction',
|
|
101
|
+
updated_at = excluded.updated_at,
|
|
102
|
+
user_locked = 1
|
|
103
|
+
`)
|
|
104
|
+
.run(input.child_case_id, input.source_type, input.source_id, input.parent_membership?.role ?? null, input.parent_membership?.confidence ?? null, input.parent_membership?.reason ?? 'User assigned source during case split.', input.now, input.now);
|
|
105
|
+
}
|
|
106
|
+
function removeParentMembership(input) {
|
|
107
|
+
input.adapter
|
|
108
|
+
.prepare(`
|
|
109
|
+
UPDATE case_memberships
|
|
110
|
+
SET status = 'removed',
|
|
111
|
+
user_locked = 1,
|
|
112
|
+
updated_at = ?
|
|
113
|
+
WHERE case_id = ?
|
|
114
|
+
AND source_type = ?
|
|
115
|
+
AND source_id = ?
|
|
116
|
+
`)
|
|
117
|
+
.run(input.now, input.parent_case_id, input.source_type, input.source_id);
|
|
118
|
+
}
|
|
119
|
+
function mergeCases(adapter, input) {
|
|
120
|
+
const tx = adapter;
|
|
121
|
+
if (!confirmed(input)) {
|
|
122
|
+
return {
|
|
123
|
+
kind: 'rejected',
|
|
124
|
+
code: 'case.confirmation_required',
|
|
125
|
+
message: 'confirmed=true, confirmed_by, and confirmation_summary are required.',
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
return (0, sqlite_transaction_js_1.runImmediateTransaction)(tx, () => {
|
|
129
|
+
const now = normalizeNow(input.now);
|
|
130
|
+
if (input.loser_case_id === input.survivor_case_id) {
|
|
131
|
+
return {
|
|
132
|
+
kind: 'rejected',
|
|
133
|
+
code: 'case.merge_self',
|
|
134
|
+
message: 'loser_case_id and survivor_case_id must be different.',
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
const loser = readCase(tx, input.loser_case_id);
|
|
138
|
+
if (!loser) {
|
|
139
|
+
return { kind: 'precompile_gap', code: 'case.precompile_gap', case_id: input.loser_case_id };
|
|
140
|
+
}
|
|
141
|
+
const survivor = readCase(tx, input.survivor_case_id);
|
|
142
|
+
if (!survivor) {
|
|
143
|
+
return {
|
|
144
|
+
kind: 'precompile_gap',
|
|
145
|
+
code: 'case.precompile_gap',
|
|
146
|
+
case_id: input.survivor_case_id,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
const loserResolution = resolveCanonicalChain(tx, input.loser_case_id);
|
|
150
|
+
if (loserResolution.kind === 'cycle') {
|
|
151
|
+
return { kind: 'rejected', code: 'case.merge_chain_cycle', message: loserResolution.message };
|
|
152
|
+
}
|
|
153
|
+
if (loserResolution.kind === 'precompile_gap') {
|
|
154
|
+
return {
|
|
155
|
+
kind: 'precompile_gap',
|
|
156
|
+
code: 'case.precompile_gap',
|
|
157
|
+
case_id: loserResolution.case_id,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
const survivorResolution = resolveCanonicalChain(tx, input.survivor_case_id);
|
|
161
|
+
if (survivorResolution.kind === 'cycle') {
|
|
162
|
+
return {
|
|
163
|
+
kind: 'rejected',
|
|
164
|
+
code: 'case.merge_chain_cycle',
|
|
165
|
+
message: survivorResolution.message,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
if (survivorResolution.kind === 'precompile_gap') {
|
|
169
|
+
return {
|
|
170
|
+
kind: 'precompile_gap',
|
|
171
|
+
code: 'case.precompile_gap',
|
|
172
|
+
case_id: survivorResolution.case_id,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
if (loserResolution.terminal_case_id === survivorResolution.terminal_case_id) {
|
|
176
|
+
return {
|
|
177
|
+
kind: 'rejected',
|
|
178
|
+
code: 'case.merge_self',
|
|
179
|
+
message: 'loser and survivor resolve to the same canonical case.',
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
const canonicalLoserCaseId = loserResolution.terminal_case_id;
|
|
183
|
+
const canonicalSurvivorCaseId = survivorResolution.terminal_case_id;
|
|
184
|
+
if (TERMINAL_CASE_STATUSES.has(loser.status) || TERMINAL_CASE_STATUSES.has(survivor.status)) {
|
|
185
|
+
return {
|
|
186
|
+
kind: 'rejected',
|
|
187
|
+
code: 'case.terminal_status',
|
|
188
|
+
message: 'Merged, archived, and split cases cannot be merge endpoints.',
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
(0, corrections_js_1.insertCaseCorrectionLock)(tx, {
|
|
192
|
+
case_id: canonicalLoserCaseId,
|
|
193
|
+
target_kind: 'case_field',
|
|
194
|
+
target_ref: { kind: 'case_field', field: 'canonical_case_id' },
|
|
195
|
+
field_name: null,
|
|
196
|
+
old_value_json: (0, canonicalize_js_1.canonicalizeJSON)({
|
|
197
|
+
status: loser.status,
|
|
198
|
+
canonical_case_id: loser.canonical_case_id,
|
|
199
|
+
}),
|
|
200
|
+
new_value_json: (0, canonicalize_js_1.canonicalizeJSON)({
|
|
201
|
+
status: 'merged',
|
|
202
|
+
canonical_case_id: canonicalSurvivorCaseId,
|
|
203
|
+
}),
|
|
204
|
+
reason: input.reason,
|
|
205
|
+
applied_by: input.confirmed_by,
|
|
206
|
+
applied_at: now,
|
|
207
|
+
is_lock_active: 0,
|
|
208
|
+
});
|
|
209
|
+
tx.prepare(`
|
|
210
|
+
UPDATE case_truth
|
|
211
|
+
SET status = 'merged',
|
|
212
|
+
canonical_case_id = ?,
|
|
213
|
+
updated_at = ?
|
|
214
|
+
WHERE case_id = ?
|
|
215
|
+
`).run(canonicalSurvivorCaseId, now, canonicalLoserCaseId);
|
|
216
|
+
const auditEventId = insertMemoryEvent({
|
|
217
|
+
adapter: tx,
|
|
218
|
+
event_type: 'case.merged',
|
|
219
|
+
topic: `case:${canonicalSurvivorCaseId}`,
|
|
220
|
+
scope_refs: [
|
|
221
|
+
{ type: 'case', id: canonicalSurvivorCaseId },
|
|
222
|
+
{ type: 'case', id: canonicalLoserCaseId },
|
|
223
|
+
],
|
|
224
|
+
evidence_refs: [canonicalLoserCaseId, canonicalSurvivorCaseId],
|
|
225
|
+
reason: {
|
|
226
|
+
reason: input.reason,
|
|
227
|
+
confirmed_by: input.confirmed_by,
|
|
228
|
+
confirmation_summary: input.confirmation_summary,
|
|
229
|
+
},
|
|
230
|
+
now,
|
|
231
|
+
});
|
|
232
|
+
return {
|
|
233
|
+
kind: 'merged',
|
|
234
|
+
loser_case_id: input.loser_case_id,
|
|
235
|
+
survivor_case_id: input.survivor_case_id,
|
|
236
|
+
audit_event_id: auditEventId,
|
|
237
|
+
};
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
function splitCase(adapter, input) {
|
|
241
|
+
const tx = adapter;
|
|
242
|
+
if (!confirmed(input)) {
|
|
243
|
+
return {
|
|
244
|
+
kind: 'rejected',
|
|
245
|
+
code: 'case.confirmation_required',
|
|
246
|
+
message: 'confirmed=true, confirmed_by, and confirmation_summary are required.',
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
if (hasCallerChildCaseIds(input.children)) {
|
|
250
|
+
return {
|
|
251
|
+
kind: 'rejected',
|
|
252
|
+
code: 'case.child_id_not_trusted',
|
|
253
|
+
message: 'Child case IDs are runner-owned and must not be supplied in child payloads.',
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
if (input.children.length < 2) {
|
|
257
|
+
return {
|
|
258
|
+
kind: 'rejected',
|
|
259
|
+
code: 'case.split_requires_two_children',
|
|
260
|
+
message: 'A split requires at least two children.',
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
if (input.trusted_child_case_ids.length !== input.children.length) {
|
|
264
|
+
return {
|
|
265
|
+
kind: 'rejected',
|
|
266
|
+
code: 'case.child_id_count_mismatch',
|
|
267
|
+
message: 'trusted_child_case_ids length must match children length.',
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
if (!childIdsAreUnique(input.trusted_child_case_ids) ||
|
|
271
|
+
!trustedChildIdsAreValid(input.trusted_child_case_ids)) {
|
|
272
|
+
return {
|
|
273
|
+
kind: 'rejected',
|
|
274
|
+
code: 'case.child_id_not_trusted',
|
|
275
|
+
message: 'trusted_child_case_ids must be unique UUIDs minted by the standalone runner.',
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
return (0, sqlite_transaction_js_1.runImmediateTransaction)(tx, () => {
|
|
279
|
+
const now = normalizeNow(input.now);
|
|
280
|
+
const parent = readCase(tx, input.parent_case_id);
|
|
281
|
+
if (!parent) {
|
|
282
|
+
return { kind: 'precompile_gap', code: 'case.precompile_gap', case_id: input.parent_case_id };
|
|
283
|
+
}
|
|
284
|
+
if (TERMINAL_CASE_STATUSES.has(parent.status)) {
|
|
285
|
+
return {
|
|
286
|
+
kind: 'rejected',
|
|
287
|
+
code: 'case.terminal_status',
|
|
288
|
+
message: `Cannot split terminal case status ${parent.status}.`,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
tx.prepare(`
|
|
292
|
+
UPDATE case_truth
|
|
293
|
+
SET status = 'split',
|
|
294
|
+
updated_at = ?
|
|
295
|
+
WHERE case_id = ?
|
|
296
|
+
`).run(now, input.parent_case_id);
|
|
297
|
+
for (const [index, child] of input.children.entries()) {
|
|
298
|
+
const childCaseId = input.trusted_child_case_ids[index];
|
|
299
|
+
tx.prepare(`
|
|
300
|
+
INSERT INTO case_truth (
|
|
301
|
+
case_id, current_wiki_path, title, status, split_from_case_id, wiki_path_history,
|
|
302
|
+
scope_refs, confidence, created_at, updated_at
|
|
303
|
+
)
|
|
304
|
+
VALUES (?, ?, ?, 'active', ?, '[]', ?, ?, ?, ?)
|
|
305
|
+
`).run(childCaseId, child.current_wiki_path ?? null, child.title, input.parent_case_id, parent.scope_refs ?? '[]', parent.confidence ?? null, now, now);
|
|
306
|
+
for (const membership of child.membership_sources) {
|
|
307
|
+
const sourceType = membership.source_type;
|
|
308
|
+
const parentMembership = readParentMembership(tx, input.parent_case_id, sourceType, membership.source_id);
|
|
309
|
+
upsertChildMembership({
|
|
310
|
+
adapter: tx,
|
|
311
|
+
child_case_id: childCaseId,
|
|
312
|
+
source_type: sourceType,
|
|
313
|
+
source_id: membership.source_id,
|
|
314
|
+
parent_membership: parentMembership,
|
|
315
|
+
now,
|
|
316
|
+
});
|
|
317
|
+
if (membership.remove_from_parent === true) {
|
|
318
|
+
removeParentMembership({
|
|
319
|
+
adapter: tx,
|
|
320
|
+
parent_case_id: input.parent_case_id,
|
|
321
|
+
source_type: sourceType,
|
|
322
|
+
source_id: membership.source_id,
|
|
323
|
+
now,
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
(0, corrections_js_1.insertCaseCorrectionLock)(tx, {
|
|
329
|
+
case_id: input.parent_case_id,
|
|
330
|
+
target_kind: 'case_field',
|
|
331
|
+
target_ref: { kind: 'case_field', field: 'status' },
|
|
332
|
+
field_name: 'status',
|
|
333
|
+
old_value_json: (0, canonicalize_js_1.canonicalizeJSON)(parent.status),
|
|
334
|
+
new_value_json: (0, canonicalize_js_1.canonicalizeJSON)({
|
|
335
|
+
status: 'split',
|
|
336
|
+
child_case_ids: input.trusted_child_case_ids,
|
|
337
|
+
}),
|
|
338
|
+
reason: input.reason,
|
|
339
|
+
applied_by: input.confirmed_by,
|
|
340
|
+
applied_at: now,
|
|
341
|
+
is_lock_active: 0,
|
|
342
|
+
});
|
|
343
|
+
const auditEventId = insertMemoryEvent({
|
|
344
|
+
adapter: tx,
|
|
345
|
+
event_type: 'case.split',
|
|
346
|
+
topic: `case:${input.parent_case_id}`,
|
|
347
|
+
scope_refs: [
|
|
348
|
+
{ type: 'case', id: input.parent_case_id },
|
|
349
|
+
...input.trusted_child_case_ids.map((id) => ({ type: 'case', id })),
|
|
350
|
+
],
|
|
351
|
+
evidence_refs: input.trusted_child_case_ids,
|
|
352
|
+
reason: {
|
|
353
|
+
reason: input.reason,
|
|
354
|
+
confirmed_by: input.confirmed_by,
|
|
355
|
+
confirmation_summary: input.confirmation_summary,
|
|
356
|
+
child_case_ids: input.trusted_child_case_ids,
|
|
357
|
+
},
|
|
358
|
+
now,
|
|
359
|
+
});
|
|
360
|
+
return {
|
|
361
|
+
kind: 'split',
|
|
362
|
+
parent_case_id: input.parent_case_id,
|
|
363
|
+
child_case_ids: input.trusted_child_case_ids,
|
|
364
|
+
audit_event_id: auditEventId,
|
|
365
|
+
};
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
//# sourceMappingURL=merge-split.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type InferredCaseRole = 'requester' | 'implementer' | 'reviewer' | 'observer' | 'affected';
|
|
2
|
+
interface InferTimelineEventRoleInput {
|
|
3
|
+
userText: string;
|
|
4
|
+
assistantText?: string;
|
|
5
|
+
eventType?: string;
|
|
6
|
+
actorHints?: string[];
|
|
7
|
+
}
|
|
8
|
+
export declare function inferTimelineEventRole(input: InferTimelineEventRoleInput): InferredCaseRole | null;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=role-inference.d.ts.map
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.inferTimelineEventRole = inferTimelineEventRole;
|
|
4
|
+
function hasPattern(text, patterns) {
|
|
5
|
+
return patterns.some((pattern) => pattern.test(text));
|
|
6
|
+
}
|
|
7
|
+
function joinText(parts) {
|
|
8
|
+
return parts.filter((part) => Boolean(part?.trim())).join('\n');
|
|
9
|
+
}
|
|
10
|
+
function mentionsActor(text, actorHints) {
|
|
11
|
+
return actorHints.some((hint) => {
|
|
12
|
+
const trimmed = hint.trim();
|
|
13
|
+
return trimmed.length > 0 && text.toLowerCase().includes(trimmed.toLowerCase());
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
const REQUESTER_PATTERNS = [
|
|
17
|
+
/\b(ask|asks|asked|request|requests|requested|please|can you|could you|would you|help me)\b/i,
|
|
18
|
+
/요청|해줘|해주세요|부탁|해주시|해 줄래|해 주실/i, // Korean: keyword patterns
|
|
19
|
+
];
|
|
20
|
+
const IMPLEMENTER_PATTERNS = [
|
|
21
|
+
/\b(i|we)\s+(will\s+handle|can\s+handle|implemented|fixed|built|handled|completed|shipped|worked\s+on|am\s+working\s+on)\b/i,
|
|
22
|
+
/\b(i'll|we'll)\s+handle\b/i,
|
|
23
|
+
/\b(implemented|fixed|built|handled|completed|shipped)\b/i,
|
|
24
|
+
/제가\s*(작업|수정|구현|처리|담당)|작업했습니다|수정했습니다|구현했습니다|담당합니다/i, // Korean: keyword patterns
|
|
25
|
+
];
|
|
26
|
+
const ACTOR_IMPLEMENTER_PATTERNS = [
|
|
27
|
+
/\b(will\s+handle|implemented|fixed|built|handled|completed|shipped|worked\s+on|owns\s+the\s+work)\b/i,
|
|
28
|
+
/작업|수정|구현|처리|담당/i, // Korean: keyword patterns
|
|
29
|
+
];
|
|
30
|
+
const REVIEWER_PATTERNS = [
|
|
31
|
+
/\b(reviewed|reviewing|review|approved|approve|approval)\b/i,
|
|
32
|
+
/검토|승인|리뷰/i, // Korean: keyword patterns
|
|
33
|
+
];
|
|
34
|
+
const AFFECTED_PATTERNS = [
|
|
35
|
+
/\b(blocked|waiting|held\s+up|impacted|affected|deferred|on\s+hold)\b/i,
|
|
36
|
+
/막힘|막혔|보류|대기|영향/i, // Korean: keyword patterns
|
|
37
|
+
];
|
|
38
|
+
const OBSERVER_PATTERNS = [
|
|
39
|
+
/\b(fyi|for your information|status-only|status only|heads up|observing)\b/i,
|
|
40
|
+
/참고|공유만|상태\s*공유|관찰/i, // Korean: keyword patterns
|
|
41
|
+
];
|
|
42
|
+
const AMBIGUOUS_OWNER_BLOCKER_PATTERNS = [/\b(owner|blocker)\b/i, /오너|소유자|블로커/i]; // Korean: keyword patterns
|
|
43
|
+
function inferTimelineEventRole(input) {
|
|
44
|
+
const userText = input.userText.trim();
|
|
45
|
+
const assistantText = input.assistantText?.trim() ?? '';
|
|
46
|
+
const eventType = input.eventType?.trim() ?? '';
|
|
47
|
+
const actorHints = input.actorHints ?? [];
|
|
48
|
+
const combinedText = joinText([userText, assistantText, eventType, actorHints.join(' ')]);
|
|
49
|
+
if (!combinedText) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
const roles = new Set();
|
|
53
|
+
if (hasPattern(userText, REQUESTER_PATTERNS)) {
|
|
54
|
+
roles.add('requester');
|
|
55
|
+
}
|
|
56
|
+
const implementerText = joinText([assistantText, eventType]);
|
|
57
|
+
if (hasPattern(implementerText, IMPLEMENTER_PATTERNS)) {
|
|
58
|
+
roles.add('implementer');
|
|
59
|
+
}
|
|
60
|
+
if (mentionsActor(userText, actorHints) &&
|
|
61
|
+
hasPattern(userText, ACTOR_IMPLEMENTER_PATTERNS) &&
|
|
62
|
+
!hasPattern(userText, REQUESTER_PATTERNS)) {
|
|
63
|
+
roles.add('implementer');
|
|
64
|
+
}
|
|
65
|
+
if (hasPattern(joinText([assistantText, eventType]), REVIEWER_PATTERNS)) {
|
|
66
|
+
roles.add('reviewer');
|
|
67
|
+
}
|
|
68
|
+
if (mentionsActor(userText, actorHints) && hasPattern(userText, REVIEWER_PATTERNS)) {
|
|
69
|
+
roles.add('reviewer');
|
|
70
|
+
}
|
|
71
|
+
if (hasPattern(combinedText, AFFECTED_PATTERNS)) {
|
|
72
|
+
roles.add('affected');
|
|
73
|
+
}
|
|
74
|
+
if (hasPattern(combinedText, OBSERVER_PATTERNS)) {
|
|
75
|
+
roles.add('observer');
|
|
76
|
+
}
|
|
77
|
+
if (roles.size === 0 && hasPattern(combinedText, AMBIGUOUS_OWNER_BLOCKER_PATTERNS)) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
if (roles.size !== 1) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
return [...roles][0] ?? null;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=role-inference.js.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { AdapterLike } from './wiki-page-index.js';
|
|
2
|
+
import type { SearchHitDiagnostics } from '../search/search-quality.js';
|
|
3
|
+
export type { AdapterLike };
|
|
4
|
+
export interface SearchRollupLeafHit {
|
|
5
|
+
source_type: 'decision' | 'checkpoint' | 'wiki_page' | 'connector_event';
|
|
6
|
+
source_id: string;
|
|
7
|
+
fused_rank_score: number;
|
|
8
|
+
page_type?: 'case';
|
|
9
|
+
case_id?: string | null;
|
|
10
|
+
record: unknown;
|
|
11
|
+
retrieval_diagnostics?: SearchHitDiagnostics;
|
|
12
|
+
}
|
|
13
|
+
export interface SearchRollupResult {
|
|
14
|
+
source_type: 'decision' | 'checkpoint' | 'wiki_page' | 'case' | 'standalone_connector_hit';
|
|
15
|
+
source_id: string;
|
|
16
|
+
case_id: string | null;
|
|
17
|
+
score: number;
|
|
18
|
+
contributing_leaves?: string[];
|
|
19
|
+
contributing_leaf_diagnostics?: Record<string, SearchHitDiagnostics>;
|
|
20
|
+
retrieval_diagnostics?: SearchHitDiagnostics;
|
|
21
|
+
record: unknown;
|
|
22
|
+
}
|
|
23
|
+
export declare function rollUpSearchHits(input: {
|
|
24
|
+
fusedHits: SearchRollupLeafHit[];
|
|
25
|
+
adapter: AdapterLike;
|
|
26
|
+
}): SearchRollupResult[];
|
|
27
|
+
//# sourceMappingURL=search-rollup.d.ts.map
|