@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,140 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getEntityInspectorDetail = getEntityInspectorDetail;
4
+ exports.listEntityLineageForInspector = listEntityLineageForInspector;
5
+ exports.getEntityImpact = getEntityImpact;
6
+ exports.getEntityIngestRun = getEntityIngestRun;
7
+ const db_manager_js_1 = require("../db-manager.js");
8
+ const source_locator_js_1 = require("./source-locator.js");
9
+ const lineage_store_js_1 = require("./lineage-store.js");
10
+ function mapEntityNode(row) {
11
+ return {
12
+ id: String(row.id),
13
+ kind: row.kind,
14
+ preferred_label: String(row.preferred_label),
15
+ status: row.status,
16
+ scope_kind: typeof row.scope_kind === 'string' ? row.scope_kind : null,
17
+ scope_id: typeof row.scope_id === 'string' ? row.scope_id : null,
18
+ merged_into: typeof row.merged_into === 'string' ? row.merged_into : null,
19
+ created_at: Number(row.created_at),
20
+ updated_at: Number(row.updated_at),
21
+ };
22
+ }
23
+ async function getEntityInspectorDetail(entityId) {
24
+ await (0, db_manager_js_1.initDB)();
25
+ const adapter = (0, db_manager_js_1.getAdapter)();
26
+ const row = adapter.prepare(`SELECT * FROM entity_nodes WHERE id = ?`).get(entityId);
27
+ if (!row) {
28
+ throw new Error(`Entity not found: ${entityId}`);
29
+ }
30
+ const lineageCount = adapter
31
+ .prepare(`
32
+ SELECT COUNT(*) AS total
33
+ FROM entity_lineage_links
34
+ WHERE canonical_entity_id = ?
35
+ AND status = 'active'
36
+ `)
37
+ .get(entityId);
38
+ return {
39
+ entity: mapEntityNode(row),
40
+ history_incomplete: lineageCount.total === 0,
41
+ };
42
+ }
43
+ async function listEntityLineageForInspector(entityId, limit = 50) {
44
+ await (0, db_manager_js_1.initDB)();
45
+ const adapter = (0, db_manager_js_1.getAdapter)();
46
+ const safeLimit = Math.max(1, Math.min(limit, 500));
47
+ const rows = adapter
48
+ .prepare(`
49
+ SELECT
50
+ l.*,
51
+ o.source_connector,
52
+ o.source_raw_record_id,
53
+ o.source_locator,
54
+ o.surface_form
55
+ FROM entity_lineage_links l
56
+ JOIN entity_observations o ON o.id = l.entity_observation_id
57
+ WHERE l.canonical_entity_id = ?
58
+ AND l.status = 'active'
59
+ ORDER BY l.created_at ASC
60
+ LIMIT ?
61
+ `)
62
+ .all(entityId, safeLimit);
63
+ return {
64
+ rows: rows.map((row) => ({
65
+ id: String(row.id),
66
+ canonical_entity_id: String(row.canonical_entity_id),
67
+ entity_observation_id: String(row.entity_observation_id),
68
+ source_entity_id: typeof row.source_entity_id === 'string' ? row.source_entity_id : null,
69
+ contribution_kind: row.contribution_kind,
70
+ run_id: typeof row.run_id === 'string' ? row.run_id : null,
71
+ candidate_id: typeof row.candidate_id === 'string' ? row.candidate_id : null,
72
+ review_action_id: typeof row.review_action_id === 'string' ? row.review_action_id : null,
73
+ status: row.status,
74
+ capture_mode: row.capture_mode,
75
+ confidence: Number(row.confidence),
76
+ created_at: Number(row.created_at),
77
+ superseded_at: typeof row.superseded_at === 'number' ? row.superseded_at : null,
78
+ source_connector: String(row.source_connector),
79
+ source_raw_record_id: String(row.source_raw_record_id),
80
+ source_locator: typeof row.source_locator === 'string' ? row.source_locator : null,
81
+ source_locator_kind: (0, source_locator_js_1.detectSourceLocatorKind)(typeof row.source_locator === 'string' ? row.source_locator : null),
82
+ surface_form: String(row.surface_form),
83
+ })),
84
+ history_incomplete: rows.length === 0,
85
+ };
86
+ }
87
+ async function getEntityImpact(entityId) {
88
+ await (0, db_manager_js_1.initDB)();
89
+ const adapter = (0, db_manager_js_1.getAdapter)();
90
+ const relatedMemories = adapter
91
+ .prepare(`
92
+ SELECT DISTINCT d.id, d.topic, d.summary, d.created_at
93
+ FROM entity_lineage_links l
94
+ JOIN decision_entity_sources des ON des.entity_observation_id = l.entity_observation_id
95
+ JOIN decisions d ON d.id = des.decision_id
96
+ WHERE l.canonical_entity_id = ?
97
+ AND l.status = 'active'
98
+ ORDER BY d.created_at DESC
99
+ `)
100
+ .all(entityId);
101
+ const ingestRuns = adapter
102
+ .prepare(`
103
+ SELECT DISTINCT ir.*
104
+ FROM entity_lineage_links l
105
+ JOIN entity_ingest_runs ir ON ir.id = l.run_id
106
+ WHERE l.canonical_entity_id = ?
107
+ AND l.status = 'active'
108
+ ORDER BY ir.created_at DESC
109
+ `)
110
+ .all(entityId);
111
+ const auditRuns = adapter
112
+ .prepare(`
113
+ SELECT DISTINCT ar.id, ar.classification, ar.status, ar.created_at, ar.completed_at
114
+ FROM entity_lineage_links l
115
+ JOIN entity_ingest_runs ir ON ir.id = l.run_id
116
+ JOIN entity_audit_runs ar ON ar.id = ir.audit_run_id
117
+ WHERE l.canonical_entity_id = ?
118
+ AND l.status = 'active'
119
+ ORDER BY ar.created_at DESC
120
+ LIMIT 10
121
+ `)
122
+ .all(entityId);
123
+ return {
124
+ related_memories: relatedMemories.map((row) => ({
125
+ id: row.id,
126
+ topic: row.topic,
127
+ summary: row.summary ?? row.topic,
128
+ created_at: row.created_at,
129
+ })),
130
+ ingest_runs: ingestRuns.map(lineage_store_js_1.parseEntityIngestRunRow),
131
+ audit_runs: auditRuns,
132
+ };
133
+ }
134
+ async function getEntityIngestRun(runId) {
135
+ await (0, db_manager_js_1.initDB)();
136
+ const adapter = (0, db_manager_js_1.getAdapter)();
137
+ const row = adapter.prepare(`SELECT * FROM entity_ingest_runs WHERE id = ?`).get(runId);
138
+ return row ? (0, lineage_store_js_1.parseEntityIngestRunRow)(row) : null;
139
+ }
140
+ //# sourceMappingURL=entity-impact.js.map
@@ -0,0 +1,2 @@
1
+ export function loadLinkedDecisionCounts(adapter: any, entityIds: any): Map<any, any>;
2
+ //# sourceMappingURL=entity-linked-decision-counts.d.ts.map
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loadLinkedDecisionCounts = loadLinkedDecisionCounts;
4
+ function loadLinkedDecisionCounts(adapter, entityIds) {
5
+ const uniqueIds = Array.from(new Set(entityIds.filter((entityId) => typeof entityId === 'string' && entityId.length > 0)));
6
+ if (uniqueIds.length === 0) {
7
+ return new Map();
8
+ }
9
+ const placeholders = uniqueIds.map(() => '?').join(', ');
10
+ const rows = adapter
11
+ .prepare(`
12
+ SELECT
13
+ l.canonical_entity_id AS entity_id,
14
+ COUNT(DISTINCT des.decision_id) AS linked_decision_count
15
+ FROM entity_lineage_links l
16
+ INNER JOIN decision_entity_sources des
17
+ ON des.entity_observation_id = l.entity_observation_id
18
+ WHERE l.status = 'active'
19
+ AND l.canonical_entity_id IN (${placeholders})
20
+ GROUP BY l.canonical_entity_id
21
+ `)
22
+ .all(...uniqueIds);
23
+ const counts = new Map(uniqueIds.map((entityId) => [entityId, 0]));
24
+ for (const row of rows) {
25
+ counts.set(row.entity_id, Number(row.linked_decision_count) || 0);
26
+ }
27
+ return counts;
28
+ }
29
+ //# sourceMappingURL=entity-linked-decision-counts.js.map
@@ -0,0 +1,23 @@
1
+ import type { EntityNode } from './types.js';
2
+ export interface ListCanonicalEntitiesInput {
3
+ limit: number;
4
+ cursor?: string | null;
5
+ include_noisy?: boolean;
6
+ }
7
+ export interface CanonicalEntityListRow {
8
+ id: string;
9
+ kind: EntityNode['kind'];
10
+ preferred_label: string;
11
+ scope_kind: EntityNode['scope_kind'];
12
+ scope_id: string | null;
13
+ created_at: number;
14
+ linked_decision_count: number;
15
+ }
16
+ export interface ListCanonicalEntitiesResult {
17
+ entities: CanonicalEntityListRow[];
18
+ next_cursor: string | null;
19
+ total_count: number;
20
+ visible_count: number;
21
+ }
22
+ export declare function listCanonicalEntities(input: ListCanonicalEntitiesInput): Promise<ListCanonicalEntitiesResult>;
23
+ //# sourceMappingURL=entity-list.d.ts.map
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listCanonicalEntities = listCanonicalEntities;
4
+ const db_manager_js_1 = require("../db-manager.js");
5
+ const entity_linked_decision_counts_js_1 = require("./entity-linked-decision-counts.js");
6
+ function normalizeBrowseLabel(input) {
7
+ return input.normalize('NFKC').trim().replace(/\s+/gu, ' ').toLowerCase();
8
+ }
9
+ function buildBrowseCollapseKey(row) {
10
+ return JSON.stringify([
11
+ row.kind,
12
+ row.scope_kind,
13
+ row.scope_id ?? '',
14
+ normalizeBrowseLabel(row.preferred_label),
15
+ ]);
16
+ }
17
+ function compareCanonicalRows(left, right) {
18
+ if (right.created_at !== left.created_at) {
19
+ return right.created_at - left.created_at;
20
+ }
21
+ const labelCompare = left.preferred_label.localeCompare(right.preferred_label);
22
+ if (labelCompare !== 0) {
23
+ return labelCompare;
24
+ }
25
+ return left.id.localeCompare(right.id);
26
+ }
27
+ function decodeCursor(cursor) {
28
+ if (!cursor) {
29
+ return 0;
30
+ }
31
+ try {
32
+ const decoded = Number(Buffer.from(cursor, 'base64').toString('utf8'));
33
+ return Number.isFinite(decoded) && decoded >= 0 ? decoded : 0;
34
+ }
35
+ catch {
36
+ return 0;
37
+ }
38
+ }
39
+ function encodeCursor(offset) {
40
+ return Buffer.from(String(offset), 'utf8').toString('base64');
41
+ }
42
+ function isNoisyCanonicalEntity(row) {
43
+ const label = row.preferred_label.trim().toLowerCase();
44
+ if (row.kind === 'person') {
45
+ return label === 'user' || label === 'claude';
46
+ }
47
+ if (row.kind === 'project') {
48
+ return label === 'workspace';
49
+ }
50
+ return false;
51
+ }
52
+ async function listCanonicalEntities(input) {
53
+ await (0, db_manager_js_1.initDB)();
54
+ const adapter = (0, db_manager_js_1.getAdapter)();
55
+ const rows = adapter
56
+ .prepare(`
57
+ SELECT id, kind, preferred_label, scope_kind, scope_id, created_at
58
+ FROM entity_nodes
59
+ WHERE status = 'active'
60
+ AND merged_into IS NULL
61
+ `)
62
+ .all();
63
+ const sorted = rows.sort(compareCanonicalRows);
64
+ const collapsedByKey = new Map();
65
+ for (const row of sorted) {
66
+ const key = buildBrowseCollapseKey(row);
67
+ const existing = collapsedByKey.get(key);
68
+ if (!existing) {
69
+ collapsedByKey.set(key, row);
70
+ continue;
71
+ }
72
+ if (row.created_at > existing.created_at ||
73
+ (row.created_at === existing.created_at && row.id.localeCompare(existing.id) < 0)) {
74
+ collapsedByKey.set(key, row);
75
+ }
76
+ }
77
+ const collapsed = Array.from(collapsedByKey.values()).sort(compareCanonicalRows);
78
+ const totalCount = sorted.length;
79
+ const visibleBase = input.include_noisy === false
80
+ ? collapsed.filter((row) => !isNoisyCanonicalEntity(row))
81
+ : collapsed;
82
+ const offset = decodeCursor(input.cursor);
83
+ const limit = Math.max(1, input.limit);
84
+ const page = visibleBase.slice(offset, offset + limit);
85
+ const linkedCounts = (0, entity_linked_decision_counts_js_1.loadLinkedDecisionCounts)(adapter, page.map((row) => row.id));
86
+ const entities = page.map((row) => ({
87
+ ...row,
88
+ linked_decision_count: linkedCounts.get(row.id) ?? 0,
89
+ }));
90
+ const visibleCount = visibleBase.length;
91
+ const nextCursor = offset + limit < visibleBase.length ? encodeCursor(offset + limit) : null;
92
+ return {
93
+ entities,
94
+ next_cursor: nextCursor,
95
+ total_count: totalCount,
96
+ visible_count: visibleCount,
97
+ };
98
+ }
99
+ //# sourceMappingURL=entity-list.js.map
@@ -0,0 +1,26 @@
1
+ import type { EntityNode } from './types.js';
2
+ export interface CanonicalEntityOrphanRow {
3
+ entity_id: string;
4
+ title: string;
5
+ kind: EntityNode['kind'];
6
+ scope_kind: EntityNode['scope_kind'];
7
+ scope_id: string | null;
8
+ scope_label: string;
9
+ created_at: number;
10
+ linked_decision_count: number;
11
+ evidence_summary: {
12
+ lineage_rows: number;
13
+ raw_evidence_rows: number;
14
+ last_seen_at: number | null;
15
+ };
16
+ }
17
+ interface ReadAdapter {
18
+ prepare(sql: string): {
19
+ all: (...params: unknown[]) => unknown[];
20
+ };
21
+ }
22
+ export declare function listCanonicalEntityOrphans(adapter?: ReadAdapter, options?: {
23
+ min_age_ms?: number | null;
24
+ }): CanonicalEntityOrphanRow[];
25
+ export {};
26
+ //# sourceMappingURL=entity-orphan-list.d.ts.map
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listCanonicalEntityOrphans = listCanonicalEntityOrphans;
4
+ const db_manager_js_1 = require("../db-manager.js");
5
+ const entity_linked_decision_counts_js_1 = require("./entity-linked-decision-counts.js");
6
+ function buildScopeLabel(scopeKind, scopeId) {
7
+ return scopeKind ? `${scopeKind}:${scopeId ?? ''}` : 'global';
8
+ }
9
+ function isNoisyCanonicalEntity(row) {
10
+ const label = row.title.trim().toLowerCase();
11
+ if (row.kind === 'person') {
12
+ return label === 'user' || label === 'claude';
13
+ }
14
+ if (row.kind === 'project') {
15
+ return label === 'workspace';
16
+ }
17
+ return false;
18
+ }
19
+ function compareOrphanRows(left, right) {
20
+ const leftSeen = typeof left.evidence_summary.last_seen_at === 'number' ? left.evidence_summary.last_seen_at : 0;
21
+ const rightSeen = typeof right.evidence_summary.last_seen_at === 'number'
22
+ ? right.evidence_summary.last_seen_at
23
+ : 0;
24
+ if (rightSeen !== leftSeen) {
25
+ return rightSeen - leftSeen;
26
+ }
27
+ return left.entity_id.localeCompare(right.entity_id);
28
+ }
29
+ function listCanonicalEntityOrphans(adapter = (0, db_manager_js_1.getAdapter)(), options = {}) {
30
+ const minAgeMs = typeof options.min_age_ms === 'number' && Number.isFinite(options.min_age_ms)
31
+ ? Math.max(0, options.min_age_ms)
32
+ : 0;
33
+ const minCreatedAt = Date.now() - minAgeMs;
34
+ const rows = adapter
35
+ .prepare(`
36
+ SELECT
37
+ n.id AS entity_id,
38
+ n.kind AS kind,
39
+ n.preferred_label AS title,
40
+ n.scope_kind AS scope_kind,
41
+ n.scope_id AS scope_id,
42
+ n.created_at AS created_at,
43
+ COUNT(DISTINCT l.entity_observation_id) AS lineage_rows,
44
+ COUNT(
45
+ DISTINCT CASE
46
+ WHEN COALESCE(o.source_raw_record_id, '') <> '' THEN l.entity_observation_id
47
+ ELSE NULL
48
+ END
49
+ ) AS raw_evidence_rows,
50
+ MAX(COALESCE(o.timestamp_observed, o.created_at)) AS last_seen_at
51
+ FROM entity_nodes n
52
+ INNER JOIN entity_lineage_links l
53
+ ON l.canonical_entity_id = n.id
54
+ AND l.status = 'active'
55
+ INNER JOIN entity_observations o
56
+ ON o.id = l.entity_observation_id
57
+ WHERE n.status = 'active'
58
+ AND n.merged_into IS NULL
59
+ AND n.created_at <= ?
60
+ GROUP BY n.id, n.kind, n.preferred_label, n.scope_kind, n.scope_id, n.created_at
61
+ `)
62
+ .all(minCreatedAt);
63
+ const orphanRows = rows.map((row) => ({
64
+ entity_id: row.entity_id,
65
+ title: row.title,
66
+ kind: row.kind,
67
+ scope_kind: row.scope_kind,
68
+ scope_id: row.scope_id,
69
+ scope_label: buildScopeLabel(row.scope_kind, row.scope_id),
70
+ created_at: Number(row.created_at),
71
+ linked_decision_count: 0,
72
+ evidence_summary: {
73
+ lineage_rows: Number(row.lineage_rows ?? 0),
74
+ raw_evidence_rows: Number(row.raw_evidence_rows ?? 0),
75
+ last_seen_at: row.last_seen_at === null || row.last_seen_at === undefined
76
+ ? null
77
+ : Number(row.last_seen_at),
78
+ },
79
+ }));
80
+ const linkedCounts = (0, entity_linked_decision_counts_js_1.loadLinkedDecisionCounts)(adapter, orphanRows.map((row) => row.entity_id));
81
+ return orphanRows
82
+ .map((row) => ({
83
+ ...row,
84
+ linked_decision_count: linkedCounts.get(row.entity_id) ?? 0,
85
+ }))
86
+ .filter((row) => row.evidence_summary.lineage_rows > 0 &&
87
+ row.linked_decision_count === 0 &&
88
+ !isNoisyCanonicalEntity(row))
89
+ .sort(compareOrphanRows);
90
+ }
91
+ //# sourceMappingURL=entity-orphan-list.js.map
@@ -0,0 +1,21 @@
1
+ import type { EntityNode } from './types.js';
2
+ export interface SearchCanonicalEntitiesInput {
3
+ query: string;
4
+ limit: number;
5
+ cursor?: string | null;
6
+ }
7
+ export interface EntitySearchRow {
8
+ id: string;
9
+ kind: EntityNode['kind'];
10
+ preferred_label: string;
11
+ scope_kind: EntityNode['scope_kind'];
12
+ scope_id: string | null;
13
+ score: number;
14
+ linked_decision_count: number;
15
+ }
16
+ export interface SearchCanonicalEntitiesResult {
17
+ entities: EntitySearchRow[];
18
+ next_cursor: string | null;
19
+ }
20
+ export declare function searchCanonicalEntities(input: SearchCanonicalEntitiesInput): Promise<SearchCanonicalEntitiesResult>;
21
+ //# sourceMappingURL=entity-search.d.ts.map
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.searchCanonicalEntities = searchCanonicalEntities;
4
+ const db_manager_js_1 = require("../db-manager.js");
5
+ const entity_linked_decision_counts_js_1 = require("./entity-linked-decision-counts.js");
6
+ const store_js_1 = require("./store.js");
7
+ function normalizeSearchLabel(input) {
8
+ return input.normalize('NFKC').trim().replace(/\s+/gu, ' ').toLowerCase();
9
+ }
10
+ function buildSearchCollapseKey(row) {
11
+ return JSON.stringify([
12
+ row.kind,
13
+ row.scope_kind,
14
+ row.scope_id ?? '',
15
+ normalizeSearchLabel(row.preferred_label),
16
+ ]);
17
+ }
18
+ function escapeLikeQuery(query) {
19
+ return query.replace(/[\\%_]/g, '\\$&');
20
+ }
21
+ function decodeCursor(cursor) {
22
+ if (!cursor) {
23
+ return 0;
24
+ }
25
+ try {
26
+ const decoded = Number(Buffer.from(cursor, 'base64').toString('utf8'));
27
+ return Number.isFinite(decoded) && decoded >= 0 ? decoded : 0;
28
+ }
29
+ catch {
30
+ return 0;
31
+ }
32
+ }
33
+ function encodeCursor(offset) {
34
+ return Buffer.from(String(offset), 'utf8').toString('base64');
35
+ }
36
+ async function searchCanonicalEntities(input) {
37
+ await (0, db_manager_js_1.initDB)();
38
+ const adapter = (0, db_manager_js_1.getAdapter)();
39
+ const query = input.query.trim();
40
+ if (!query) {
41
+ return { entities: [], next_cursor: null };
42
+ }
43
+ const escaped = escapeLikeQuery(query);
44
+ const normalizedQuery = normalizeSearchLabel(query);
45
+ const rows = adapter
46
+ .prepare(`
47
+ SELECT
48
+ n.id AS matched_id,
49
+ n.preferred_label,
50
+ n.kind,
51
+ n.scope_kind,
52
+ n.scope_id,
53
+ a.label AS alias_label,
54
+ o.surface_form AS observation_surface_form
55
+ FROM entity_nodes n
56
+ LEFT JOIN entity_aliases a ON a.entity_id = n.id
57
+ LEFT JOIN entity_lineage_links l ON l.canonical_entity_id = n.id AND l.status = 'active'
58
+ LEFT JOIN entity_observations o ON o.id = l.entity_observation_id
59
+ WHERE (
60
+ lower(n.preferred_label) LIKE '%' || lower(?) || '%' ESCAPE '\\'
61
+ OR lower(COALESCE(a.label, '')) LIKE '%' || lower(?) || '%' ESCAPE '\\'
62
+ OR lower(COALESCE(o.surface_form, '')) LIKE '%' || lower(?) || '%' ESCAPE '\\'
63
+ )
64
+ `)
65
+ .all(escaped, escaped, escaped);
66
+ const canonicalIdByMatchedId = new Map();
67
+ for (const matchedId of new Set(rows.map((row) => row.matched_id))) {
68
+ try {
69
+ canonicalIdByMatchedId.set(matchedId, (0, store_js_1.resolveCanonicalEntityId)(adapter, matchedId));
70
+ }
71
+ catch (error) {
72
+ if (error instanceof store_js_1.EntityMergeError) {
73
+ continue;
74
+ }
75
+ throw error;
76
+ }
77
+ }
78
+ const canonicalIds = Array.from(new Set(canonicalIdByMatchedId.values()));
79
+ const canonicalLookup = new Map();
80
+ if (canonicalIds.length > 0) {
81
+ const fetched = adapter
82
+ .prepare(`
83
+ SELECT id, kind, preferred_label, scope_kind, scope_id
84
+ FROM entity_nodes
85
+ WHERE id IN (${canonicalIds.map(() => '?').join(', ')})
86
+ AND status = 'active'
87
+ AND merged_into IS NULL
88
+ `)
89
+ .all(...canonicalIds);
90
+ for (const row of fetched) {
91
+ canonicalLookup.set(row.id, row);
92
+ }
93
+ }
94
+ const canonicalRows = new Map();
95
+ for (const row of rows) {
96
+ const canonicalId = canonicalIdByMatchedId.get(row.matched_id);
97
+ const canonical = canonicalId ? canonicalLookup.get(canonicalId) : undefined;
98
+ if (!canonical) {
99
+ continue;
100
+ }
101
+ const score = normalizeSearchLabel(row.preferred_label).includes(normalizedQuery)
102
+ ? 3
103
+ : row.alias_label && normalizeSearchLabel(row.alias_label).includes(normalizedQuery)
104
+ ? 2
105
+ : 1;
106
+ const existing = canonicalRows.get(canonical.id);
107
+ if (!existing || score > existing.score) {
108
+ canonicalRows.set(canonical.id, {
109
+ id: canonical.id,
110
+ kind: canonical.kind,
111
+ preferred_label: canonical.preferred_label,
112
+ scope_kind: canonical.scope_kind,
113
+ scope_id: canonical.scope_id,
114
+ score,
115
+ linked_decision_count: 0,
116
+ });
117
+ }
118
+ }
119
+ const collapsedRows = new Map();
120
+ for (const row of canonicalRows.values()) {
121
+ const key = buildSearchCollapseKey(row);
122
+ const existing = collapsedRows.get(key);
123
+ if (!existing ||
124
+ row.score > existing.score ||
125
+ (row.score === existing.score && row.id.localeCompare(existing.id) < 0)) {
126
+ collapsedRows.set(key, row);
127
+ }
128
+ }
129
+ const sorted = Array.from(collapsedRows.values()).sort((left, right) => {
130
+ if (right.score !== left.score) {
131
+ return right.score - left.score;
132
+ }
133
+ const labelCompare = left.preferred_label.localeCompare(right.preferred_label);
134
+ if (labelCompare !== 0) {
135
+ return labelCompare;
136
+ }
137
+ return left.id.localeCompare(right.id);
138
+ });
139
+ const offset = decodeCursor(input.cursor);
140
+ const limit = Math.max(1, input.limit);
141
+ const page = sorted.slice(offset, offset + limit);
142
+ const linkedCounts = (0, entity_linked_decision_counts_js_1.loadLinkedDecisionCounts)(adapter, page.map((row) => row.id));
143
+ const entities = page.map((row) => ({
144
+ ...row,
145
+ linked_decision_count: linkedCounts.get(row.id) ?? 0,
146
+ }));
147
+ const next_cursor = offset + limit < sorted.length ? encodeCursor(offset + limit) : null;
148
+ return { entities, next_cursor };
149
+ }
150
+ //# sourceMappingURL=entity-search.js.map
@@ -0,0 +1,65 @@
1
+ export interface EntityErrorEnvelope {
2
+ error: {
3
+ code: string;
4
+ message: string;
5
+ hint: string;
6
+ doc_url: string;
7
+ };
8
+ }
9
+ export declare abstract class EntityError extends Error {
10
+ abstract readonly code: string;
11
+ abstract readonly doc_section: string;
12
+ readonly context: Record<string, unknown>;
13
+ readonly hint: string;
14
+ constructor(opts: {
15
+ message: string;
16
+ context?: Record<string, unknown>;
17
+ hint: string;
18
+ });
19
+ toErrorEnvelope(): EntityErrorEnvelope;
20
+ }
21
+ export declare class InvalidEntityLabelError extends EntityError {
22
+ readonly code = "entity.invalid_label";
23
+ readonly doc_section = "#invalid-label";
24
+ constructor(context?: Record<string, unknown>);
25
+ }
26
+ export declare class EmbeddingUnavailableError extends EntityError {
27
+ readonly code = "entity.embedding_unavailable";
28
+ readonly doc_section = "#embedding-unavailable";
29
+ constructor(context?: Record<string, unknown>);
30
+ }
31
+ export declare class OntologyViolationError extends EntityError {
32
+ readonly code = "entity.ontology_violation";
33
+ readonly doc_section = "#ontology-violation";
34
+ constructor(context?: Record<string, unknown>);
35
+ }
36
+ export declare class MergeTargetStaleError extends EntityError {
37
+ readonly code = "entity.merge_target_stale";
38
+ readonly doc_section = "#merge-target-stale";
39
+ constructor(context?: Record<string, unknown>);
40
+ }
41
+ export declare class EntityLabelMissingError extends EntityError {
42
+ readonly code = "entity.label_missing";
43
+ readonly doc_section = "#label-missing";
44
+ constructor(context?: Record<string, unknown>);
45
+ }
46
+ export declare class CandidateStaleError extends EntityError {
47
+ readonly code = "entity.candidate_stale";
48
+ readonly doc_section = "#candidate-stale";
49
+ constructor(context?: Record<string, unknown>);
50
+ }
51
+ export declare class AuditRunInProgressError extends EntityError {
52
+ readonly code = "entity.audit_run_in_progress";
53
+ readonly doc_section = "#audit-run-in-progress";
54
+ constructor(context?: Record<string, unknown>);
55
+ }
56
+ export declare class CaseMergeChainCycleError extends EntityError {
57
+ readonly code = "case.merge_chain_cycle";
58
+ readonly doc_section = "#case-merge-chain-cycle";
59
+ constructor(context: {
60
+ case_id: string;
61
+ chain: string[];
62
+ detected_at_depth: number;
63
+ });
64
+ }
65
+ //# sourceMappingURL=errors.d.ts.map