@jungjaehoon/mama-core 1.4.1 → 1.5.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 (120) hide show
  1. package/db/migrations/026-create-canonical-entity-tables.sql +99 -0
  2. package/db/migrations/027-create-entity-review-support.sql +44 -0
  3. package/db/migrations/028-create-entity-audit-tables.sql +45 -0
  4. package/db/migrations/029-extend-entity-observations-observation-type.sql +60 -0
  5. package/db/migrations/030-case-first-memory-substrate.sql +901 -0
  6. package/db/migrations/031-wiki-page-index-provenance-columns.sql +2 -0
  7. package/dist/canonicalize.d.ts +38 -0
  8. package/dist/canonicalize.js +106 -0
  9. package/dist/cases/case-links.d.ts +116 -0
  10. package/dist/cases/case-links.js +794 -0
  11. package/dist/cases/composition-overrides.d.ts +90 -0
  12. package/dist/cases/composition-overrides.js +724 -0
  13. package/dist/cases/corrections.d.ts +156 -0
  14. package/dist/cases/corrections.js +1045 -0
  15. package/dist/cases/freshness.d.ts +57 -0
  16. package/dist/cases/freshness.js +283 -0
  17. package/dist/cases/live-state.d.ts +48 -0
  18. package/dist/cases/live-state.js +256 -0
  19. package/dist/cases/membership-explain.d.ts +56 -0
  20. package/dist/cases/membership-explain.js +140 -0
  21. package/dist/cases/membership-matcher.d.ts +23 -0
  22. package/dist/cases/membership-matcher.js +193 -0
  23. package/dist/cases/merge-split.d.ts +61 -0
  24. package/dist/cases/merge-split.js +368 -0
  25. package/dist/cases/role-inference.d.ts +10 -0
  26. package/dist/cases/role-inference.js +85 -0
  27. package/dist/cases/search-rollup.d.ts +23 -0
  28. package/dist/cases/search-rollup.js +225 -0
  29. package/dist/cases/sqlite-transaction.d.ts +10 -0
  30. package/dist/cases/sqlite-transaction.js +62 -0
  31. package/dist/cases/store.d.ts +74 -0
  32. package/dist/cases/store.js +816 -0
  33. package/dist/cases/target-ref.d.ts +25 -0
  34. package/dist/cases/target-ref.js +22 -0
  35. package/dist/cases/timeline-range.d.ts +6 -0
  36. package/dist/cases/timeline-range.js +322 -0
  37. package/dist/cases/tombstone-sweeper.d.ts +13 -0
  38. package/dist/cases/tombstone-sweeper.js +128 -0
  39. package/dist/cases/types.d.ts +244 -0
  40. package/dist/cases/types.js +47 -0
  41. package/dist/cases/wiki-page-index.d.ts +50 -0
  42. package/dist/cases/wiki-page-index.js +350 -0
  43. package/dist/config-loader.d.ts +1 -0
  44. package/dist/config-loader.js +43 -0
  45. package/dist/connectors/event-index.d.ts +44 -0
  46. package/dist/connectors/event-index.js +366 -0
  47. package/dist/connectors/types.d.ts +69 -0
  48. package/dist/connectors/types.js +3 -0
  49. package/dist/db-manager.d.ts +4 -0
  50. package/dist/db-manager.js +10 -3
  51. package/dist/decision-formatter.d.ts +2 -0
  52. package/dist/decision-formatter.js +11 -8
  53. package/dist/embedding-server/mobile/daemon.d.ts +1 -0
  54. package/dist/embedding-server/mobile/daemon.js +11 -1
  55. package/dist/entities/audit-metrics.d.ts +73 -0
  56. package/dist/entities/audit-metrics.js +128 -0
  57. package/dist/entities/candidate-generator.d.ts +9 -0
  58. package/dist/entities/candidate-generator.js +436 -0
  59. package/dist/entities/entity-impact.d.ts +41 -0
  60. package/dist/entities/entity-impact.js +140 -0
  61. package/dist/entities/entity-linked-decision-counts.d.ts +2 -0
  62. package/dist/entities/entity-linked-decision-counts.js +29 -0
  63. package/dist/entities/entity-list.d.ts +23 -0
  64. package/dist/entities/entity-list.js +99 -0
  65. package/dist/entities/entity-orphan-list.d.ts +26 -0
  66. package/dist/entities/entity-orphan-list.js +91 -0
  67. package/dist/entities/entity-search.d.ts +21 -0
  68. package/dist/entities/entity-search.js +150 -0
  69. package/dist/entities/errors.d.ts +65 -0
  70. package/dist/entities/errors.js +124 -0
  71. package/dist/entities/exact-merge-backfill.d.ts +11 -0
  72. package/dist/entities/exact-merge-backfill.js +161 -0
  73. package/dist/entities/lineage-backfill.d.ts +12 -0
  74. package/dist/entities/lineage-backfill.js +101 -0
  75. package/dist/entities/lineage-store.d.ts +51 -0
  76. package/dist/entities/lineage-store.js +308 -0
  77. package/dist/entities/normalization.d.ts +14 -0
  78. package/dist/entities/normalization.js +63 -0
  79. package/dist/entities/policy-store.d.ts +17 -0
  80. package/dist/entities/policy-store.js +263 -0
  81. package/dist/entities/policy-types.d.ts +72 -0
  82. package/dist/entities/policy-types.js +59 -0
  83. package/dist/entities/projection.d.ts +8 -0
  84. package/dist/entities/projection.js +82 -0
  85. package/dist/entities/provenance-query.d.ts +33 -0
  86. package/dist/entities/provenance-query.js +167 -0
  87. package/dist/entities/read-identity.d.ts +31 -0
  88. package/dist/entities/read-identity.js +264 -0
  89. package/dist/entities/recall-bridge.d.ts +5 -0
  90. package/dist/entities/recall-bridge.js +171 -0
  91. package/dist/entities/resolution-engine.d.ts +15 -0
  92. package/dist/entities/resolution-engine.js +69 -0
  93. package/dist/entities/rollback-preview.d.ts +39 -0
  94. package/dist/entities/rollback-preview.js +235 -0
  95. package/dist/entities/source-locator.d.ts +3 -0
  96. package/dist/entities/source-locator.js +32 -0
  97. package/dist/entities/store.d.ts +75 -0
  98. package/dist/entities/store.js +465 -0
  99. package/dist/entities/types.d.ts +162 -0
  100. package/dist/entities/types.js +30 -0
  101. package/dist/index.d.ts +47 -2
  102. package/dist/index.js +66 -25
  103. package/dist/mama-api.d.ts +22 -1
  104. package/dist/mama-api.js +272 -64
  105. package/dist/memory/api.d.ts +28 -1
  106. package/dist/memory/api.js +293 -31
  107. package/dist/memory/types.d.ts +11 -2
  108. package/dist/search/feedback-store.d.ts +59 -0
  109. package/dist/search/feedback-store.js +211 -0
  110. package/dist/search/question-type.d.ts +5 -0
  111. package/dist/search/question-type.js +41 -0
  112. package/dist/search/ranker-features.d.ts +23 -0
  113. package/dist/search/ranker-features.js +104 -0
  114. package/dist/search/ranker-rescore.d.ts +31 -0
  115. package/dist/search/ranker-rescore.js +226 -0
  116. package/dist/search/ranker-trainer.d.ts +94 -0
  117. package/dist/search/ranker-trainer.js +860 -0
  118. package/dist/test-utils.d.ts +14 -0
  119. package/dist/test-utils.js +52 -0
  120. package/package.json +21 -3
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.backfillExactDuplicateCanonicals = backfillExactDuplicateCanonicals;
4
+ const db_manager_js_1 = require("../db-manager.js");
5
+ const store_js_1 = require("./store.js");
6
+ const lineage_store_js_1 = require("./lineage-store.js");
7
+ function normalizeExactLabel(input) {
8
+ return input.normalize('NFKC').trim().replace(/\s+/gu, ' ').toLowerCase();
9
+ }
10
+ function buildExactGroupKey(row) {
11
+ if (row.normalized_form) {
12
+ return {
13
+ key: JSON.stringify([row.kind, row.scope_kind, row.scope_id ?? '', row.normalized_form]),
14
+ fromObservation: true,
15
+ };
16
+ }
17
+ const fallback = normalizeExactLabel(row.preferred_label);
18
+ if (!fallback) {
19
+ return null;
20
+ }
21
+ return {
22
+ key: JSON.stringify([row.kind, row.scope_kind, row.scope_id ?? '', fallback]),
23
+ fromObservation: false,
24
+ };
25
+ }
26
+ function compareRows(left, right) {
27
+ if (left.created_at !== right.created_at) {
28
+ return left.created_at - right.created_at;
29
+ }
30
+ return left.id.localeCompare(right.id);
31
+ }
32
+ function runAdapterTransaction(adapter, fn) {
33
+ const result = adapter.transaction(fn);
34
+ return typeof result === 'function' ? result() : result;
35
+ }
36
+ function requireTransactionAdapter(adapter) {
37
+ if (!('transaction' in adapter) || typeof adapter.transaction !== 'function') {
38
+ throw new Error('exact-merge-backfill requires adapter.transaction');
39
+ }
40
+ return adapter;
41
+ }
42
+ function mergeAndAdoptLineage(input) {
43
+ const mergeResult = (0, store_js_1.mergeEntityNodes)({
44
+ adapter: input.adapter,
45
+ source_id: input.source.id,
46
+ target_id: input.target.id,
47
+ actor_type: 'system',
48
+ actor_id: 'exact-duplicate-backfill',
49
+ reason: 'Exact duplicate canonical backfill',
50
+ candidate_id: null,
51
+ evidence_json: JSON.stringify({
52
+ strategy: 'exact_duplicate_backfill',
53
+ group_key: input.groupKey,
54
+ normalized_form: input.source.normalized_form,
55
+ target_id: input.target.id,
56
+ }),
57
+ });
58
+ (0, lineage_store_js_1.adoptLineageAfterMergeSync)({
59
+ adapter: input.adapter,
60
+ source_entity_id: input.source.id,
61
+ target_entity_id: input.target.id,
62
+ candidate_id: null,
63
+ review_action_id: mergeResult.merge_action_id,
64
+ });
65
+ }
66
+ async function backfillExactDuplicateCanonicals(opts = {}) {
67
+ await (0, db_manager_js_1.initDB)();
68
+ const adapter = (0, db_manager_js_1.getAdapter)();
69
+ const dryRun = opts.dryRun === true;
70
+ let groups = 0;
71
+ let merged = 0;
72
+ let incomplete = 0;
73
+ let skipped = 0;
74
+ const rows = adapter
75
+ .prepare(`
76
+ SELECT
77
+ n.id,
78
+ n.kind,
79
+ n.preferred_label,
80
+ n.scope_kind,
81
+ n.scope_id,
82
+ n.created_at,
83
+ MIN(o.normalized_form) AS normalized_form
84
+ FROM entity_nodes n
85
+ LEFT JOIN entity_lineage_links l
86
+ ON l.canonical_entity_id = n.id
87
+ AND l.status = 'active'
88
+ LEFT JOIN entity_observations o
89
+ ON o.id = l.entity_observation_id
90
+ WHERE n.status = 'active'
91
+ AND n.merged_into IS NULL
92
+ GROUP BY n.id, n.kind, n.preferred_label, n.scope_kind, n.scope_id, n.created_at
93
+ `)
94
+ .all();
95
+ const grouped = new Map();
96
+ const fallbackRows = new Set();
97
+ for (const row of rows) {
98
+ const keyInfo = buildExactGroupKey(row);
99
+ if (!keyInfo) {
100
+ incomplete += 1;
101
+ continue;
102
+ }
103
+ if (!keyInfo.fromObservation) {
104
+ fallbackRows.add(row.id);
105
+ }
106
+ const bucket = grouped.get(keyInfo.key) ?? [];
107
+ bucket.push(row);
108
+ grouped.set(keyInfo.key, bucket);
109
+ }
110
+ for (const bucket of grouped.values()) {
111
+ if (bucket.length >= 2) {
112
+ continue;
113
+ }
114
+ for (const row of bucket) {
115
+ if (fallbackRows.has(row.id)) {
116
+ incomplete += 1;
117
+ }
118
+ }
119
+ }
120
+ for (const [groupKey, bucket] of grouped.entries()) {
121
+ if (bucket.length < 2) {
122
+ continue;
123
+ }
124
+ groups += 1;
125
+ const sorted = [...bucket].sort(compareRows);
126
+ const target = sorted[0];
127
+ const sources = sorted.slice(1);
128
+ if (dryRun) {
129
+ merged += sources.length;
130
+ continue;
131
+ }
132
+ try {
133
+ const transactionalAdapter = requireTransactionAdapter(adapter);
134
+ runAdapterTransaction(transactionalAdapter, () => {
135
+ for (const source of sources) {
136
+ mergeAndAdoptLineage({
137
+ adapter,
138
+ source,
139
+ target,
140
+ groupKey,
141
+ });
142
+ }
143
+ });
144
+ merged += sources.length;
145
+ }
146
+ catch (error) {
147
+ if (error instanceof store_js_1.EntityMergeError) {
148
+ skipped += sources.length;
149
+ continue;
150
+ }
151
+ throw error;
152
+ }
153
+ }
154
+ return {
155
+ groups,
156
+ merged,
157
+ incomplete,
158
+ skipped,
159
+ };
160
+ }
161
+ //# sourceMappingURL=exact-merge-backfill.js.map
@@ -0,0 +1,12 @@
1
+ interface BackfillEntityLineageOptions {
2
+ dryRun?: boolean;
3
+ now?: number;
4
+ }
5
+ interface BackfillEntityLineageResult {
6
+ seeded: number;
7
+ adopted: number;
8
+ incomplete: number;
9
+ }
10
+ export declare function backfillEntityLineage(opts?: BackfillEntityLineageOptions): Promise<BackfillEntityLineageResult>;
11
+ export {};
12
+ //# sourceMappingURL=lineage-backfill.d.ts.map
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.backfillEntityLineage = backfillEntityLineage;
4
+ const db_manager_js_1 = require("../db-manager.js");
5
+ const lineage_store_js_1 = require("./lineage-store.js");
6
+ async function backfillEntityLineage(opts = {}) {
7
+ await (0, db_manager_js_1.initDB)();
8
+ const adapter = (0, db_manager_js_1.getAdapter)();
9
+ const dryRun = opts.dryRun === true;
10
+ const seedConfidence = 0.75;
11
+ const adoptConfidence = 0.6;
12
+ let seeded = 0;
13
+ let adopted = 0;
14
+ let incomplete = 0;
15
+ const activeRows = adapter
16
+ .prepare(`
17
+ SELECT n.id, o.id AS observation_id
18
+ FROM entity_nodes n
19
+ LEFT JOIN entity_observations o ON o.id = n.id
20
+ WHERE n.status = 'active'
21
+ AND n.merged_into IS NULL
22
+ `)
23
+ .all();
24
+ for (const row of activeRows) {
25
+ if (!row.observation_id) {
26
+ incomplete += 1;
27
+ continue;
28
+ }
29
+ const existing = adapter
30
+ .prepare(`
31
+ SELECT id
32
+ FROM entity_lineage_links
33
+ WHERE canonical_entity_id = ?
34
+ AND entity_observation_id = ?
35
+ AND status = 'active'
36
+ LIMIT 1
37
+ `)
38
+ .get(row.id, row.observation_id);
39
+ if (existing) {
40
+ continue;
41
+ }
42
+ seeded += 1;
43
+ if (!dryRun) {
44
+ await (0, lineage_store_js_1.appendEntityLineageLink)({
45
+ canonical_entity_id: row.id,
46
+ entity_observation_id: row.observation_id,
47
+ source_entity_id: null,
48
+ contribution_kind: 'seed',
49
+ run_id: null,
50
+ candidate_id: null,
51
+ review_action_id: null,
52
+ capture_mode: 'backfilled',
53
+ confidence: seedConfidence,
54
+ });
55
+ }
56
+ }
57
+ const mergedRows = adapter
58
+ .prepare(`
59
+ SELECT n.id AS source_entity_id, n.merged_into AS target_entity_id, o.id AS observation_id
60
+ FROM entity_nodes n
61
+ LEFT JOIN entity_observations o ON o.id = n.id
62
+ WHERE n.status = 'merged'
63
+ AND n.merged_into IS NOT NULL
64
+ `)
65
+ .all();
66
+ for (const row of mergedRows) {
67
+ if (!row.observation_id) {
68
+ incomplete += 1;
69
+ continue;
70
+ }
71
+ const existing = adapter
72
+ .prepare(`
73
+ SELECT id
74
+ FROM entity_lineage_links
75
+ WHERE canonical_entity_id = ?
76
+ AND entity_observation_id = ?
77
+ AND status = 'active'
78
+ LIMIT 1
79
+ `)
80
+ .get(row.target_entity_id, row.observation_id);
81
+ if (existing) {
82
+ continue;
83
+ }
84
+ adopted += 1;
85
+ if (!dryRun) {
86
+ await (0, lineage_store_js_1.appendEntityLineageLink)({
87
+ canonical_entity_id: row.target_entity_id,
88
+ entity_observation_id: row.observation_id,
89
+ source_entity_id: row.source_entity_id,
90
+ contribution_kind: 'merge_adopt',
91
+ run_id: null,
92
+ candidate_id: null,
93
+ review_action_id: null,
94
+ capture_mode: 'backfilled',
95
+ confidence: adoptConfidence,
96
+ });
97
+ }
98
+ }
99
+ return { seeded, adopted, incomplete };
100
+ }
101
+ //# sourceMappingURL=lineage-backfill.js.map
@@ -0,0 +1,51 @@
1
+ import type { DatabaseAdapter } from '../db-manager.js';
2
+ import type { EntityIngestRun, EntityLineageLink } from './types.js';
3
+ import type { EntityStoreAdapter } from './store.js';
4
+ type CreateEntityIngestRunInput = Omit<EntityIngestRun, 'status' | 'raw_count' | 'observation_count' | 'candidate_count' | 'reviewable_count' | 'audit_run_id' | 'audit_classification' | 'error_reason' | 'created_at' | 'completed_at'> & {
5
+ id?: string;
6
+ };
7
+ type AppendEntityLineageLinkInput = Omit<EntityLineageLink, 'id' | 'status' | 'created_at' | 'superseded_at'>;
8
+ interface CompleteEntityIngestRunInput {
9
+ raw_count: number;
10
+ observation_count: number;
11
+ candidate_count: number;
12
+ reviewable_count: number;
13
+ audit_run_id?: string | null;
14
+ audit_classification?: EntityIngestRun['audit_classification'];
15
+ }
16
+ interface AdoptLineageAfterMergeInput {
17
+ adapter?: LineageMutationAdapter;
18
+ source_entity_id: string;
19
+ target_entity_id: string;
20
+ candidate_id?: string | null;
21
+ review_action_id?: string | null;
22
+ confidence?: number;
23
+ capture_mode?: EntityLineageLink['capture_mode'];
24
+ }
25
+ type LineageMutationAdapter = Pick<DatabaseAdapter, 'prepare'>;
26
+ export interface AppendEntityLineageLinkResult {
27
+ link: EntityLineageLink;
28
+ created: boolean;
29
+ }
30
+ interface AdoptLineageAfterMergeSyncInput extends Omit<AdoptLineageAfterMergeInput, 'adapter'> {
31
+ adapter: LineageMutationAdapter;
32
+ }
33
+ export declare function parseEntityIngestRunRow(row: Record<string, unknown>): EntityIngestRun;
34
+ export declare function createEntityIngestRun(input: CreateEntityIngestRunInput): Promise<EntityIngestRun>;
35
+ export declare function completeEntityIngestRun(id: string, input: CompleteEntityIngestRunInput): Promise<EntityIngestRun>;
36
+ export declare function failEntityIngestRun(id: string, errorReason: string): Promise<EntityIngestRun>;
37
+ export declare function appendEntityLineageLink(input: AppendEntityLineageLinkInput): Promise<AppendEntityLineageLinkResult>;
38
+ export declare function supersedeEntityLineageForEntity(entityId: string): Promise<number>;
39
+ export declare function seedLineageForEntityMaterialization(input: {
40
+ canonical_entity_id: string;
41
+ entity_observation_id: string;
42
+ run_id?: string | null;
43
+ confidence?: number;
44
+ capture_mode?: EntityLineageLink['capture_mode'];
45
+ }): Promise<EntityLineageLink>;
46
+ export declare function adoptLineageAfterMerge(input: AdoptLineageAfterMergeInput): Promise<EntityLineageLink[]>;
47
+ export declare function adoptLineageAfterMergeSync(input: AdoptLineageAfterMergeSyncInput): EntityLineageLink[];
48
+ export declare function listActiveEntityLineage(entityId: string, adapter?: EntityStoreAdapter): Promise<EntityLineageLink[]>;
49
+ export declare function listEntityLineageHistory(entityId: string, adapter?: EntityStoreAdapter): Promise<EntityLineageLink[]>;
50
+ export {};
51
+ //# sourceMappingURL=lineage-store.d.ts.map
@@ -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