@jungjaehoon/mama-core 1.8.1 → 2.0.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/dist/agent-graph/alias-write.js +3 -0
- package/dist/agent-graph/entity-resolve.js +3 -7
- package/dist/agent-graph/graph-query.js +5 -4
- package/dist/agent-graph/types.d.ts +30 -0
- package/dist/cases/store.d.ts +0 -4
- package/dist/cases/store.js +0 -66
- package/dist/cases/wiki-page-index.d.ts +0 -13
- package/dist/cases/wiki-page-index.js +0 -153
- package/dist/connectors/event-index.d.ts +1 -22
- package/dist/connectors/event-index.js +0 -157
- package/dist/context-compile/channel-grant.d.ts +46 -0
- package/dist/context-compile/channel-grant.js +66 -0
- package/dist/context-compile/compiler.js +5 -2
- package/dist/context-compile/index.d.ts +1 -0
- package/dist/context-compile/index.js +1 -0
- package/dist/context-compile/packet-store.d.ts +0 -1
- package/dist/context-compile/packet-store.js +0 -16
- package/dist/context-compile/source-readers.d.ts +1 -0
- package/dist/context-compile/source-readers.js +156 -23
- package/dist/context-compile/types.d.ts +17 -0
- package/dist/context-compile/visibility.js +28 -4
- package/dist/db-adapter/node-sqlite-adapter.js +4 -0
- package/dist/db-manager.d.ts +0 -13
- package/dist/db-manager.js +0 -66
- package/dist/decision-formatter.d.ts +0 -11
- package/dist/decision-formatter.js +0 -122
- package/dist/decision-tracker.d.ts +0 -12
- package/dist/decision-tracker.js +0 -50
- package/dist/edges/ref-validation.d.ts +1 -2
- package/dist/edges/ref-validation.js +33 -13
- package/dist/edges/store.d.ts +1 -3
- package/dist/edges/store.js +0 -214
- package/dist/edges/types.d.ts +10 -0
- package/dist/embedding-client.d.ts +0 -17
- package/dist/embedding-client.js +0 -80
- package/dist/embedding-server/mobile/auth.d.ts +1 -10
- package/dist/embedding-server/mobile/auth.js +0 -15
- package/dist/embeddings.d.ts +0 -12
- package/dist/embeddings.js +0 -68
- package/dist/entities/audit-metrics.d.ts +0 -5
- package/dist/entities/audit-metrics.js +0 -4
- package/dist/entities/candidate-generator.d.ts +1 -2
- package/dist/entities/candidate-generator.js +0 -412
- package/dist/entities/entity-list.d.ts +0 -1
- package/dist/entities/entity-list.js +0 -96
- package/dist/entities/entity-orphan-list.d.ts +0 -9
- package/dist/entities/entity-orphan-list.js +0 -88
- package/dist/entities/entity-search.d.ts +0 -1
- package/dist/entities/entity-search.js +0 -147
- package/dist/entities/errors.d.ts +0 -10
- package/dist/entities/errors.js +1 -25
- package/dist/entities/lineage-store.d.ts +0 -23
- package/dist/entities/lineage-store.js +0 -104
- package/dist/entities/policy-store.d.ts +0 -9
- package/dist/entities/policy-store.js +0 -188
- package/dist/entities/provenance-query.d.ts +0 -1
- package/dist/entities/provenance-query.js +0 -164
- package/dist/entities/resolution-engine.d.ts +1 -8
- package/dist/entities/resolution-engine.js +0 -66
- package/dist/entities/rollback-preview.d.ts +0 -4
- package/dist/entities/rollback-preview.js +0 -232
- package/dist/entities/store.d.ts +1 -10
- package/dist/entities/store.js +0 -49
- package/dist/index.d.ts +15 -31
- package/dist/index.js +4 -46
- package/dist/mama-api.d.ts +19 -1
- package/dist/mama-api.js +97 -2
- package/dist/memory/api.d.ts +9 -0
- package/dist/memory/api.js +68 -4
- package/dist/memory/event-store.d.ts +0 -1
- package/dist/memory/event-store.js +0 -15
- package/dist/memory/finding-store.d.ts +0 -1
- package/dist/memory/finding-store.js +0 -12
- package/dist/memory/scope-backfill.d.ts +0 -3
- package/dist/memory/scope-backfill.js +0 -74
- package/dist/memory/scope-store.d.ts +0 -1
- package/dist/memory/scope-store.js +0 -4
- package/dist/memory/truth-store.d.ts +0 -3
- package/dist/memory/truth-store.js +0 -22
- package/dist/memory/types.d.ts +0 -1
- package/dist/memory/types.js +0 -13
- package/dist/memory-store.d.ts +0 -16
- package/dist/memory-store.js +0 -51
- package/dist/ollama-client.d.ts +0 -9
- package/dist/ollama-client.js +0 -125
- package/dist/outcome-tracker.d.ts +0 -14
- package/dist/outcome-tracker.js +0 -44
- package/dist/progress-indicator.d.ts +0 -4
- package/dist/progress-indicator.js +0 -7
- package/dist/provenance/source-ref.d.ts +0 -9
- package/dist/provenance/source-ref.js +0 -145
- package/dist/query-intent.d.ts +0 -4
- package/dist/query-intent.js +0 -30
- package/dist/relevance-scorer.d.ts +0 -9
- package/dist/relevance-scorer.js +0 -81
- package/dist/search/feedback-store.d.ts +0 -1
- package/dist/search/feedback-store.js +0 -4
- package/dist/search/ranker-rescore.d.ts +0 -3
- package/dist/search/ranker-rescore.js +0 -27
- package/dist/search/ranker-trainer.d.ts +0 -8
- package/dist/search/ranker-trainer.js +0 -376
- package/dist/tier-validator.d.ts +0 -12
- package/dist/tier-validator.js +0 -56
- package/package.json +1 -10
- package/dist/cases/case-links.d.ts +0 -116
- package/dist/cases/case-links.js +0 -794
- package/dist/cases/composition-overrides.d.ts +0 -90
- package/dist/cases/composition-overrides.js +0 -724
- package/dist/cases/corrections.d.ts +0 -156
- package/dist/cases/corrections.js +0 -1045
- package/dist/cases/freshness.d.ts +0 -57
- package/dist/cases/freshness.js +0 -283
- package/dist/cases/live-state.d.ts +0 -48
- package/dist/cases/live-state.js +0 -256
- package/dist/cases/membership-explain.d.ts +0 -56
- package/dist/cases/membership-explain.js +0 -140
- package/dist/cases/membership-matcher.d.ts +0 -23
- package/dist/cases/membership-matcher.js +0 -193
- package/dist/cases/merge-split.d.ts +0 -61
- package/dist/cases/merge-split.js +0 -368
- package/dist/cases/role-inference.d.ts +0 -10
- package/dist/cases/role-inference.js +0 -85
- package/dist/cases/sqlite-transaction.d.ts +0 -10
- package/dist/cases/sqlite-transaction.js +0 -62
- package/dist/cases/target-ref.d.ts +0 -25
- package/dist/cases/target-ref.js +0 -22
- package/dist/cases/tombstone-sweeper.d.ts +0 -13
- package/dist/cases/tombstone-sweeper.js +0 -128
- package/dist/entities/entity-impact.d.ts +0 -41
- package/dist/entities/entity-impact.js +0 -140
- package/dist/entities/exact-merge-backfill.d.ts +0 -11
- package/dist/entities/exact-merge-backfill.js +0 -161
- package/dist/entities/lineage-backfill.d.ts +0 -12
- package/dist/entities/lineage-backfill.js +0 -101
- package/dist/memory-inject.d.ts +0 -24
- package/dist/memory-inject.js +0 -116
|
@@ -1,368 +0,0 @@
|
|
|
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
|
|
@@ -1,10 +0,0 @@
|
|
|
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
|
|
@@ -1,85 +0,0 @@
|
|
|
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
|
|
@@ -1,10 +0,0 @@
|
|
|
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
|
|
@@ -1,62 +0,0 @@
|
|
|
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
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import type { CaseMembershipSourceType } from './types.js';
|
|
2
|
-
export type CaseFieldName = 'case_id' | 'canonical_case_id' | 'title' | 'status' | 'status_reason' | 'primary_actors' | 'blockers' | 'confidence' | 'last_activity_at';
|
|
3
|
-
export interface CaseFieldTargetRef {
|
|
4
|
-
kind: 'case_field';
|
|
5
|
-
field: CaseFieldName;
|
|
6
|
-
}
|
|
7
|
-
export interface CaseMembershipTargetRef {
|
|
8
|
-
kind: 'membership';
|
|
9
|
-
source_type: CaseMembershipSourceType;
|
|
10
|
-
source_id: string;
|
|
11
|
-
}
|
|
12
|
-
export interface CaseWikiSectionTargetRef {
|
|
13
|
-
kind: 'wiki_section';
|
|
14
|
-
section_heading: string;
|
|
15
|
-
}
|
|
16
|
-
export type CaseTargetRef = CaseFieldTargetRef | CaseMembershipTargetRef | CaseWikiSectionTargetRef;
|
|
17
|
-
export interface CanonicalTargetRef {
|
|
18
|
-
json: string;
|
|
19
|
-
hash: Buffer;
|
|
20
|
-
}
|
|
21
|
-
export declare function buildCaseFieldTargetRef(fieldName: CaseFieldName): CaseFieldTargetRef;
|
|
22
|
-
export declare function buildMembershipTargetRef(sourceType: CaseMembershipSourceType, sourceId: string): CaseMembershipTargetRef;
|
|
23
|
-
export declare function buildWikiSectionTargetRef(sectionHeading: string): CaseWikiSectionTargetRef;
|
|
24
|
-
export declare function canonicalTargetRef(targetRef: CaseTargetRef): CanonicalTargetRef;
|
|
25
|
-
//# sourceMappingURL=target-ref.d.ts.map
|
package/dist/cases/target-ref.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.buildCaseFieldTargetRef = buildCaseFieldTargetRef;
|
|
4
|
-
exports.buildMembershipTargetRef = buildMembershipTargetRef;
|
|
5
|
-
exports.buildWikiSectionTargetRef = buildWikiSectionTargetRef;
|
|
6
|
-
exports.canonicalTargetRef = canonicalTargetRef;
|
|
7
|
-
const canonicalize_js_1 = require("../canonicalize.js");
|
|
8
|
-
function buildCaseFieldTargetRef(fieldName) {
|
|
9
|
-
return { kind: 'case_field', field: fieldName };
|
|
10
|
-
}
|
|
11
|
-
function buildMembershipTargetRef(sourceType, sourceId) {
|
|
12
|
-
return { kind: 'membership', source_type: sourceType, source_id: sourceId };
|
|
13
|
-
}
|
|
14
|
-
function buildWikiSectionTargetRef(sectionHeading) {
|
|
15
|
-
return { kind: 'wiki_section', section_heading: sectionHeading };
|
|
16
|
-
}
|
|
17
|
-
function canonicalTargetRef(targetRef) {
|
|
18
|
-
const json = (0, canonicalize_js_1.canonicalizeJSON)(targetRef);
|
|
19
|
-
const hash = (0, canonicalize_js_1.targetRefHashCanonicalJSON)(json);
|
|
20
|
-
return { json, hash };
|
|
21
|
-
}
|
|
22
|
-
//# sourceMappingURL=target-ref.js.map
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { DatabaseAdapter } from '../db-manager.js';
|
|
2
|
-
export interface SweepStaleCaseMembershipsOptions {
|
|
3
|
-
now?: string;
|
|
4
|
-
limit?: number;
|
|
5
|
-
}
|
|
6
|
-
export interface SweepStaleCaseMembershipsResult {
|
|
7
|
-
scanned_count: number;
|
|
8
|
-
stale_count: number;
|
|
9
|
-
locked_stale_count: number;
|
|
10
|
-
audit_event_ids: string[];
|
|
11
|
-
}
|
|
12
|
-
export declare function sweepStaleCaseMemberships(adapter: DatabaseAdapter, options?: SweepStaleCaseMembershipsOptions): SweepStaleCaseMembershipsResult;
|
|
13
|
-
//# sourceMappingURL=tombstone-sweeper.d.ts.map
|