@jungjaehoon/mama-core 1.4.2 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -10
- package/db/migrations/026-create-canonical-entity-tables.sql +99 -0
- package/db/migrations/027-create-entity-review-support.sql +44 -0
- package/db/migrations/028-create-entity-audit-tables.sql +45 -0
- package/db/migrations/029-extend-entity-observations-observation-type.sql +60 -0
- package/db/migrations/030-case-first-memory-substrate.sql +901 -0
- package/db/migrations/031-wiki-page-index-provenance-columns.sql +2 -0
- package/db/migrations/032-add-memory-provenance-columns.sql +21 -0
- package/db/migrations/033-create-model-runs-and-tool-traces.sql +53 -0
- package/db/migrations/034-add-connector-event-scope-columns.sql +14 -0
- package/db/migrations/035-create-twin-edges.sql +63 -0
- package/db/migrations/036-create-agent-situation-packets.sql +61 -0
- package/dist/agent-graph/alias-write.d.ts +3 -0
- package/dist/agent-graph/alias-write.js +231 -0
- package/dist/agent-graph/entity-resolve.d.ts +3 -0
- package/dist/agent-graph/entity-resolve.js +275 -0
- package/dist/agent-graph/errors.d.ts +4 -0
- package/dist/agent-graph/errors.js +11 -0
- package/dist/agent-graph/graph-query.d.ts +5 -0
- package/dist/agent-graph/graph-query.js +359 -0
- package/dist/agent-graph/index.d.ts +6 -0
- package/dist/agent-graph/index.js +22 -0
- package/dist/agent-graph/types.d.ts +162 -0
- package/dist/agent-graph/types.js +3 -0
- package/dist/agent-situation/builder.d.ts +8 -0
- package/dist/agent-situation/builder.js +239 -0
- package/dist/agent-situation/cache-key.d.ts +4 -0
- package/dist/agent-situation/cache-key.js +64 -0
- package/dist/agent-situation/index.d.ts +7 -0
- package/dist/agent-situation/index.js +28 -0
- package/dist/agent-situation/packet-store.d.ts +27 -0
- package/dist/agent-situation/packet-store.js +228 -0
- package/dist/agent-situation/ranking-policy.d.ts +18 -0
- package/dist/agent-situation/ranking-policy.js +75 -0
- package/dist/agent-situation/source-readers.d.ts +52 -0
- package/dist/agent-situation/source-readers.js +300 -0
- package/dist/agent-situation/types.d.ts +195 -0
- package/dist/agent-situation/types.js +3 -0
- package/dist/canonicalize.d.ts +38 -0
- package/dist/canonicalize.js +106 -0
- package/dist/cases/case-links.d.ts +116 -0
- package/dist/cases/case-links.js +794 -0
- package/dist/cases/composition-overrides.d.ts +90 -0
- package/dist/cases/composition-overrides.js +724 -0
- package/dist/cases/corrections.d.ts +156 -0
- package/dist/cases/corrections.js +1045 -0
- package/dist/cases/freshness.d.ts +57 -0
- package/dist/cases/freshness.js +283 -0
- package/dist/cases/live-state.d.ts +48 -0
- package/dist/cases/live-state.js +256 -0
- package/dist/cases/membership-explain.d.ts +56 -0
- package/dist/cases/membership-explain.js +140 -0
- package/dist/cases/membership-matcher.d.ts +23 -0
- package/dist/cases/membership-matcher.js +193 -0
- package/dist/cases/merge-split.d.ts +61 -0
- package/dist/cases/merge-split.js +368 -0
- package/dist/cases/role-inference.d.ts +10 -0
- package/dist/cases/role-inference.js +85 -0
- package/dist/cases/search-rollup.d.ts +27 -0
- package/dist/cases/search-rollup.js +309 -0
- package/dist/cases/sqlite-transaction.d.ts +10 -0
- package/dist/cases/sqlite-transaction.js +62 -0
- package/dist/cases/store.d.ts +74 -0
- package/dist/cases/store.js +816 -0
- package/dist/cases/target-ref.d.ts +25 -0
- package/dist/cases/target-ref.js +22 -0
- package/dist/cases/timeline-range.d.ts +6 -0
- package/dist/cases/timeline-range.js +322 -0
- package/dist/cases/tombstone-sweeper.d.ts +13 -0
- package/dist/cases/tombstone-sweeper.js +128 -0
- package/dist/cases/types.d.ts +244 -0
- package/dist/cases/types.js +47 -0
- package/dist/cases/wiki-page-index.d.ts +50 -0
- package/dist/cases/wiki-page-index.js +350 -0
- package/dist/connectors/event-index.d.ts +45 -0
- package/dist/connectors/event-index.js +381 -0
- package/dist/connectors/raw-query.d.ts +18 -0
- package/dist/connectors/raw-query.js +305 -0
- package/dist/connectors/types.d.ts +109 -0
- package/dist/connectors/types.js +3 -0
- package/dist/db-adapter/node-sqlite-adapter.d.ts +6 -0
- package/dist/db-adapter/node-sqlite-adapter.js +131 -0
- package/dist/db-manager.d.ts +12 -1
- package/dist/db-manager.js +60 -5
- package/dist/decision-formatter.d.ts +2 -0
- package/dist/decision-formatter.js +11 -8
- package/dist/edges/ref-validation.d.ts +8 -0
- package/dist/edges/ref-validation.js +262 -0
- package/dist/edges/store.d.ts +10 -0
- package/dist/edges/store.js +343 -0
- package/dist/edges/types.d.ts +86 -0
- package/dist/edges/types.js +18 -0
- package/dist/embedding-server/mobile/daemon.d.ts +1 -0
- package/dist/embedding-server/mobile/daemon.js +11 -1
- package/dist/embeddings.js +13 -0
- package/dist/entities/audit-metrics.d.ts +73 -0
- package/dist/entities/audit-metrics.js +128 -0
- package/dist/entities/candidate-generator.d.ts +9 -0
- package/dist/entities/candidate-generator.js +436 -0
- package/dist/entities/entity-impact.d.ts +41 -0
- package/dist/entities/entity-impact.js +140 -0
- package/dist/entities/entity-linked-decision-counts.d.ts +2 -0
- package/dist/entities/entity-linked-decision-counts.js +29 -0
- package/dist/entities/entity-list.d.ts +23 -0
- package/dist/entities/entity-list.js +99 -0
- package/dist/entities/entity-orphan-list.d.ts +26 -0
- package/dist/entities/entity-orphan-list.js +91 -0
- package/dist/entities/entity-search.d.ts +21 -0
- package/dist/entities/entity-search.js +150 -0
- package/dist/entities/errors.d.ts +65 -0
- package/dist/entities/errors.js +124 -0
- package/dist/entities/exact-merge-backfill.d.ts +11 -0
- package/dist/entities/exact-merge-backfill.js +161 -0
- package/dist/entities/lineage-backfill.d.ts +12 -0
- package/dist/entities/lineage-backfill.js +101 -0
- package/dist/entities/lineage-store.d.ts +51 -0
- package/dist/entities/lineage-store.js +308 -0
- package/dist/entities/normalization.d.ts +14 -0
- package/dist/entities/normalization.js +63 -0
- package/dist/entities/policy-store.d.ts +17 -0
- package/dist/entities/policy-store.js +263 -0
- package/dist/entities/policy-types.d.ts +72 -0
- package/dist/entities/policy-types.js +59 -0
- package/dist/entities/projection.d.ts +8 -0
- package/dist/entities/projection.js +82 -0
- package/dist/entities/provenance-query.d.ts +33 -0
- package/dist/entities/provenance-query.js +167 -0
- package/dist/entities/read-identity.d.ts +31 -0
- package/dist/entities/read-identity.js +264 -0
- package/dist/entities/recall-bridge.d.ts +5 -0
- package/dist/entities/recall-bridge.js +171 -0
- package/dist/entities/resolution-engine.d.ts +15 -0
- package/dist/entities/resolution-engine.js +69 -0
- package/dist/entities/rollback-preview.d.ts +39 -0
- package/dist/entities/rollback-preview.js +235 -0
- package/dist/entities/source-locator.d.ts +3 -0
- package/dist/entities/source-locator.js +32 -0
- package/dist/entities/store.d.ts +75 -0
- package/dist/entities/store.js +465 -0
- package/dist/entities/types.d.ts +162 -0
- package/dist/entities/types.js +30 -0
- package/dist/index.d.ts +62 -3
- package/dist/index.js +116 -26
- package/dist/mama-api.d.ts +53 -35
- package/dist/mama-api.js +437 -78
- package/dist/memory/api.d.ts +26 -38
- package/dist/memory/api.js +772 -68
- package/dist/memory/event-store.d.ts +3 -0
- package/dist/memory/event-store.js +21 -2
- package/dist/memory/provenance-audit.d.ts +22 -0
- package/dist/memory/provenance-audit.js +112 -0
- package/dist/memory/provenance-query.d.ts +11 -0
- package/dist/memory/provenance-query.js +193 -0
- package/dist/memory/provenance.d.ts +29 -0
- package/dist/memory/provenance.js +136 -0
- package/dist/memory/scope-backfill.d.ts +17 -0
- package/dist/memory/scope-backfill.js +77 -0
- package/dist/memory/types.d.ts +97 -16
- package/dist/model-runs/store.d.ts +13 -0
- package/dist/model-runs/store.js +457 -0
- package/dist/model-runs/tool-trace-store.d.ts +4 -0
- package/dist/model-runs/tool-trace-store.js +114 -0
- package/dist/model-runs/types.d.ts +69 -0
- package/dist/model-runs/types.js +5 -0
- package/dist/runtime/trusted-provenance.d.ts +3 -0
- package/dist/runtime/trusted-provenance.js +6 -0
- package/dist/search/feedback-store.d.ts +59 -0
- package/dist/search/feedback-store.js +211 -0
- package/dist/search/question-type.d.ts +5 -0
- package/dist/search/question-type.js +41 -0
- package/dist/search/ranker-features.d.ts +23 -0
- package/dist/search/ranker-features.js +104 -0
- package/dist/search/ranker-rescore.d.ts +31 -0
- package/dist/search/ranker-rescore.js +226 -0
- package/dist/search/ranker-trainer.d.ts +94 -0
- package/dist/search/ranker-trainer.js +860 -0
- package/dist/search/search-quality.d.ts +36 -0
- package/dist/search/search-quality.js +29 -0
- package/dist/test-utils.d.ts +14 -0
- package/dist/test-utils.js +52 -0
- package/package.json +28 -3
|
@@ -0,0 +1,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,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
|