@jungjaehoon/mama-core 1.4.2 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/README.md +48 -10
  2. package/db/migrations/026-create-canonical-entity-tables.sql +99 -0
  3. package/db/migrations/027-create-entity-review-support.sql +44 -0
  4. package/db/migrations/028-create-entity-audit-tables.sql +45 -0
  5. package/db/migrations/029-extend-entity-observations-observation-type.sql +60 -0
  6. package/db/migrations/030-case-first-memory-substrate.sql +901 -0
  7. package/db/migrations/031-wiki-page-index-provenance-columns.sql +2 -0
  8. package/db/migrations/032-add-memory-provenance-columns.sql +21 -0
  9. package/db/migrations/033-create-model-runs-and-tool-traces.sql +53 -0
  10. package/db/migrations/034-add-connector-event-scope-columns.sql +14 -0
  11. package/db/migrations/035-create-twin-edges.sql +63 -0
  12. package/db/migrations/036-create-agent-situation-packets.sql +61 -0
  13. package/dist/agent-graph/alias-write.d.ts +3 -0
  14. package/dist/agent-graph/alias-write.js +231 -0
  15. package/dist/agent-graph/entity-resolve.d.ts +3 -0
  16. package/dist/agent-graph/entity-resolve.js +275 -0
  17. package/dist/agent-graph/errors.d.ts +4 -0
  18. package/dist/agent-graph/errors.js +11 -0
  19. package/dist/agent-graph/graph-query.d.ts +5 -0
  20. package/dist/agent-graph/graph-query.js +359 -0
  21. package/dist/agent-graph/index.d.ts +6 -0
  22. package/dist/agent-graph/index.js +22 -0
  23. package/dist/agent-graph/types.d.ts +162 -0
  24. package/dist/agent-graph/types.js +3 -0
  25. package/dist/agent-situation/builder.d.ts +8 -0
  26. package/dist/agent-situation/builder.js +239 -0
  27. package/dist/agent-situation/cache-key.d.ts +4 -0
  28. package/dist/agent-situation/cache-key.js +64 -0
  29. package/dist/agent-situation/index.d.ts +7 -0
  30. package/dist/agent-situation/index.js +28 -0
  31. package/dist/agent-situation/packet-store.d.ts +27 -0
  32. package/dist/agent-situation/packet-store.js +228 -0
  33. package/dist/agent-situation/ranking-policy.d.ts +18 -0
  34. package/dist/agent-situation/ranking-policy.js +75 -0
  35. package/dist/agent-situation/source-readers.d.ts +52 -0
  36. package/dist/agent-situation/source-readers.js +300 -0
  37. package/dist/agent-situation/types.d.ts +195 -0
  38. package/dist/agent-situation/types.js +3 -0
  39. package/dist/canonicalize.d.ts +38 -0
  40. package/dist/canonicalize.js +106 -0
  41. package/dist/cases/case-links.d.ts +116 -0
  42. package/dist/cases/case-links.js +794 -0
  43. package/dist/cases/composition-overrides.d.ts +90 -0
  44. package/dist/cases/composition-overrides.js +724 -0
  45. package/dist/cases/corrections.d.ts +156 -0
  46. package/dist/cases/corrections.js +1045 -0
  47. package/dist/cases/freshness.d.ts +57 -0
  48. package/dist/cases/freshness.js +283 -0
  49. package/dist/cases/live-state.d.ts +48 -0
  50. package/dist/cases/live-state.js +256 -0
  51. package/dist/cases/membership-explain.d.ts +56 -0
  52. package/dist/cases/membership-explain.js +140 -0
  53. package/dist/cases/membership-matcher.d.ts +23 -0
  54. package/dist/cases/membership-matcher.js +193 -0
  55. package/dist/cases/merge-split.d.ts +61 -0
  56. package/dist/cases/merge-split.js +368 -0
  57. package/dist/cases/role-inference.d.ts +10 -0
  58. package/dist/cases/role-inference.js +85 -0
  59. package/dist/cases/search-rollup.d.ts +27 -0
  60. package/dist/cases/search-rollup.js +309 -0
  61. package/dist/cases/sqlite-transaction.d.ts +10 -0
  62. package/dist/cases/sqlite-transaction.js +62 -0
  63. package/dist/cases/store.d.ts +74 -0
  64. package/dist/cases/store.js +816 -0
  65. package/dist/cases/target-ref.d.ts +25 -0
  66. package/dist/cases/target-ref.js +22 -0
  67. package/dist/cases/timeline-range.d.ts +6 -0
  68. package/dist/cases/timeline-range.js +322 -0
  69. package/dist/cases/tombstone-sweeper.d.ts +13 -0
  70. package/dist/cases/tombstone-sweeper.js +128 -0
  71. package/dist/cases/types.d.ts +244 -0
  72. package/dist/cases/types.js +47 -0
  73. package/dist/cases/wiki-page-index.d.ts +50 -0
  74. package/dist/cases/wiki-page-index.js +350 -0
  75. package/dist/connectors/event-index.d.ts +45 -0
  76. package/dist/connectors/event-index.js +381 -0
  77. package/dist/connectors/raw-query.d.ts +18 -0
  78. package/dist/connectors/raw-query.js +305 -0
  79. package/dist/connectors/types.d.ts +109 -0
  80. package/dist/connectors/types.js +3 -0
  81. package/dist/db-adapter/node-sqlite-adapter.d.ts +6 -0
  82. package/dist/db-adapter/node-sqlite-adapter.js +131 -0
  83. package/dist/db-manager.d.ts +12 -1
  84. package/dist/db-manager.js +60 -5
  85. package/dist/decision-formatter.d.ts +2 -0
  86. package/dist/decision-formatter.js +11 -8
  87. package/dist/edges/ref-validation.d.ts +8 -0
  88. package/dist/edges/ref-validation.js +262 -0
  89. package/dist/edges/store.d.ts +10 -0
  90. package/dist/edges/store.js +343 -0
  91. package/dist/edges/types.d.ts +86 -0
  92. package/dist/edges/types.js +18 -0
  93. package/dist/embedding-server/mobile/daemon.d.ts +1 -0
  94. package/dist/embedding-server/mobile/daemon.js +11 -1
  95. package/dist/embeddings.js +13 -0
  96. package/dist/entities/audit-metrics.d.ts +73 -0
  97. package/dist/entities/audit-metrics.js +128 -0
  98. package/dist/entities/candidate-generator.d.ts +9 -0
  99. package/dist/entities/candidate-generator.js +436 -0
  100. package/dist/entities/entity-impact.d.ts +41 -0
  101. package/dist/entities/entity-impact.js +140 -0
  102. package/dist/entities/entity-linked-decision-counts.d.ts +2 -0
  103. package/dist/entities/entity-linked-decision-counts.js +29 -0
  104. package/dist/entities/entity-list.d.ts +23 -0
  105. package/dist/entities/entity-list.js +99 -0
  106. package/dist/entities/entity-orphan-list.d.ts +26 -0
  107. package/dist/entities/entity-orphan-list.js +91 -0
  108. package/dist/entities/entity-search.d.ts +21 -0
  109. package/dist/entities/entity-search.js +150 -0
  110. package/dist/entities/errors.d.ts +65 -0
  111. package/dist/entities/errors.js +124 -0
  112. package/dist/entities/exact-merge-backfill.d.ts +11 -0
  113. package/dist/entities/exact-merge-backfill.js +161 -0
  114. package/dist/entities/lineage-backfill.d.ts +12 -0
  115. package/dist/entities/lineage-backfill.js +101 -0
  116. package/dist/entities/lineage-store.d.ts +51 -0
  117. package/dist/entities/lineage-store.js +308 -0
  118. package/dist/entities/normalization.d.ts +14 -0
  119. package/dist/entities/normalization.js +63 -0
  120. package/dist/entities/policy-store.d.ts +17 -0
  121. package/dist/entities/policy-store.js +263 -0
  122. package/dist/entities/policy-types.d.ts +72 -0
  123. package/dist/entities/policy-types.js +59 -0
  124. package/dist/entities/projection.d.ts +8 -0
  125. package/dist/entities/projection.js +82 -0
  126. package/dist/entities/provenance-query.d.ts +33 -0
  127. package/dist/entities/provenance-query.js +167 -0
  128. package/dist/entities/read-identity.d.ts +31 -0
  129. package/dist/entities/read-identity.js +264 -0
  130. package/dist/entities/recall-bridge.d.ts +5 -0
  131. package/dist/entities/recall-bridge.js +171 -0
  132. package/dist/entities/resolution-engine.d.ts +15 -0
  133. package/dist/entities/resolution-engine.js +69 -0
  134. package/dist/entities/rollback-preview.d.ts +39 -0
  135. package/dist/entities/rollback-preview.js +235 -0
  136. package/dist/entities/source-locator.d.ts +3 -0
  137. package/dist/entities/source-locator.js +32 -0
  138. package/dist/entities/store.d.ts +75 -0
  139. package/dist/entities/store.js +465 -0
  140. package/dist/entities/types.d.ts +162 -0
  141. package/dist/entities/types.js +30 -0
  142. package/dist/index.d.ts +62 -3
  143. package/dist/index.js +116 -26
  144. package/dist/mama-api.d.ts +53 -35
  145. package/dist/mama-api.js +437 -78
  146. package/dist/memory/api.d.ts +26 -38
  147. package/dist/memory/api.js +772 -68
  148. package/dist/memory/event-store.d.ts +3 -0
  149. package/dist/memory/event-store.js +21 -2
  150. package/dist/memory/provenance-audit.d.ts +22 -0
  151. package/dist/memory/provenance-audit.js +112 -0
  152. package/dist/memory/provenance-query.d.ts +11 -0
  153. package/dist/memory/provenance-query.js +193 -0
  154. package/dist/memory/provenance.d.ts +29 -0
  155. package/dist/memory/provenance.js +136 -0
  156. package/dist/memory/scope-backfill.d.ts +17 -0
  157. package/dist/memory/scope-backfill.js +77 -0
  158. package/dist/memory/types.d.ts +97 -16
  159. package/dist/model-runs/store.d.ts +13 -0
  160. package/dist/model-runs/store.js +457 -0
  161. package/dist/model-runs/tool-trace-store.d.ts +4 -0
  162. package/dist/model-runs/tool-trace-store.js +114 -0
  163. package/dist/model-runs/types.d.ts +69 -0
  164. package/dist/model-runs/types.js +5 -0
  165. package/dist/runtime/trusted-provenance.d.ts +3 -0
  166. package/dist/runtime/trusted-provenance.js +6 -0
  167. package/dist/search/feedback-store.d.ts +59 -0
  168. package/dist/search/feedback-store.js +211 -0
  169. package/dist/search/question-type.d.ts +5 -0
  170. package/dist/search/question-type.js +41 -0
  171. package/dist/search/ranker-features.d.ts +23 -0
  172. package/dist/search/ranker-features.js +104 -0
  173. package/dist/search/ranker-rescore.d.ts +31 -0
  174. package/dist/search/ranker-rescore.js +226 -0
  175. package/dist/search/ranker-trainer.d.ts +94 -0
  176. package/dist/search/ranker-trainer.js +860 -0
  177. package/dist/search/search-quality.d.ts +36 -0
  178. package/dist/search/search-quality.js +29 -0
  179. package/dist/test-utils.d.ts +14 -0
  180. package/dist/test-utils.js +52 -0
  181. package/package.json +28 -3
@@ -0,0 +1,308 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseEntityIngestRunRow = parseEntityIngestRunRow;
4
+ exports.createEntityIngestRun = createEntityIngestRun;
5
+ exports.completeEntityIngestRun = completeEntityIngestRun;
6
+ exports.failEntityIngestRun = failEntityIngestRun;
7
+ exports.appendEntityLineageLink = appendEntityLineageLink;
8
+ exports.supersedeEntityLineageForEntity = supersedeEntityLineageForEntity;
9
+ exports.seedLineageForEntityMaterialization = seedLineageForEntityMaterialization;
10
+ exports.adoptLineageAfterMerge = adoptLineageAfterMerge;
11
+ exports.adoptLineageAfterMergeSync = adoptLineageAfterMergeSync;
12
+ exports.listActiveEntityLineage = listActiveEntityLineage;
13
+ exports.listEntityLineageHistory = listEntityLineageHistory;
14
+ const node_crypto_1 = require("node:crypto");
15
+ const db_manager_js_1 = require("../db-manager.js");
16
+ function now() {
17
+ return Date.now();
18
+ }
19
+ function parseEntityIngestRunRow(row) {
20
+ return {
21
+ id: String(row.id),
22
+ connector: String(row.connector),
23
+ run_kind: row.run_kind,
24
+ status: row.status,
25
+ scope_key: String(row.scope_key),
26
+ source_window_start: typeof row.source_window_start === 'number' ? row.source_window_start : null,
27
+ source_window_end: typeof row.source_window_end === 'number' ? row.source_window_end : null,
28
+ raw_count: Number(row.raw_count ?? 0),
29
+ observation_count: Number(row.observation_count ?? 0),
30
+ candidate_count: Number(row.candidate_count ?? 0),
31
+ reviewable_count: Number(row.reviewable_count ?? 0),
32
+ audit_run_id: typeof row.audit_run_id === 'string' ? row.audit_run_id : null,
33
+ audit_classification: typeof row.audit_classification === 'string'
34
+ ? row.audit_classification
35
+ : null,
36
+ error_reason: typeof row.error_reason === 'string' ? row.error_reason : null,
37
+ created_at: Number(row.created_at),
38
+ completed_at: typeof row.completed_at === 'number' ? row.completed_at : null,
39
+ };
40
+ }
41
+ function parseEntityLineageLinkRow(row) {
42
+ return {
43
+ id: String(row.id),
44
+ canonical_entity_id: String(row.canonical_entity_id),
45
+ entity_observation_id: String(row.entity_observation_id),
46
+ source_entity_id: typeof row.source_entity_id === 'string' ? row.source_entity_id : null,
47
+ contribution_kind: row.contribution_kind,
48
+ run_id: typeof row.run_id === 'string' ? row.run_id : null,
49
+ candidate_id: typeof row.candidate_id === 'string' ? row.candidate_id : null,
50
+ review_action_id: typeof row.review_action_id === 'string' ? row.review_action_id : null,
51
+ status: row.status,
52
+ capture_mode: row.capture_mode,
53
+ confidence: Number(row.confidence),
54
+ created_at: Number(row.created_at),
55
+ superseded_at: typeof row.superseded_at === 'number' ? row.superseded_at : null,
56
+ };
57
+ }
58
+ async function createEntityIngestRun(input) {
59
+ await (0, db_manager_js_1.initDB)();
60
+ const adapter = (0, db_manager_js_1.getAdapter)();
61
+ const createdAt = now();
62
+ const id = input.id || `eir_${(0, node_crypto_1.randomUUID)()}`;
63
+ adapter
64
+ .prepare(`
65
+ INSERT INTO entity_ingest_runs (
66
+ id, connector, run_kind, status, scope_key, source_window_start, source_window_end,
67
+ raw_count, observation_count, candidate_count, reviewable_count, audit_run_id,
68
+ audit_classification, error_reason, created_at, completed_at
69
+ ) VALUES (?, ?, ?, 'running', ?, ?, ?, 0, 0, 0, 0, NULL, NULL, NULL, ?, NULL)
70
+ `)
71
+ .run(id, input.connector, input.run_kind, input.scope_key, input.source_window_start, input.source_window_end, createdAt);
72
+ const row = adapter.prepare(`SELECT * FROM entity_ingest_runs WHERE id = ?`).get(id);
73
+ return parseEntityIngestRunRow(row);
74
+ }
75
+ async function completeEntityIngestRun(id, input) {
76
+ await (0, db_manager_js_1.initDB)();
77
+ const adapter = (0, db_manager_js_1.getAdapter)();
78
+ const completedAt = now();
79
+ const completion = adapter
80
+ .prepare(`
81
+ UPDATE entity_ingest_runs
82
+ SET status = 'complete',
83
+ raw_count = ?,
84
+ observation_count = ?,
85
+ candidate_count = ?,
86
+ reviewable_count = ?,
87
+ audit_run_id = ?,
88
+ audit_classification = ?,
89
+ error_reason = NULL,
90
+ completed_at = ?
91
+ WHERE id = ?
92
+ `)
93
+ .run(input.raw_count, input.observation_count, input.candidate_count, input.reviewable_count, input.audit_run_id ?? null, input.audit_classification ?? null, completedAt, id);
94
+ if (completion.changes !== 1) {
95
+ throw new Error(`Entity ingest run not found: ${id}`);
96
+ }
97
+ const row = adapter.prepare(`SELECT * FROM entity_ingest_runs WHERE id = ?`).get(id);
98
+ return parseEntityIngestRunRow(row);
99
+ }
100
+ async function failEntityIngestRun(id, errorReason) {
101
+ await (0, db_manager_js_1.initDB)();
102
+ const adapter = (0, db_manager_js_1.getAdapter)();
103
+ const completedAt = now();
104
+ const completion = adapter
105
+ .prepare(`
106
+ UPDATE entity_ingest_runs
107
+ SET status = 'failed',
108
+ error_reason = ?,
109
+ completed_at = ?
110
+ WHERE id = ?
111
+ `)
112
+ .run(errorReason, completedAt, id);
113
+ if (completion.changes !== 1) {
114
+ throw new Error(`Entity ingest run not found: ${id}`);
115
+ }
116
+ const row = adapter.prepare(`SELECT * FROM entity_ingest_runs WHERE id = ?`).get(id);
117
+ return parseEntityIngestRunRow(row);
118
+ }
119
+ async function appendEntityLineageLink(input) {
120
+ await (0, db_manager_js_1.initDB)();
121
+ const adapter = (0, db_manager_js_1.getAdapter)();
122
+ const existing = adapter
123
+ .prepare(`
124
+ SELECT *
125
+ FROM entity_lineage_links
126
+ WHERE canonical_entity_id = ?
127
+ AND entity_observation_id = ?
128
+ AND status = 'active'
129
+ LIMIT 1
130
+ `)
131
+ .get(input.canonical_entity_id, input.entity_observation_id);
132
+ // Idempotent by (canonical_entity_id, entity_observation_id, status='active').
133
+ // If an active row already exists for the pair, the existing row is returned
134
+ // and the caller-provided metadata is ignored.
135
+ if (existing) {
136
+ return {
137
+ link: parseEntityLineageLinkRow(existing),
138
+ created: false,
139
+ };
140
+ }
141
+ const id = `elin_${(0, node_crypto_1.randomUUID)()}`;
142
+ const createdAt = now();
143
+ adapter
144
+ .prepare(`
145
+ INSERT INTO entity_lineage_links (
146
+ id, canonical_entity_id, entity_observation_id, source_entity_id,
147
+ contribution_kind, run_id, candidate_id, review_action_id,
148
+ status, capture_mode, confidence, created_at, superseded_at
149
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'active', ?, ?, ?, NULL)
150
+ `)
151
+ .run(id, input.canonical_entity_id, input.entity_observation_id, input.source_entity_id, input.contribution_kind, input.run_id, input.candidate_id, input.review_action_id, input.capture_mode, input.confidence, createdAt);
152
+ const row = adapter.prepare(`SELECT * FROM entity_lineage_links WHERE id = ?`).get(id);
153
+ return {
154
+ link: parseEntityLineageLinkRow(row),
155
+ created: true,
156
+ };
157
+ }
158
+ async function supersedeEntityLineageForEntity(entityId) {
159
+ await (0, db_manager_js_1.initDB)();
160
+ const adapter = (0, db_manager_js_1.getAdapter)();
161
+ const supersededAt = now();
162
+ const result = adapter
163
+ .prepare(`
164
+ UPDATE entity_lineage_links
165
+ SET status = 'superseded',
166
+ superseded_at = ?
167
+ WHERE canonical_entity_id = ?
168
+ AND status = 'active'
169
+ `)
170
+ .run(supersededAt, entityId);
171
+ return Number(result.changes ?? 0);
172
+ }
173
+ async function seedLineageForEntityMaterialization(input) {
174
+ const result = await appendEntityLineageLink({
175
+ canonical_entity_id: input.canonical_entity_id,
176
+ entity_observation_id: input.entity_observation_id,
177
+ source_entity_id: null,
178
+ contribution_kind: 'seed',
179
+ run_id: input.run_id ?? null,
180
+ candidate_id: null,
181
+ review_action_id: null,
182
+ capture_mode: input.capture_mode ?? 'direct',
183
+ confidence: input.confidence ?? 1,
184
+ });
185
+ return result.link;
186
+ }
187
+ function runLineageTransaction(adapter, fn) {
188
+ const savepoint = `adopt_lineage_${(0, node_crypto_1.randomUUID)().replace(/-/g, '')}`;
189
+ adapter.prepare(`SAVEPOINT ${savepoint}`).run();
190
+ try {
191
+ const result = fn();
192
+ adapter.prepare(`RELEASE SAVEPOINT ${savepoint}`).run();
193
+ return result;
194
+ }
195
+ catch (error) {
196
+ try {
197
+ adapter.prepare(`ROLLBACK TO SAVEPOINT ${savepoint}`).run();
198
+ }
199
+ catch {
200
+ // Ignore rollback errors when the connection already unwound the savepoint.
201
+ }
202
+ try {
203
+ adapter.prepare(`RELEASE SAVEPOINT ${savepoint}`).run();
204
+ }
205
+ catch {
206
+ // Ignore cleanup failures after rollback.
207
+ }
208
+ throw error;
209
+ }
210
+ }
211
+ async function adoptLineageAfterMerge(input) {
212
+ if (!input.adapter) {
213
+ await (0, db_manager_js_1.initDB)();
214
+ const adapter = (0, db_manager_js_1.getAdapter)();
215
+ return adoptLineageAfterMergeSync({ ...input, adapter });
216
+ }
217
+ return adoptLineageAfterMergeSync(input);
218
+ }
219
+ function adoptLineageAfterMergeSync(input) {
220
+ return runLineageTransaction(input.adapter, () => adoptLineageAfterMergeWithAdapter(input.adapter, input));
221
+ }
222
+ function adoptLineageAfterMergeWithAdapter(adapter, input) {
223
+ const createdAt = now();
224
+ const supersededAt = now();
225
+ const sourceRows = adapter
226
+ .prepare(`
227
+ SELECT *
228
+ FROM entity_lineage_links
229
+ WHERE canonical_entity_id = ?
230
+ AND status = 'active'
231
+ ORDER BY created_at ASC
232
+ `)
233
+ .all(input.source_entity_id);
234
+ adapter
235
+ .prepare(`
236
+ UPDATE entity_lineage_links
237
+ SET status = 'superseded',
238
+ superseded_at = ?
239
+ WHERE canonical_entity_id = ?
240
+ AND status = 'active'
241
+ `)
242
+ .run(supersededAt, input.source_entity_id);
243
+ const adopted = [];
244
+ for (const row of sourceRows) {
245
+ const observationId = String(row.entity_observation_id);
246
+ const existing = adapter
247
+ .prepare(`
248
+ SELECT *
249
+ FROM entity_lineage_links
250
+ WHERE canonical_entity_id = ?
251
+ AND entity_observation_id = ?
252
+ AND status = 'active'
253
+ LIMIT 1
254
+ `)
255
+ .get(input.target_entity_id, observationId);
256
+ if (existing) {
257
+ adopted.push(parseEntityLineageLinkRow(existing));
258
+ continue;
259
+ }
260
+ const id = `elin_${(0, node_crypto_1.randomUUID)()}`;
261
+ adapter
262
+ .prepare(`
263
+ INSERT INTO entity_lineage_links (
264
+ id, canonical_entity_id, entity_observation_id, source_entity_id,
265
+ contribution_kind, run_id, candidate_id, review_action_id,
266
+ status, capture_mode, confidence, created_at, superseded_at
267
+ ) VALUES (?, ?, ?, ?, 'merge_adopt', ?, ?, ?, 'active', ?, ?, ?, NULL)
268
+ `)
269
+ .run(id, input.target_entity_id, observationId, input.source_entity_id, typeof row.run_id === 'string' ? row.run_id : null, input.candidate_id ?? null, input.review_action_id ?? null, input.capture_mode ?? 'direct', input.confidence ?? Number(row.confidence ?? 1), createdAt);
270
+ const created = adapter
271
+ .prepare(`SELECT * FROM entity_lineage_links WHERE id = ?`)
272
+ .get(id);
273
+ adopted.push(parseEntityLineageLinkRow(created));
274
+ }
275
+ return adopted;
276
+ }
277
+ async function listActiveEntityLineage(entityId, adapter) {
278
+ if (!adapter) {
279
+ await (0, db_manager_js_1.initDB)();
280
+ }
281
+ const effectiveAdapter = adapter ?? (0, db_manager_js_1.getAdapter)();
282
+ const rows = effectiveAdapter
283
+ .prepare(`
284
+ SELECT *
285
+ FROM entity_lineage_links
286
+ WHERE canonical_entity_id = ?
287
+ AND status = 'active'
288
+ ORDER BY created_at ASC
289
+ `)
290
+ .all(entityId);
291
+ return rows.map(parseEntityLineageLinkRow);
292
+ }
293
+ async function listEntityLineageHistory(entityId, adapter) {
294
+ if (!adapter) {
295
+ await (0, db_manager_js_1.initDB)();
296
+ }
297
+ const effectiveAdapter = adapter ?? (0, db_manager_js_1.getAdapter)();
298
+ const rows = effectiveAdapter
299
+ .prepare(`
300
+ SELECT *
301
+ FROM entity_lineage_links
302
+ WHERE canonical_entity_id = ?
303
+ ORDER BY created_at ASC
304
+ `)
305
+ .all(entityId);
306
+ return rows.map(parseEntityLineageLinkRow);
307
+ }
308
+ //# sourceMappingURL=lineage-store.js.map
@@ -0,0 +1,14 @@
1
+ export type EntityScript = 'Latn' | 'Hang' | 'Jpan' | 'Mixed' | 'Unknown';
2
+ export interface NormalizedEntityLabel {
3
+ original: string;
4
+ normalized: string;
5
+ script: EntityScript;
6
+ }
7
+ export declare function detectScript(input: string): EntityScript;
8
+ export declare function normalizeEntityLabel(input: unknown): NormalizedEntityLabel;
9
+ export declare function extractStructuredIdentifiers(input: string): {
10
+ emails: string[];
11
+ handles: string[];
12
+ domains: string[];
13
+ };
14
+ //# sourceMappingURL=normalization.d.ts.map
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.detectScript = detectScript;
4
+ exports.normalizeEntityLabel = normalizeEntityLabel;
5
+ exports.extractStructuredIdentifiers = extractStructuredIdentifiers;
6
+ const errors_js_1 = require("./errors.js");
7
+ const LATIN_RE = /\p{Script=Latin}/u;
8
+ const HANGUL_RE = /\p{Script=Hangul}/u;
9
+ const HIRAGANA_RE = /\p{Script=Hiragana}/u;
10
+ const KATAKANA_RE = /\p{Script=Katakana}/u;
11
+ const HAN_RE = /\p{Script=Han}/u;
12
+ function collapseWhitespace(input) {
13
+ return input.normalize('NFKC').trim().replace(/\s+/gu, ' ');
14
+ }
15
+ function detectScript(input) {
16
+ const hasLatin = LATIN_RE.test(input);
17
+ const hasHangul = HANGUL_RE.test(input);
18
+ const hasJapanese = HIRAGANA_RE.test(input) || KATAKANA_RE.test(input) || HAN_RE.test(input);
19
+ const activeScripts = [hasLatin, hasHangul, hasJapanese].filter(Boolean).length;
20
+ if (activeScripts > 1) {
21
+ return 'Mixed';
22
+ }
23
+ if (hasHangul) {
24
+ return 'Hang';
25
+ }
26
+ if (hasJapanese) {
27
+ return 'Jpan';
28
+ }
29
+ if (hasLatin) {
30
+ return 'Latn';
31
+ }
32
+ return 'Unknown';
33
+ }
34
+ function normalizeEntityLabel(input) {
35
+ if (typeof input !== 'string' || input.trim().length === 0) {
36
+ throw new errors_js_1.InvalidEntityLabelError({
37
+ input,
38
+ reason: typeof input === 'string' ? 'empty_string' : 'non_string_input',
39
+ });
40
+ }
41
+ const original = input;
42
+ const collapsed = collapseWhitespace(input);
43
+ const script = detectScript(collapsed);
44
+ const normalized = script === 'Latn' ? collapsed.toLowerCase() : collapsed;
45
+ return {
46
+ original,
47
+ normalized,
48
+ script,
49
+ };
50
+ }
51
+ function extractStructuredIdentifiers(input) {
52
+ const emails = Array.from(new Set((input.match(/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/giu) ?? []).map((v) => v.toLowerCase())));
53
+ const handles = Array.from(new Set((input.match(/(?<!\w)@([a-z0-9_][a-z0-9_.-]*)/giu) ?? []).map((v) => v.slice(1).toLowerCase())));
54
+ const domains = Array.from(new Set((input.match(/\b(?:[a-z0-9-]+\.)+[a-z]{2,}\b/giu) ?? [])
55
+ .map((v) => v.toLowerCase())
56
+ .filter((v) => !emails.some((email) => email.endsWith(`@${v}`)))));
57
+ return {
58
+ emails,
59
+ handles,
60
+ domains,
61
+ };
62
+ }
63
+ //# sourceMappingURL=normalization.js.map
@@ -0,0 +1,17 @@
1
+ import type { DatabaseAdapter } from '../db-manager.js';
2
+ import { createDefaultEntityPolicyBootstrap, type EntityPolicyApprovalInput, type EntityPolicyBootstrapDocument, type EntityPolicyKind, type EntityPolicyProposalInput, type EntityPolicyProposalRow, type EntityPolicyRow, type EntityRole, type EntityRoleBinding, type EntityRoleBindingInput } from './policy-types.js';
3
+ type PolicyStoreAdapter = Pick<DatabaseAdapter, 'prepare' | 'transaction'>;
4
+ export declare function ensureEntityPolicyBootstrap(args: {
5
+ bootstrapPath: string;
6
+ now?: () => number;
7
+ }): Promise<void>;
8
+ export declare function listEntityPolicies(adapter?: PolicyStoreAdapter): EntityPolicyRow[];
9
+ export declare function getEntityPolicy(policyKey: string, adapter?: PolicyStoreAdapter): EntityPolicyRow | null;
10
+ export declare function listEntityRoleBindings(adapter?: PolicyStoreAdapter): EntityRoleBinding[];
11
+ export declare function upsertEntityRoleBinding(input: EntityRoleBindingInput, adapter?: PolicyStoreAdapter): void;
12
+ export declare function resolveEntityRoleForActor(actorId: string, adapter?: PolicyStoreAdapter): EntityRole;
13
+ export declare function createEntityPolicyProposal(input: EntityPolicyProposalInput, adapter?: PolicyStoreAdapter): string;
14
+ export declare function getEntityPolicyProposal(proposalId: string, adapter?: PolicyStoreAdapter): EntityPolicyProposalRow | null;
15
+ export declare function approveEntityPolicyProposal(input: EntityPolicyApprovalInput, adapter?: PolicyStoreAdapter): EntityPolicyProposalRow;
16
+ export { createDefaultEntityPolicyBootstrap, type EntityPolicyApprovalInput, type EntityPolicyBootstrapDocument, type EntityPolicyKind, type EntityPolicyProposalInput, type EntityPolicyProposalRow, type EntityPolicyRow, type EntityRole, type EntityRoleBinding, type EntityRoleBindingInput, };
17
+ //# sourceMappingURL=policy-store.d.ts.map
@@ -0,0 +1,263 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createDefaultEntityPolicyBootstrap = void 0;
7
+ exports.ensureEntityPolicyBootstrap = ensureEntityPolicyBootstrap;
8
+ exports.listEntityPolicies = listEntityPolicies;
9
+ exports.getEntityPolicy = getEntityPolicy;
10
+ exports.listEntityRoleBindings = listEntityRoleBindings;
11
+ exports.upsertEntityRoleBinding = upsertEntityRoleBinding;
12
+ exports.resolveEntityRoleForActor = resolveEntityRoleForActor;
13
+ exports.createEntityPolicyProposal = createEntityPolicyProposal;
14
+ exports.getEntityPolicyProposal = getEntityPolicyProposal;
15
+ exports.approveEntityPolicyProposal = approveEntityPolicyProposal;
16
+ const node_crypto_1 = require("node:crypto");
17
+ const node_fs_1 = __importDefault(require("node:fs"));
18
+ const db_manager_js_1 = require("../db-manager.js");
19
+ const policy_types_js_1 = require("./policy-types.js");
20
+ Object.defineProperty(exports, "createDefaultEntityPolicyBootstrap", { enumerable: true, get: function () { return policy_types_js_1.createDefaultEntityPolicyBootstrap; } });
21
+ function now() {
22
+ return Date.now();
23
+ }
24
+ function parseJsonObject(raw, label) {
25
+ const parsed = JSON.parse(raw);
26
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
27
+ throw new Error(`${label} must be a JSON object`);
28
+ }
29
+ return parsed;
30
+ }
31
+ function parsePolicyRow(row) {
32
+ const policyKind = String(row.policy_kind);
33
+ if (!(0, policy_types_js_1.isEntityPolicyKind)(policyKind)) {
34
+ throw new Error(`Invalid entity policy row: policy_kind=${policyKind}`);
35
+ }
36
+ const valueJson = String(row.value_json);
37
+ return {
38
+ policy_key: String(row.policy_key),
39
+ policy_kind: policyKind,
40
+ value_json: valueJson,
41
+ value: parseJsonObject(valueJson, 'entity_policy.value_json'),
42
+ version: Number(row.version),
43
+ created_at: Number(row.created_at),
44
+ updated_at: Number(row.updated_at),
45
+ };
46
+ }
47
+ function parseRoleBindingRow(row) {
48
+ const role = String(row.role);
49
+ if (!(0, policy_types_js_1.isEntityRole)(role)) {
50
+ throw new Error(`Invalid entity role binding row: role=${role}`);
51
+ }
52
+ return {
53
+ actor_id: String(row.actor_id),
54
+ role,
55
+ created_at: Number(row.created_at),
56
+ updated_at: Number(row.updated_at),
57
+ };
58
+ }
59
+ function parseProposalRow(row) {
60
+ const policyKind = String(row.policy_kind);
61
+ if (!(0, policy_types_js_1.isEntityPolicyKind)(policyKind)) {
62
+ throw new Error(`Invalid entity policy proposal row: policy_kind=${policyKind}`);
63
+ }
64
+ const status = String(row.status);
65
+ if (!(0, policy_types_js_1.isEntityPolicyProposalStatus)(status)) {
66
+ throw new Error(`Invalid entity policy proposal row: status=${status}`);
67
+ }
68
+ const proposedValueJson = String(row.proposed_value_json);
69
+ return {
70
+ proposal_id: String(row.proposal_id),
71
+ policy_key: String(row.policy_key),
72
+ policy_kind: policyKind,
73
+ proposed_value_json: proposedValueJson,
74
+ proposed_value: parseJsonObject(proposedValueJson, 'entity_policy_proposals.proposed_value_json'),
75
+ proposer_actor: String(row.proposer_actor),
76
+ approver_actor: typeof row.approver_actor === 'string' ? row.approver_actor : null,
77
+ reason: String(row.reason),
78
+ status,
79
+ created_at: Number(row.created_at),
80
+ approved_at: typeof row.approved_at === 'number' ? row.approved_at : null,
81
+ };
82
+ }
83
+ function readBootstrapDocument(bootstrapPath) {
84
+ const raw = node_fs_1.default.readFileSync(bootstrapPath, 'utf-8');
85
+ const parsed = JSON.parse(raw);
86
+ if (!parsed || typeof parsed !== 'object') {
87
+ throw new Error('Entity policy bootstrap must be a JSON object');
88
+ }
89
+ if (typeof parsed.version !== 'number') {
90
+ throw new Error('Entity policy bootstrap must include numeric version');
91
+ }
92
+ if (!Array.isArray(parsed.policies)) {
93
+ throw new Error('Entity policy bootstrap must include policies[]');
94
+ }
95
+ if (!Array.isArray(parsed.role_bindings)) {
96
+ throw new Error('Entity policy bootstrap must include role_bindings[]');
97
+ }
98
+ for (const policy of parsed.policies) {
99
+ if (!policy || typeof policy !== 'object' || Array.isArray(policy)) {
100
+ throw new Error('Entity policy bootstrap policies must be objects');
101
+ }
102
+ if (typeof policy.policy_key !== 'string' ||
103
+ typeof policy.policy_kind !== 'string' ||
104
+ !(0, policy_types_js_1.isEntityPolicyKind)(policy.policy_kind) ||
105
+ !policy.value ||
106
+ typeof policy.value !== 'object' ||
107
+ Array.isArray(policy.value)) {
108
+ throw new Error('Entity policy bootstrap policy rows are invalid');
109
+ }
110
+ }
111
+ for (const binding of parsed.role_bindings) {
112
+ if (!binding || typeof binding !== 'object' || Array.isArray(binding)) {
113
+ throw new Error('Entity policy bootstrap role_bindings must be objects');
114
+ }
115
+ if (typeof binding.actor_id !== 'string' ||
116
+ typeof binding.role !== 'string' ||
117
+ !(0, policy_types_js_1.isEntityRole)(binding.role)) {
118
+ throw new Error('Entity policy bootstrap role binding rows are invalid');
119
+ }
120
+ }
121
+ return parsed;
122
+ }
123
+ function countRows(tableName, adapter) {
124
+ const row = adapter.prepare(`SELECT COUNT(*) AS total FROM ${tableName}`).get();
125
+ return Number(row.total ?? 0);
126
+ }
127
+ async function ensureEntityPolicyBootstrap(args) {
128
+ await (0, db_manager_js_1.initDB)();
129
+ const adapter = (0, db_manager_js_1.getAdapter)();
130
+ const document = readBootstrapDocument(args.bootstrapPath);
131
+ const timestamp = args.now?.() ?? now();
132
+ adapter.transaction(() => {
133
+ const policyCount = countRows('entity_policy', adapter);
134
+ const roleBindingCount = countRows('entity_role_bindings', adapter);
135
+ if ((policyCount === 0) !== (roleBindingCount === 0)) {
136
+ throw new Error('Entity policy bootstrap tables are out of sync: entity_policy and entity_role_bindings must be seeded together.');
137
+ }
138
+ if (policyCount === 0 && roleBindingCount === 0) {
139
+ for (const policy of document.policies) {
140
+ adapter
141
+ .prepare(`
142
+ INSERT INTO entity_policy (
143
+ policy_key, policy_kind, value_json, version, created_at, updated_at
144
+ ) VALUES (?, ?, ?, 1, ?, ?)
145
+ `)
146
+ .run(policy.policy_key, policy.policy_kind, JSON.stringify(policy.value), timestamp, timestamp);
147
+ }
148
+ for (const binding of document.role_bindings) {
149
+ adapter
150
+ .prepare(`
151
+ INSERT INTO entity_role_bindings (
152
+ actor_id, role, created_at, updated_at
153
+ ) VALUES (?, ?, ?, ?)
154
+ `)
155
+ .run(binding.actor_id, binding.role, timestamp, timestamp);
156
+ }
157
+ }
158
+ });
159
+ }
160
+ function listEntityPolicies(adapter = (0, db_manager_js_1.getAdapter)()) {
161
+ const rows = adapter
162
+ .prepare('SELECT * FROM entity_policy ORDER BY policy_key ASC')
163
+ .all();
164
+ return rows.map(parsePolicyRow);
165
+ }
166
+ function getEntityPolicy(policyKey, adapter = (0, db_manager_js_1.getAdapter)()) {
167
+ const row = adapter
168
+ .prepare('SELECT * FROM entity_policy WHERE policy_key = ? LIMIT 1')
169
+ .get(policyKey);
170
+ return row ? parsePolicyRow(row) : null;
171
+ }
172
+ function listEntityRoleBindings(adapter = (0, db_manager_js_1.getAdapter)()) {
173
+ const rows = adapter
174
+ .prepare('SELECT * FROM entity_role_bindings ORDER BY actor_id ASC')
175
+ .all();
176
+ return rows.map(parseRoleBindingRow);
177
+ }
178
+ function upsertEntityRoleBinding(input, adapter = (0, db_manager_js_1.getAdapter)()) {
179
+ const timestamp = now();
180
+ adapter
181
+ .prepare(`
182
+ INSERT INTO entity_role_bindings (
183
+ actor_id, role, created_at, updated_at
184
+ ) VALUES (?, ?, ?, ?)
185
+ ON CONFLICT(actor_id) DO UPDATE SET
186
+ role = excluded.role,
187
+ updated_at = excluded.updated_at
188
+ `)
189
+ .run(input.actor_id, input.role, timestamp, timestamp);
190
+ }
191
+ function resolveEntityRoleForActor(actorId, adapter = (0, db_manager_js_1.getAdapter)()) {
192
+ const row = adapter
193
+ .prepare('SELECT role FROM entity_role_bindings WHERE actor_id = ? LIMIT 1')
194
+ .get(actorId);
195
+ return row?.role && (0, policy_types_js_1.isEntityRole)(row.role) ? row.role : 'viewer';
196
+ }
197
+ function createEntityPolicyProposal(input, adapter = (0, db_manager_js_1.getAdapter)()) {
198
+ const proposalId = input.proposal_id ?? `epol_${(0, node_crypto_1.randomUUID)()}`;
199
+ const createdAt = input.created_at ?? now();
200
+ adapter
201
+ .prepare(`
202
+ INSERT INTO entity_policy_proposals (
203
+ proposal_id, policy_key, policy_kind, proposed_value_json, proposer_actor,
204
+ approver_actor, reason, status, created_at, approved_at
205
+ ) VALUES (?, ?, ?, ?, ?, NULL, ?, 'pending', ?, NULL)
206
+ `)
207
+ .run(proposalId, input.policy_key, input.policy_kind, JSON.stringify(input.proposed_value), input.proposer_actor, input.reason, createdAt);
208
+ return proposalId;
209
+ }
210
+ function getEntityPolicyProposal(proposalId, adapter = (0, db_manager_js_1.getAdapter)()) {
211
+ const row = adapter
212
+ .prepare('SELECT * FROM entity_policy_proposals WHERE proposal_id = ? LIMIT 1')
213
+ .get(proposalId);
214
+ return row ? parseProposalRow(row) : null;
215
+ }
216
+ function approveEntityPolicyProposal(input, adapter = (0, db_manager_js_1.getAdapter)()) {
217
+ const approvedAt = input.approved_at ?? now();
218
+ return adapter.transaction(() => {
219
+ const proposal = getEntityPolicyProposal(input.proposal_id, adapter);
220
+ if (!proposal) {
221
+ throw new Error(`Entity policy proposal not found: ${input.proposal_id}`);
222
+ }
223
+ if (proposal.status !== 'pending') {
224
+ throw new Error(`Entity policy proposal is not pending: ${input.proposal_id}`);
225
+ }
226
+ if (proposal.proposer_actor === input.approver_actor) {
227
+ throw new Error('Entity policy proposal requires a different approver');
228
+ }
229
+ const approverRole = resolveEntityRoleForActor(input.approver_actor, adapter);
230
+ if (approverRole !== 'admin') {
231
+ throw new Error('Entity policy proposal approval requires an admin approver');
232
+ }
233
+ const existingPolicy = getEntityPolicy(proposal.policy_key, adapter);
234
+ const version = existingPolicy ? existingPolicy.version + 1 : 1;
235
+ adapter
236
+ .prepare(`
237
+ INSERT INTO entity_policy (
238
+ policy_key, policy_kind, value_json, version, created_at, updated_at
239
+ ) VALUES (?, ?, ?, ?, ?, ?)
240
+ ON CONFLICT(policy_key) DO UPDATE SET
241
+ policy_kind = excluded.policy_kind,
242
+ value_json = excluded.value_json,
243
+ version = excluded.version,
244
+ updated_at = excluded.updated_at
245
+ `)
246
+ .run(proposal.policy_key, proposal.policy_kind, proposal.proposed_value_json, version, approvedAt, approvedAt);
247
+ adapter
248
+ .prepare(`
249
+ UPDATE entity_policy_proposals
250
+ SET approver_actor = ?,
251
+ status = 'approved',
252
+ approved_at = ?
253
+ WHERE proposal_id = ?
254
+ `)
255
+ .run(input.approver_actor, approvedAt, input.proposal_id);
256
+ const approved = getEntityPolicyProposal(input.proposal_id, adapter);
257
+ if (!approved) {
258
+ throw new Error(`Failed to reload approved entity policy proposal: ${input.proposal_id}`);
259
+ }
260
+ return approved;
261
+ });
262
+ }
263
+ //# sourceMappingURL=policy-store.js.map