@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,343 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mapTwinEdgeRow = mapTwinEdgeRow;
|
|
4
|
+
exports.getTwinEdge = getTwinEdge;
|
|
5
|
+
exports.listTwinEdgesForRefs = listTwinEdgesForRefs;
|
|
6
|
+
exports.insertTwinEdge = insertTwinEdge;
|
|
7
|
+
const node_crypto_1 = require("node:crypto");
|
|
8
|
+
const canonicalize_js_1 = require("../canonicalize.js");
|
|
9
|
+
const types_js_1 = require("./types.js");
|
|
10
|
+
const EDGE_TYPE_SET = new Set(types_js_1.TWIN_EDGE_TYPES);
|
|
11
|
+
const REF_KIND_SET = new Set(types_js_1.TWIN_REF_KINDS);
|
|
12
|
+
const SOURCE_SET = new Set(types_js_1.TWIN_EDGE_SOURCES);
|
|
13
|
+
const HUMAN_ACTOR_ROLES = new Set(['commander', 'configurator_elevated']);
|
|
14
|
+
const HUMAN_REASON_CLASSIFICATIONS = new Set([
|
|
15
|
+
'factual_correction',
|
|
16
|
+
'agent_inference_wrong',
|
|
17
|
+
'privacy_redaction',
|
|
18
|
+
'duplicate_merge',
|
|
19
|
+
'state_override',
|
|
20
|
+
'other',
|
|
21
|
+
]);
|
|
22
|
+
function sha256Hex(input) {
|
|
23
|
+
return (0, node_crypto_1.createHash)('sha256').update(input, 'utf8').digest('hex');
|
|
24
|
+
}
|
|
25
|
+
function sha256Buffer(input) {
|
|
26
|
+
return (0, node_crypto_1.createHash)('sha256').update(input, 'utf8').digest();
|
|
27
|
+
}
|
|
28
|
+
function generatedEdgeId() {
|
|
29
|
+
return `edge_${(0, node_crypto_1.randomUUID)().replace(/-/g, '')}`;
|
|
30
|
+
}
|
|
31
|
+
function nullableString(value) {
|
|
32
|
+
return typeof value === 'string' && value.length > 0 ? value : null;
|
|
33
|
+
}
|
|
34
|
+
function requireNonEmptyString(value, field) {
|
|
35
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
36
|
+
throw new Error(`twin_edges.${field} must be a non-empty string`);
|
|
37
|
+
}
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
function normalizeEdgeType(value) {
|
|
41
|
+
if (typeof value === 'string' && EDGE_TYPE_SET.has(value)) {
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
throw new Error(`Unsupported twin edge type: ${String(value)}`);
|
|
45
|
+
}
|
|
46
|
+
function normalizeSource(value) {
|
|
47
|
+
if (typeof value === 'string' && SOURCE_SET.has(value)) {
|
|
48
|
+
return value;
|
|
49
|
+
}
|
|
50
|
+
throw new Error(`Unsupported twin edge source: ${String(value)}`);
|
|
51
|
+
}
|
|
52
|
+
function normalizeRef(ref, field) {
|
|
53
|
+
if (!ref || typeof ref !== 'object') {
|
|
54
|
+
throw new Error(`${field} must be a TwinRef`);
|
|
55
|
+
}
|
|
56
|
+
if (!REF_KIND_SET.has(ref.kind)) {
|
|
57
|
+
throw new Error(`${field}.kind is unsupported: ${String(ref.kind)}`);
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
kind: ref.kind,
|
|
61
|
+
id: requireNonEmptyString(ref.id, `${field}.id`),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function assertSubjectKindAllowed(ref) {
|
|
65
|
+
if (ref.kind === 'raw') {
|
|
66
|
+
throw new Error('twin_edges.subject_ref.kind cannot be raw');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function normalizeConfidence(value) {
|
|
70
|
+
if (value === undefined || value === null) {
|
|
71
|
+
return 1;
|
|
72
|
+
}
|
|
73
|
+
if (typeof value === 'number' && Number.isFinite(value) && value >= 0 && value <= 1) {
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
throw new Error(`twin_edges.confidence must be between 0 and 1: ${String(value)}`);
|
|
77
|
+
}
|
|
78
|
+
function hasDeterministicRule(value) {
|
|
79
|
+
return (value !== null &&
|
|
80
|
+
typeof value === 'object' &&
|
|
81
|
+
!Array.isArray(value) &&
|
|
82
|
+
typeof value.deterministic_rule === 'string' &&
|
|
83
|
+
value.deterministic_rule.length > 0);
|
|
84
|
+
}
|
|
85
|
+
function validateSourceMetadata(input, source) {
|
|
86
|
+
if (source === 'agent') {
|
|
87
|
+
requireNonEmptyString(input.agent_id, 'agent_id');
|
|
88
|
+
requireNonEmptyString(input.model_run_id, 'model_run_id');
|
|
89
|
+
requireNonEmptyString(input.envelope_hash, 'envelope_hash');
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (source === 'human') {
|
|
93
|
+
requireNonEmptyString(input.human_actor_id, 'human_actor_id');
|
|
94
|
+
const role = requireNonEmptyString(input.human_actor_role, 'human_actor_role');
|
|
95
|
+
if (!HUMAN_ACTOR_ROLES.has(role)) {
|
|
96
|
+
throw new Error(`Unsupported twin_edges.human_actor_role: ${role}`);
|
|
97
|
+
}
|
|
98
|
+
if (input.authority_scope_json === undefined || input.authority_scope_json === null) {
|
|
99
|
+
throw new Error('twin_edges.authority_scope_json is required for human edges');
|
|
100
|
+
}
|
|
101
|
+
const classification = requireNonEmptyString(input.reason_classification, 'reason_classification');
|
|
102
|
+
if (!HUMAN_REASON_CLASSIFICATIONS.has(classification)) {
|
|
103
|
+
throw new Error(`Unsupported twin_edges.reason_classification: ${classification}`);
|
|
104
|
+
}
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (!nullableString(input.reason_text) && !hasDeterministicRule(input.relation_attrs)) {
|
|
108
|
+
throw new Error('twin_edges.reason_text or relation_attrs.deterministic_rule is required for code edges');
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function jsonField(value, field) {
|
|
112
|
+
if (value === undefined || value === null) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
if (typeof value === 'string') {
|
|
117
|
+
return (0, canonicalize_js_1.canonicalizeJSON)(JSON.parse(value));
|
|
118
|
+
}
|
|
119
|
+
return (0, canonicalize_js_1.canonicalizeJSON)(value);
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
123
|
+
throw new Error(`Invalid twin_edges.${field}: ${message}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function parseJsonField(value, field, edgeId) {
|
|
127
|
+
if (value === null || value === undefined) {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
const text = String(value);
|
|
131
|
+
if (text.length === 0) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
return JSON.parse(text);
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
139
|
+
throw new Error(`Invalid twin_edges.${field} for ${edgeId}: ${message}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function toBuffer(value) {
|
|
143
|
+
if (Buffer.isBuffer(value)) {
|
|
144
|
+
return value;
|
|
145
|
+
}
|
|
146
|
+
if (value instanceof Uint8Array) {
|
|
147
|
+
return Buffer.from(value);
|
|
148
|
+
}
|
|
149
|
+
throw new Error('twin_edges.content_hash must be a 32-byte Buffer');
|
|
150
|
+
}
|
|
151
|
+
function exists(adapter, table, column, id) {
|
|
152
|
+
const row = adapter
|
|
153
|
+
.prepare(`SELECT 1 AS ok FROM ${table} WHERE ${column} = ? LIMIT 1`)
|
|
154
|
+
.get(id);
|
|
155
|
+
return Boolean(row?.ok);
|
|
156
|
+
}
|
|
157
|
+
function validateRefExists(adapter, ref, field) {
|
|
158
|
+
if (ref.kind === 'report') {
|
|
159
|
+
throw new Error(`${field} ${ref.kind}:${ref.id} is not supported by twin_edges yet`);
|
|
160
|
+
}
|
|
161
|
+
const found = (ref.kind === 'memory' && exists(adapter, 'decisions', 'id', ref.id)) ||
|
|
162
|
+
(ref.kind === 'case' && exists(adapter, 'case_truth', 'case_id', ref.id)) ||
|
|
163
|
+
(ref.kind === 'entity' && exists(adapter, 'entity_nodes', 'id', ref.id)) ||
|
|
164
|
+
(ref.kind === 'edge' && exists(adapter, 'twin_edges', 'edge_id', ref.id)) ||
|
|
165
|
+
(ref.kind === 'raw' && exists(adapter, 'connector_event_index', 'event_index_id', ref.id));
|
|
166
|
+
if (!found) {
|
|
167
|
+
throw new Error(`${field} not found: ${ref.kind}:${ref.id}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function contentHashInput(input) {
|
|
171
|
+
return (0, canonicalize_js_1.canonicalizeJSON)(input);
|
|
172
|
+
}
|
|
173
|
+
function deriveEdgeIdempotencyKey(requestIdempotencyKey, contentHash) {
|
|
174
|
+
return `reqedge_${sha256Hex((0, canonicalize_js_1.canonicalizeJSON)({
|
|
175
|
+
request_idempotency_key: requestIdempotencyKey,
|
|
176
|
+
content_hash: contentHash.toString('hex'),
|
|
177
|
+
})).slice(0, 40)}`;
|
|
178
|
+
}
|
|
179
|
+
function selectByIdempotency(adapter, modelRunId, edgeIdempotencyKey) {
|
|
180
|
+
if (!edgeIdempotencyKey) {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
if (!modelRunId) {
|
|
184
|
+
const row = adapter
|
|
185
|
+
.prepare(`
|
|
186
|
+
SELECT *
|
|
187
|
+
FROM twin_edges
|
|
188
|
+
WHERE model_run_id IS NULL
|
|
189
|
+
AND edge_idempotency_key = ?
|
|
190
|
+
ORDER BY created_at ASC, edge_id ASC
|
|
191
|
+
LIMIT 1
|
|
192
|
+
`)
|
|
193
|
+
.get(edgeIdempotencyKey);
|
|
194
|
+
return row ? mapTwinEdgeRow(row) : null;
|
|
195
|
+
}
|
|
196
|
+
const row = adapter
|
|
197
|
+
.prepare(`
|
|
198
|
+
SELECT *
|
|
199
|
+
FROM twin_edges
|
|
200
|
+
WHERE model_run_id = ?
|
|
201
|
+
AND edge_idempotency_key = ?
|
|
202
|
+
LIMIT 1
|
|
203
|
+
`)
|
|
204
|
+
.get(modelRunId, edgeIdempotencyKey);
|
|
205
|
+
return row ? mapTwinEdgeRow(row) : null;
|
|
206
|
+
}
|
|
207
|
+
function mapTwinEdgeRow(row) {
|
|
208
|
+
const edgeId = String(row.edge_id);
|
|
209
|
+
return {
|
|
210
|
+
edge_id: edgeId,
|
|
211
|
+
edge_type: normalizeEdgeType(row.edge_type),
|
|
212
|
+
subject_ref: {
|
|
213
|
+
kind: String(row.subject_kind),
|
|
214
|
+
id: String(row.subject_id),
|
|
215
|
+
},
|
|
216
|
+
object_ref: {
|
|
217
|
+
kind: String(row.object_kind),
|
|
218
|
+
id: String(row.object_id),
|
|
219
|
+
},
|
|
220
|
+
relation_attrs_json: nullableString(row.relation_attrs_json),
|
|
221
|
+
relation_attrs: parseJsonField(row.relation_attrs_json, 'relation_attrs_json', edgeId),
|
|
222
|
+
confidence: Number(row.confidence),
|
|
223
|
+
source: normalizeSource(row.source),
|
|
224
|
+
agent_id: nullableString(row.agent_id),
|
|
225
|
+
model_run_id: nullableString(row.model_run_id),
|
|
226
|
+
envelope_hash: nullableString(row.envelope_hash),
|
|
227
|
+
human_actor_id: nullableString(row.human_actor_id),
|
|
228
|
+
human_actor_role: nullableString(row.human_actor_role),
|
|
229
|
+
authority_scope_json: nullableString(row.authority_scope_json),
|
|
230
|
+
authority_scope: parseJsonField(row.authority_scope_json, 'authority_scope_json', edgeId),
|
|
231
|
+
reason_classification: nullableString(row.reason_classification),
|
|
232
|
+
reason_text: nullableString(row.reason_text),
|
|
233
|
+
evidence_refs_json: nullableString(row.evidence_refs_json),
|
|
234
|
+
evidence_refs: parseJsonField(row.evidence_refs_json, 'evidence_refs_json', edgeId),
|
|
235
|
+
request_idempotency_key: nullableString(row.request_idempotency_key),
|
|
236
|
+
edge_idempotency_key: nullableString(row.edge_idempotency_key),
|
|
237
|
+
content_hash: toBuffer(row.content_hash),
|
|
238
|
+
created_at: Number(row.created_at),
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
function getTwinEdge(adapter, edgeId) {
|
|
242
|
+
const row = adapter.prepare('SELECT * FROM twin_edges WHERE edge_id = ?').get(edgeId);
|
|
243
|
+
return row ? mapTwinEdgeRow(row) : null;
|
|
244
|
+
}
|
|
245
|
+
function listTwinEdgesForRefs(adapter, refs) {
|
|
246
|
+
const normalizedRefs = refs.map((ref, index) => normalizeRef(ref, `refs[${index}]`));
|
|
247
|
+
if (normalizedRefs.length === 0) {
|
|
248
|
+
return [];
|
|
249
|
+
}
|
|
250
|
+
const clauses = [];
|
|
251
|
+
const params = [];
|
|
252
|
+
for (const ref of normalizedRefs) {
|
|
253
|
+
clauses.push('(subject_kind = ? AND subject_id = ?)');
|
|
254
|
+
params.push(ref.kind, ref.id);
|
|
255
|
+
clauses.push('(object_kind = ? AND object_id = ?)');
|
|
256
|
+
params.push(ref.kind, ref.id);
|
|
257
|
+
}
|
|
258
|
+
const rows = adapter
|
|
259
|
+
.prepare(`
|
|
260
|
+
SELECT *
|
|
261
|
+
FROM twin_edges
|
|
262
|
+
WHERE ${clauses.join(' OR ')}
|
|
263
|
+
ORDER BY created_at ASC, edge_id ASC
|
|
264
|
+
`)
|
|
265
|
+
.all(...params);
|
|
266
|
+
return rows.map(mapTwinEdgeRow);
|
|
267
|
+
}
|
|
268
|
+
function insertTwinEdge(adapter, input) {
|
|
269
|
+
const edgeType = normalizeEdgeType(input.edge_type);
|
|
270
|
+
const subjectRef = normalizeRef(input.subject_ref, 'subject_ref');
|
|
271
|
+
const objectRef = normalizeRef(input.object_ref, 'object_ref');
|
|
272
|
+
assertSubjectKindAllowed(subjectRef);
|
|
273
|
+
const source = normalizeSource(input.source);
|
|
274
|
+
const confidence = normalizeConfidence(input.confidence);
|
|
275
|
+
validateSourceMetadata(input, source);
|
|
276
|
+
validateRefExists(adapter, subjectRef, 'subject_ref');
|
|
277
|
+
validateRefExists(adapter, objectRef, 'object_ref');
|
|
278
|
+
const relationAttrsJson = jsonField(input.relation_attrs, 'relation_attrs_json');
|
|
279
|
+
const evidenceRefsJson = jsonField(input.evidence_refs, 'evidence_refs_json');
|
|
280
|
+
const authorityScopeJson = jsonField(input.authority_scope_json, 'authority_scope_json');
|
|
281
|
+
const agentId = nullableString(input.agent_id);
|
|
282
|
+
const reasonText = nullableString(input.reason_text);
|
|
283
|
+
const envelopeHash = nullableString(input.envelope_hash);
|
|
284
|
+
const humanActorId = nullableString(input.human_actor_id);
|
|
285
|
+
const humanActorRole = nullableString(input.human_actor_role);
|
|
286
|
+
const reasonClassification = nullableString(input.reason_classification);
|
|
287
|
+
const requestIdempotencyKey = nullableString(input.request_idempotency_key);
|
|
288
|
+
const modelRunId = nullableString(input.model_run_id);
|
|
289
|
+
const contentHash = sha256Buffer(contentHashInput({
|
|
290
|
+
edge_type: edgeType,
|
|
291
|
+
subject_ref: subjectRef,
|
|
292
|
+
object_ref: objectRef,
|
|
293
|
+
relation_attrs_json: relationAttrsJson,
|
|
294
|
+
confidence,
|
|
295
|
+
source,
|
|
296
|
+
agent_id: agentId,
|
|
297
|
+
model_run_id: modelRunId,
|
|
298
|
+
envelope_hash: envelopeHash,
|
|
299
|
+
human_actor_id: humanActorId,
|
|
300
|
+
human_actor_role: humanActorRole,
|
|
301
|
+
authority_scope_json: authorityScopeJson,
|
|
302
|
+
reason_classification: reasonClassification,
|
|
303
|
+
reason_text: reasonText,
|
|
304
|
+
evidence_refs_json: evidenceRefsJson,
|
|
305
|
+
}));
|
|
306
|
+
const edgeIdempotencyKey = nullableString(input.edge_idempotency_key) ??
|
|
307
|
+
(requestIdempotencyKey ? deriveEdgeIdempotencyKey(requestIdempotencyKey, contentHash) : null);
|
|
308
|
+
const existing = selectByIdempotency(adapter, modelRunId, edgeIdempotencyKey);
|
|
309
|
+
if (existing) {
|
|
310
|
+
return existing;
|
|
311
|
+
}
|
|
312
|
+
const edgeId = nullableString(input.edge_id) ?? generatedEdgeId();
|
|
313
|
+
const createdAt = Date.now();
|
|
314
|
+
try {
|
|
315
|
+
return adapter.transaction(() => {
|
|
316
|
+
adapter
|
|
317
|
+
.prepare(`
|
|
318
|
+
INSERT INTO twin_edges (
|
|
319
|
+
edge_id, edge_type, subject_kind, subject_id, object_kind, object_id,
|
|
320
|
+
relation_attrs_json, confidence, source, agent_id, model_run_id, envelope_hash,
|
|
321
|
+
human_actor_id, human_actor_role, authority_scope_json, reason_classification,
|
|
322
|
+
reason_text, evidence_refs_json, request_idempotency_key, edge_idempotency_key,
|
|
323
|
+
content_hash, created_at
|
|
324
|
+
)
|
|
325
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
326
|
+
`)
|
|
327
|
+
.run(edgeId, edgeType, subjectRef.kind, subjectRef.id, objectRef.kind, objectRef.id, relationAttrsJson, confidence, source, agentId, modelRunId, envelopeHash, humanActorId, humanActorRole, authorityScopeJson, reasonClassification, reasonText, evidenceRefsJson, requestIdempotencyKey, edgeIdempotencyKey, contentHash, createdAt);
|
|
328
|
+
const inserted = getTwinEdge(adapter, edgeId);
|
|
329
|
+
if (!inserted) {
|
|
330
|
+
throw new Error(`Failed to load inserted twin edge: ${edgeId}`);
|
|
331
|
+
}
|
|
332
|
+
return inserted;
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
catch (error) {
|
|
336
|
+
const duplicate = selectByIdempotency(adapter, modelRunId, edgeIdempotencyKey);
|
|
337
|
+
if (duplicate) {
|
|
338
|
+
return duplicate;
|
|
339
|
+
}
|
|
340
|
+
throw error;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { Buffer } from 'node:buffer';
|
|
2
|
+
export declare const TWIN_REF_KINDS: readonly ["memory", "case", "entity", "report", "edge", "raw"];
|
|
3
|
+
export type TwinRefKind = (typeof TWIN_REF_KINDS)[number];
|
|
4
|
+
export declare const TWIN_EDGE_SOURCES: readonly ["agent", "human", "code"];
|
|
5
|
+
export type TwinEdgeSource = (typeof TWIN_EDGE_SOURCES)[number];
|
|
6
|
+
export declare const TWIN_EDGE_TYPES: readonly ["supersedes", "builds_on", "debates", "synthesizes", "mentions", "derived_from", "case_member", "alias_of", "next_action_for", "blocks"];
|
|
7
|
+
export type TwinEdgeType = (typeof TWIN_EDGE_TYPES)[number];
|
|
8
|
+
export type TwinRef = {
|
|
9
|
+
[Kind in TwinRefKind]: {
|
|
10
|
+
kind: Kind;
|
|
11
|
+
id: string;
|
|
12
|
+
};
|
|
13
|
+
}[TwinRefKind];
|
|
14
|
+
export type TwinEdgeSubjectRef = Exclude<TwinRef, {
|
|
15
|
+
kind: 'raw';
|
|
16
|
+
}>;
|
|
17
|
+
export interface TwinScopeRef {
|
|
18
|
+
kind: 'global' | 'user' | 'channel' | 'project';
|
|
19
|
+
id: string;
|
|
20
|
+
}
|
|
21
|
+
export interface TwinProjectRef {
|
|
22
|
+
kind: string;
|
|
23
|
+
id: string;
|
|
24
|
+
}
|
|
25
|
+
export interface TwinVisibility {
|
|
26
|
+
scopes?: TwinScopeRef[];
|
|
27
|
+
connectors?: string[];
|
|
28
|
+
projectRefs?: TwinProjectRef[];
|
|
29
|
+
tenantId?: string | null;
|
|
30
|
+
asOfMs?: number | null;
|
|
31
|
+
}
|
|
32
|
+
export interface InsertTwinEdgeInput {
|
|
33
|
+
edge_id?: string;
|
|
34
|
+
edge_type: TwinEdgeType;
|
|
35
|
+
subject_ref: TwinEdgeSubjectRef;
|
|
36
|
+
object_ref: TwinRef;
|
|
37
|
+
relation_attrs?: unknown;
|
|
38
|
+
confidence?: number;
|
|
39
|
+
source: TwinEdgeSource;
|
|
40
|
+
agent_id?: string;
|
|
41
|
+
model_run_id?: string;
|
|
42
|
+
envelope_hash?: string;
|
|
43
|
+
request_idempotency_key?: string;
|
|
44
|
+
edge_idempotency_key?: string;
|
|
45
|
+
human_actor_id?: string;
|
|
46
|
+
human_actor_role?: 'commander' | 'configurator_elevated';
|
|
47
|
+
authority_scope_json?: unknown;
|
|
48
|
+
reason_classification?: 'factual_correction' | 'agent_inference_wrong' | 'privacy_redaction' | 'duplicate_merge' | 'state_override' | 'other';
|
|
49
|
+
reason_text?: string;
|
|
50
|
+
evidence_refs?: unknown;
|
|
51
|
+
}
|
|
52
|
+
export interface TwinEdgeRecord {
|
|
53
|
+
edge_id: string;
|
|
54
|
+
edge_type: TwinEdgeType;
|
|
55
|
+
subject_ref: TwinRef;
|
|
56
|
+
object_ref: TwinRef;
|
|
57
|
+
relation_attrs_json: string | null;
|
|
58
|
+
relation_attrs: unknown | null;
|
|
59
|
+
confidence: number;
|
|
60
|
+
source: TwinEdgeSource;
|
|
61
|
+
agent_id: string | null;
|
|
62
|
+
model_run_id: string | null;
|
|
63
|
+
envelope_hash: string | null;
|
|
64
|
+
human_actor_id: string | null;
|
|
65
|
+
human_actor_role: string | null;
|
|
66
|
+
authority_scope_json: string | null;
|
|
67
|
+
authority_scope: unknown | null;
|
|
68
|
+
reason_classification: string | null;
|
|
69
|
+
reason_text: string | null;
|
|
70
|
+
evidence_refs_json: string | null;
|
|
71
|
+
evidence_refs: unknown | null;
|
|
72
|
+
request_idempotency_key: string | null;
|
|
73
|
+
edge_idempotency_key: string | null;
|
|
74
|
+
content_hash: Buffer;
|
|
75
|
+
created_at: number;
|
|
76
|
+
}
|
|
77
|
+
export interface ListVisibleTwinEdgesOptions {
|
|
78
|
+
scopes?: TwinScopeRef[];
|
|
79
|
+
connectors?: string[];
|
|
80
|
+
projectRefs?: TwinProjectRef[];
|
|
81
|
+
tenantId?: string | null;
|
|
82
|
+
edgeTypes?: TwinEdgeType[];
|
|
83
|
+
asOfMs?: number | null;
|
|
84
|
+
limit?: number;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TWIN_EDGE_TYPES = exports.TWIN_EDGE_SOURCES = exports.TWIN_REF_KINDS = void 0;
|
|
4
|
+
exports.TWIN_REF_KINDS = ['memory', 'case', 'entity', 'report', 'edge', 'raw'];
|
|
5
|
+
exports.TWIN_EDGE_SOURCES = ['agent', 'human', 'code'];
|
|
6
|
+
exports.TWIN_EDGE_TYPES = [
|
|
7
|
+
'supersedes',
|
|
8
|
+
'builds_on',
|
|
9
|
+
'debates',
|
|
10
|
+
'synthesizes',
|
|
11
|
+
'mentions',
|
|
12
|
+
'derived_from',
|
|
13
|
+
'case_member',
|
|
14
|
+
'alias_of',
|
|
15
|
+
'next_action_for',
|
|
16
|
+
'blocks',
|
|
17
|
+
];
|
|
18
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
18
|
exports.ClaudeDaemon = exports.ANSI_REGEX = void 0;
|
|
19
19
|
const events_1 = require("events");
|
|
20
|
+
const crypto_1 = require("crypto");
|
|
20
21
|
const child_process_1 = require("child_process");
|
|
21
22
|
/**
|
|
22
23
|
* ANSI escape code regex pattern
|
|
@@ -38,6 +39,7 @@ class ClaudeDaemon extends events_1.EventEmitter {
|
|
|
38
39
|
_pid;
|
|
39
40
|
_isRunning;
|
|
40
41
|
buffer;
|
|
42
|
+
cliSessionId;
|
|
41
43
|
/**
|
|
42
44
|
* Create a new ClaudeDaemon instance
|
|
43
45
|
* @param projectDir - Working directory for Claude Code
|
|
@@ -51,6 +53,7 @@ class ClaudeDaemon extends events_1.EventEmitter {
|
|
|
51
53
|
this._pid = null;
|
|
52
54
|
this._isRunning = false;
|
|
53
55
|
this.buffer = '';
|
|
56
|
+
this.cliSessionId = (0, crypto_1.randomUUID)();
|
|
54
57
|
}
|
|
55
58
|
/**
|
|
56
59
|
* Check if the daemon is currently running
|
|
@@ -76,13 +79,20 @@ class ClaudeDaemon extends events_1.EventEmitter {
|
|
|
76
79
|
return new Promise((resolve, reject) => {
|
|
77
80
|
let settled = false;
|
|
78
81
|
try {
|
|
79
|
-
//
|
|
82
|
+
// Requires a modern Claude CLI build with stream-json support
|
|
83
|
+
// for --print/--verbose/--session-id/--setting-sources.
|
|
80
84
|
const args = [
|
|
85
|
+
'--print',
|
|
86
|
+
'--verbose',
|
|
81
87
|
'--dangerously-skip-permissions',
|
|
82
88
|
'--input-format',
|
|
83
89
|
'stream-json',
|
|
84
90
|
'--output-format',
|
|
85
91
|
'stream-json',
|
|
92
|
+
'--session-id',
|
|
93
|
+
this.cliSessionId,
|
|
94
|
+
'--setting-sources',
|
|
95
|
+
'project,local',
|
|
86
96
|
];
|
|
87
97
|
// Spawn Claude Code CLI
|
|
88
98
|
this.process = (0, child_process_1.spawn)('claude', args, {
|
package/dist/embeddings.js
CHANGED
|
@@ -32,10 +32,20 @@ Object.defineProperty(exports, "getEmbeddingDim", { enumerable: true, get: funct
|
|
|
32
32
|
Object.defineProperty(exports, "getQuantized", { enumerable: true, get: function () { return config_loader_js_1.getQuantized; } });
|
|
33
33
|
// Shared cache directory (not in node_modules)
|
|
34
34
|
const DEFAULT_CACHE_DIR = path_1.default.join(os_1.default.homedir(), '.cache', 'huggingface', 'transformers');
|
|
35
|
+
const TIER3_ENV_VALUES = new Set(['1', 'true', 'yes']);
|
|
35
36
|
// Singleton pattern for model loading
|
|
36
37
|
let embeddingPipeline = null;
|
|
37
38
|
let currentModelName = null;
|
|
38
39
|
let modelLoadFailed = false; // Cache load failures to avoid repeated slow retries
|
|
40
|
+
function isForceTier3Enabled() {
|
|
41
|
+
return TIER3_ENV_VALUES.has(String(process.env.MAMA_FORCE_TIER_3 || '').toLowerCase());
|
|
42
|
+
}
|
|
43
|
+
function assertEmbeddingsEnabled() {
|
|
44
|
+
if (isForceTier3Enabled()) {
|
|
45
|
+
throw new Error('Embedding generation disabled because MAMA_FORCE_TIER_3=true. ' +
|
|
46
|
+
'Tier 3 test mode must use lexical/no-vector fallback instead of loading the embedding model.');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
39
49
|
/**
|
|
40
50
|
* Load embedding model (configurable)
|
|
41
51
|
*
|
|
@@ -45,6 +55,7 @@ let modelLoadFailed = false; // Cache load failures to avoid repeated slow retri
|
|
|
45
55
|
* @returns Embedding pipeline
|
|
46
56
|
*/
|
|
47
57
|
async function loadModel() {
|
|
58
|
+
assertEmbeddingsEnabled();
|
|
48
59
|
const modelName = (0, config_loader_js_1.getModelName)();
|
|
49
60
|
// Check if model has changed (Story M1.4 AC #3)
|
|
50
61
|
if (embeddingPipeline && currentModelName && currentModelName !== modelName) {
|
|
@@ -104,6 +115,7 @@ async function generateEmbedding(text) {
|
|
|
104
115
|
if (!text || text.trim().length === 0) {
|
|
105
116
|
throw new Error('Text cannot be empty');
|
|
106
117
|
}
|
|
118
|
+
assertEmbeddingsEnabled();
|
|
107
119
|
// Task 2: Check cache first (AC #3)
|
|
108
120
|
const cached = embedding_cache_js_1.embeddingCache.get(text);
|
|
109
121
|
if (cached) {
|
|
@@ -189,6 +201,7 @@ async function generateEnhancedEmbedding(decision) {
|
|
|
189
201
|
* @returns Array of embeddings
|
|
190
202
|
*/
|
|
191
203
|
async function generateBatchEmbeddings(texts) {
|
|
204
|
+
assertEmbeddingsEnabled();
|
|
192
205
|
if (!Array.isArray(texts) || texts.length === 0) {
|
|
193
206
|
throw new Error('Texts must be a non-empty array');
|
|
194
207
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entity audit metric helpers.
|
|
3
|
+
*
|
|
4
|
+
* Pure functions — no DB, no embeddings. The audit queue assembles the
|
|
5
|
+
* snapshot from live tables and fixtures, then hands it to these helpers
|
|
6
|
+
* for deterministic metric calculation + baseline classification.
|
|
7
|
+
*/
|
|
8
|
+
export declare const FALSE_MERGE_HARD_CEILING = 0.02;
|
|
9
|
+
export type EntityAuditClassification = 'improved' | 'stable' | 'regressed' | 'inconclusive';
|
|
10
|
+
export interface AuditCandidateSnapshot {
|
|
11
|
+
id: string;
|
|
12
|
+
status: 'pending' | 'auto_merged' | 'approved' | 'rejected' | 'deferred';
|
|
13
|
+
score_total: number;
|
|
14
|
+
left_canonical_id: string | null;
|
|
15
|
+
right_canonical_id: string | null;
|
|
16
|
+
left_kind: string | null;
|
|
17
|
+
right_kind: string | null;
|
|
18
|
+
}
|
|
19
|
+
export interface AuditGoldPair {
|
|
20
|
+
canonical_id: string;
|
|
21
|
+
left_obs_id: string;
|
|
22
|
+
right_obs_id: string;
|
|
23
|
+
}
|
|
24
|
+
export interface AuditCandidateGoldLink {
|
|
25
|
+
candidate_id: string;
|
|
26
|
+
matched_pair_key: string;
|
|
27
|
+
score_total: number;
|
|
28
|
+
}
|
|
29
|
+
export interface AuditSnapshot {
|
|
30
|
+
candidates: AuditCandidateSnapshot[];
|
|
31
|
+
gold_pairs: AuditGoldPair[];
|
|
32
|
+
candidate_gold_matches: AuditCandidateGoldLink[];
|
|
33
|
+
gold_canonical_projection_counts: Record<string, number>;
|
|
34
|
+
}
|
|
35
|
+
export interface AuditMetricSummary {
|
|
36
|
+
false_merge_rate: number;
|
|
37
|
+
false_merge_numerator: number;
|
|
38
|
+
false_merge_denominator: number;
|
|
39
|
+
cross_language_candidate_recall_at_10: number;
|
|
40
|
+
cross_language_recall_numerator: number;
|
|
41
|
+
cross_language_recall_denominator: number;
|
|
42
|
+
ontology_violation_count: number;
|
|
43
|
+
projection_fragmentation_rate: number;
|
|
44
|
+
}
|
|
45
|
+
export interface AuditRunClassificationInput {
|
|
46
|
+
current: AuditMetricSummary;
|
|
47
|
+
baseline: AuditMetricSummary | null;
|
|
48
|
+
}
|
|
49
|
+
export declare function computeFalseMergeRate(candidates: AuditCandidateSnapshot[]): {
|
|
50
|
+
rate: number;
|
|
51
|
+
numerator: number;
|
|
52
|
+
denominator: number;
|
|
53
|
+
};
|
|
54
|
+
export declare function computeCrossLanguageRecallAtK(goldPairs: AuditGoldPair[], matches: AuditCandidateGoldLink[], k: number): {
|
|
55
|
+
recall: number;
|
|
56
|
+
numerator: number;
|
|
57
|
+
denominator: number;
|
|
58
|
+
};
|
|
59
|
+
export declare function computeCrossLanguageRecallAt5(goldPairs: AuditGoldPair[], matches: AuditCandidateGoldLink[]): {
|
|
60
|
+
recall: number;
|
|
61
|
+
numerator: number;
|
|
62
|
+
denominator: number;
|
|
63
|
+
};
|
|
64
|
+
export declare function computeCrossLanguageRecallAt10(goldPairs: AuditGoldPair[], matches: AuditCandidateGoldLink[]): {
|
|
65
|
+
recall: number;
|
|
66
|
+
numerator: number;
|
|
67
|
+
denominator: number;
|
|
68
|
+
};
|
|
69
|
+
export declare function computeOntologyViolationCount(candidates: AuditCandidateSnapshot[]): number;
|
|
70
|
+
export declare function computeProjectionFragmentationRate(counts: Record<string, number>): number;
|
|
71
|
+
export declare function computeAuditMetrics(snapshot: AuditSnapshot): AuditMetricSummary;
|
|
72
|
+
export declare function classifyAuditRun(input: AuditRunClassificationInput): EntityAuditClassification;
|
|
73
|
+
//# sourceMappingURL=audit-metrics.d.ts.map
|